Está en la página 1de 43

Capítulo 3 68-114 enfocar los conceptos a un lenguaje de alto nivel

Este capítulo intenta dar un tratamiento unificado del tema, con profundidad suficiente para ser
útil personas que realmente diseñan un lenguaje de programación.

DESCRIPCIÓN GENERAL DEL PROBLEMA

Aunque el primer primitivo los lenguajes de programación surgieron hace más de 25 años, hasta
hace poco tiempo hubo poco orden en el proceso de diseño de nuevos lenguajes.

Los primeros idiomas fueron pioneros, explorando un nuevo campo. No lo es sorprendente que
estaban mal diseñados.

Después del desarrollo inicial de lenguajes de alto nivel y la implementación de los primeros
compiladores, se produjo un período bastante largo en el que se hicieron intentos para diseñar
nuevos lenguajes sin los defectos de los antiguos. La mayoría de estos intentos fueron fracasos, no
tanto por la falta de ideas sobre cómo diseñar mejores idiomas a partir de un exceso de ideas. Un
buen ejemplo de este proceso es el noción de que "si pudiera significar algo, debería" (Radin y
Rogoway, 1965), que llevó a PL / I.

Más recientemente, la experiencia de errores anteriores ha llevado a un conocimiento real acerca


de cómo construir buenos lenguajes de programación. Ideas y principios básicos están siendo
suficientemente bien establecidos como para que sea práctico declarar explícitamente pautas para
el diseño del lenguaje. Aquellas áreas que aún no se comprenden están siendo investigado
sistemáticamente

Una proposición básica de todo este capítulo es que un buen lenguaje no es solo un azar colección
de características, pero un todo unificado.

3-2 CONSIDERACIONES PRELIMINARES

En el diseño de un nuevo lenguaje, ciertos asuntos requieren pensamiento, antes de cualquier se


tiene en cuenta los detalles del diseño. La atención adecuada a estos por adelantado puede evitar
problemas más adelante.

La primera y más importante pregunta que debe hacerse es:

¿Es necesario diseñar un nuevo idioma?

Casi. Cualquier enfoque alternativo será más simple y más rápido que intentar el tarea difícil y
lenta de diseñar un lenguaje completamente nuevo.

¿Hay un idioma existente que pueda usarse para llenar el requisito? Incluso si requiere una nueva
implementación, implementar un lenguaje existente es más fácil y más rápido que diseñar y luego
implementar un nuevo idioma.

¿Se puede extender un idioma existente? Es más fácil diseñar una extensión limpia a un idioma
existente, "incluso si la extensión \ implica un compilador nuevo, que diseñar un idioma
completamente nuevo. Si se toma este enfoque, sin embargo, se debe tener cuidado tomado para
no hacer la extensión tan grande y compleja que se convierta, en efecto, nuevo idioma. En tales
casos, la necesidad de mantener alguna interfaz con el viejo el lenguaje probablemente
comprometerá seriamente el diseño de la extensión. También si uno está extendiendo un idioma
existente, es necesario seleccionar el idioma base cuidadosamente para que el trabajo de
extensión se minimice y la extensión se ajuste con gracia en el lenguaje. El objetivo debe ser
producir un lenguaje que es algo más grande pero igualmente bien construido.

¿Sería posible modificar un idioma existente, posiblemente usando un macroprocesador o algo


similar? Incluso una instalación para macros sin parámetros (simplemente sustituyendo un texto
específico por cada aparición de un identificador definido) puede producir alteraciones
importantes en la sintaxis de un idioma, si se usa hábilmente (p. ej., RATFOR según lo definido por
Kernighan y Plauger. 1976). Sin embargo, el poder de este enfoque para tareas más complejas,
como la introducción de nuevas estructuras de datos, es limitado. Se debe considerar seriamente
estas técnicas como alternativas a un nuevo lenguaje, simplemente sobre la base de minimizar el
trabajo y el tiempo involucrados.

Quizás no haya otro problema relacionado con la computadora que se vea tan tentadoramente
fácil y es tan terriblemente difícil como hacer un buen trabajo de diseño del lenguaje. Prescindir de
la idea de que es posible impulsar un diseño durante el fin de semana y comenzar implementar un
traductor para ello el lunes. Un mes después, todavía habrá puntos menores de diseño del
lenguaje para resolver, y la implementación tendrá conseguido casi en ninguna parte.

Suponiendo que se haya tomado la decisión de que ninguno de los precedentes los enfoques
serán suficientes, el siguiente punto de interés es:

¿Cuál es el propósito del lenguaje?

Un lenguaje a menudo está diseñado específicamente para un área de aplicación. Cuanto más
atención que se le da a la restricción de la aplicación de área del idioma, la mejor será el idioma
para problemas en el área. De hecho, no es recomendable intentar diseñar un lenguaje de
propósito general adecuado para cualquier p! ~ blem. AIVsuch los intentos hasta la fecha han sido
desalentadores (especialmente PL / I y ALGOL 68).

En la actualidad, toda evidencia indica que nadie sabe cómo hacer un trabajo adecuado de
diseñando un lenguaje que será "bueno para todo".

Finalmente, la relación del nuevo idioma con los idiomas existentes debe ser considerada.
Weinberg (1971) discute el fenómeno psicológico de "i ~ hibition" que ocurre cuando un idioma
antiguo y un nuevo idioma son similares pero no idéntico. El usuario está sujeto a una seria
confusión debido a la incertidumbre sobre cómo gran parte del lenguaje antiguo se traslada al
nuevo. Por ejemplo. FORTRAN

Los programadores que aprenden E / S con formato PL / I tienen grandes problemas con los tipos E
y F formatos, que son similares pero no idénticos a FORTRAN equivalente construcciones

En resumen, es mejor hacer que el nuevo idioma sea claramente diferente de en lugar de muy
similar a cualquier idioma existente. Si los idiomas nuevos y antiguos son tan similares, tal vez la
necesidad del nuevo idioma no ha sido adecuadamente examinada.
3-3 FUENTES DE IDEAS

La inspiración para el diseño del lenguaje de programación ha surgido de varias fuentes en el


pasado. Varias de estas fuentes importantes serán discutidas en esta sección: idiomas naturales,
matemáticas, lenguajes de programación existentes, y uso y experimentos.

Derivamos gran parte de nuestra práctica actual en lenguajes de programación de idiomas


naturales. Obviamente, muchos de nuestros constructos están redactados de una manera que se
basa en el lenguaje natural. Menos obviamente, también derivamos más sutil convenciones como
la noción del espacio en blanco significativo, la idea relacionada de que (excepto en cadenas)
cualquier número de espacios en blanco es equivalente a un espacio en blanco, y la concepto de
que el final de una línea generalmente no es más significativo que un espacio en blanco.

En términos generales, los constructos derivados del lenguaje natural son valiosos para su
obviedad y legibilidad. Lo hacen (al menos aproximadamente) lo que dicen. Esta es una gran
ventaja incluso para los programadores experimentados y puede ser crucial para idiomas
destinados a principiantes y no programadores. Por otro lado, el problema de inhibición con
respecto a otros lenguajes de programación puede operar aquí. Los idiomas que se parecen
demasiado al inglés pueden producir tasas de error más altas ya que los usuarios intentan otras
construcciones en inglés solo para descubrir que no lo hacen trabajar como se espera Usar el
lenguaje natural como modelo también puede resultar en un exceso palabrería. COBOL es un
ejemplo obvio. Tenga en cuenta también que los lenguajes naturales a menudo contienen mucha
amiguidad, lo cual es indeseable en un lenguaje de programación.

En términos generales, una buena estrategia es utilizar el lenguaje natural como guía cuando
diseñando la sLuego pasamos a las matemáticas como fuente de ideas. Matemáticas ha sido un
fuente importante de convenciones y dispositivos utilizados en lenguajes de programación (más
obviamente, la expresión aritmética). Es completamente posible que nuevo los lenguajes de
programación incorporarán otros préstamos de las matemáticas.

Sin embargo, es importante recordar que la programación no es matemática.

En general, los programadores y los matemáticos usan diferentes métodos y resuelven diferentes
problemas al trabajar hacia diferentes objetivos. Se debería notar en particular que, para lograr la
brevedad que es tan importante para manipulación compleja de fórmulas, los matemáticos a
menudo muestran una completa indiferencia por claridad y obviedad. Esto se ejemplifica en la
increíble proliferación de juegos de caracteres en notación matemática. Mientras que las
matemáticas son una fuente útil de ideas, particularmente con respecto a los tipos de datos, el
diseñador del lenguaje debe ser muy cuidado con la adopción de la notación matemática para un
concepto dado. Considerar, por ejemplo, el lenguaje APL, que está fuertemente orientado
matemáticamente. Un verdadero problema de implementación que surge es la representación de
algunos de los APL símbolos en dispositivos de E / S tradicionales. sintaxis detallada de nuevos
constructos, mientras se evita su influencia en otra parte Los lenguajes de programación
existentes pueden ser la mejor fuente de ideas para diseñadores de lenguaje de programación. Los
diseñadores deben tener mucho cuidado al incluir tales ideas en su propio producto, sin embargo,
porque los diseñadores en el p cometió serios errores en el diseño.
Se pueden enunciar algunos principios básicos para distinguir buenas ideas dignas de la
perpetuación de malas ideas dignas de extinción. Quizás el mayor principio es preguntar "¿Por qué
se hizo de esa manera?" Una vez que obtenga una respuesta a esta pregunta, pregunte "¿Es esa
razón (todavía) válida?" A menudo la respuesta a esta pregunta será no. Por ejemplo, las extrañas
restricciones de FORTRAN sobre los subíndices de matriz se remontan a su primera
implementación: los implementadores deseaban hacer eficiente uso de las características de
direccionamiento de su hardware y tenían miedo de que esto podría no se debe hacer si se
permitió alguna expresión como subíndice. Aunque esto puede quizás se considere razonable (o al
menos comprensible) en las circunstancias, ciertamente no es defendible hoy, considerando la
seria reducción en usabilidad que resulta.

También vale la pena recordar que incluso si el veredicto general sobre un el lenguaje es "un mal
diseño", esto no significa que no oculta características que valen la pena en algún lugar en el
fondo. Por ejemplo, aunque APL puede ser criticados en muchos frentes, sus poderosos
operadores de manipulación de matrices pueden ser vale la pena copiar

Del mismo modo, el hecho de que una característica esté comúnmente disponible no puede
implicar que es una buena idea. Muchos idiomas han seguido el liderazgo de ALGOL 60 al permitir
tamaño de matrices que se decidirá en el tiempo de ejecución, una característica que introduce
considerables problemas de implementación e interfiere con la verificación de errores en tiempo
de compilación. Esta la característica puede tener un valor limitado en ciertas áreas de
aplicaciones. El fenómeno de las declaraciones predeterminadas, heredadas de FORTRAN, es otro
ejemplo de mal diseño del lenguaje. Esta característica en particular ilustra el hecho de que
algunos las características actualmente populares son, de hecho, muy perjudiciales para la calidad
del programa.

Otra consideración es que una característica mal diseñada puede ser el resultado de tratando de
cumplir dos requisitos al mismo tiempo, con el resultado de que uno de ellos (quizás el más
conspicuo) está mal servido. Un ejemplo de esto es el parámetros de paso-como-si-por-macro-
sustitución de ALGOL 60 (también conocido como "passby-name"), que parecen haber resultado
de la confusión de las dos nociones "procedimiento", un concepto lógico y "macro", una técnica de
implementación para procedimiento.

Algunas características deseables se pueden identificar al observar la forma en que los


programadores usa las instalaciones de los idiomas existentes. En particular, es posible derivar
restricciones que pueden mejorar la verificación de errores o la legibilidad mientras no restringir al
programador, observando qué partes de un idioma se usan raramente.

Por ejemplo, parte de la motivación para las fuerzas anti-GOTO proviene de la observación de que
los buenos programadores no usan todo el poder del GOTO-ellos use el GOTO para simular
estructuras menos generales pero más comprensibles.

Del mismo modo, al observar los patrones de uso establecidos, uno puede determinar qué las
características son deseables. Por ejemplo, mediciones de programas reales por

Alexander (1972) estableció que dos tercios de todas las ifs no tienen un ELSE cláusula; por lo que
probablemente sería irrazonable requerir el ELSE en todas las FI. Los noción relativamente
reciente de experimentar con cambios de diseño (Gannon, 1975) bajo condiciones controladas
ofrece básicamente los mismos tipos de conclusiones. Indudablemente la investigación continuará
en las áreas de medición del lenguaje de programación uso y experimentación en el diseño de
lenguaje de programación.

3-4 METAS Y FILOSOFÍAS DEL DISEÑO DE LENGUAJES DE PROGRAMACIÓN

Cuando se diseña un lenguaje de programación, se debe prestar especial atención a los objetivos
del lenguaje. Una serie de objetivos importantes, como la comunicación humana, la prevención y
detección de errores, usabilidad, efectividad del programa, compatibilidad, eficiencia,
independent machine, and simplicidad, se describen en giro.

Esta sección también se ocupa de las filosofías de diseño. Filosofías como uniformidad,
ortogonalidad, generalidad, modularidad del lenguaje, minimización y nivel de la abstracción se
discuten.

3-4.1 Comunicación humana

Si bien es importante comunicarse de manera eficiente con la computadora, detectar errores


bueno, y así sucesivamente, el objetivo más básico de un lenguaje de programación es la
comunicación entre los seres humanos. Si un programa no puede ser entendido por los humanos,
es difícil de verificar y no puede mantenerse o modificarse. Incluso si el programa es aún claro
para su autor, esta es una condición estrictamente temporal. Se ha sugerido por Kernighan y PI
auger (1976) que la legibilidad es el mejor índice individual de buena programación. Sin duda, uno
de los factores cruciales para lograr programas legibles es un lenguaje de programación
comprensible.

Es importante 'darse cuenta de que los problemas de la comunicación humana no pueden dejarse
enteramente a comentarios o documentación externa. A los programadores no les gusta
escribiendo comentarios excesivos y tienden a evitarlos. La documentación externa es muy a
menudo desactualizado e incompleto. Además, esto a veces puede aplicarse a documentación
interna. Una buena y muy confiable forma de documentación es la programa en sí mismo, si es
legible. Los lenguajes de programación deben diseñarse con atención constante a la claridad y
comprensibilidad.

Es vital distinguir entre legibilidad y escritura. Es importante ser capaz de escribir programas
fácilmente. Es necesario poder leer programas fácilmente. La legibilidad de los programas es
mucho más importante a largo plazo que su capacidad de escritura (Hoare, 1973). Esto es de
particular importancia porque muchos las características "potentes" y "convenientes" tienden a
producir monumentalmente oscuro programas (por ejemplo, APL "one-liners").

La implicación más básica del problema de legibilidad para el diseño de los lenguajes de
programación es que la sintaxis debe reflejar la semántica. Si la sintaxis del lenguaje de
programación no refleja de manera precisa y completa las manipulaciones que se están llevando a
cabo, hay pocas esperanzas de comprender el programa. Hacer que la sintaxis coincida con la
semántica implica varias cosas. Los la sintaxis debe establecer claramente lo que está sucediendo
con suficiente detalle como para ser comprensible pero no en detalles tan innecesarios como para
ser abrumador. Cuidado debe ser tomado para asegurar que una construcción que realiza una
operación particular no lea como si estuviera haciendo algo similar, pero no del todo. Además, es
más indeseable para acciones significativas que se llevarán a cabo sin ninguna indicación en la
sintaxis en absoluto. Esta regla puede parecer obvia, pero un número considerable de idiomas lo
violan (notablemente PL / I con sus conversiones y unidades ON, y ALGOL 68 con sus coacciones).

La elección más desafortunada de la terminología en el campo de la informática es el término


azúcar sintáctica porque implica que hacer construcciones de lenguaje comprensible es un volante
innecesario. Es vital que la sintaxis de una programación el lenguaje debe reflejar los patrones de
pensamiento humano, en lugar de los más "elegantes" pero patrones mucho más oscuros de, por
ejemplo, el cálculo lambda (Iglesia, 1941) (o, lo que es peor, las peculiaridades del conjunto de
instrucciones de una computadora en particular).

Una faceta final que debe mencionarse con respecto a la comunicación humana a través de los
lenguajes de programación es que los programadores no son computadoras. Sólo porque un
compilador puede entender una construcción dada, no hay garantía de que programador puede.
Las limitaciones de la mente humana lo hacen bastante fácil para una estructura complicada para
volverse inmanejable, mientras que el compilador no tiene problema. Por ejemplo, un algoritmo
de pila simple maneja construcciones anidadas para un compilador. Los seres humanos, sin
embargo, probablemente no funcionen en forma de pila manera, y las construcciones
profundamente anidadas pueden muy fácilmente volverse completamente incomprensibles.

Los seres humanos están mal equipados para hacer frente a situaciones en las que el efecto de
una constructo depende de una manera compleja en el contexto (como puede surgir a través del
uso de PL / I 0 unidades). También es muy posible que una construcción sea ambigua a una
humano Whl es claro y obvio para un compilador (Weinberg, 1971). Un bien ejemplo de th es la
expresión aritmética simple "a / b / c". En general, un analizador no considerará esto ambiguo,
pero ¿cuántos programadores humanos escribirían sin usar paréntesis? Aunque puede tomar más
tiempo para un compilador para rechazar tal construcción, la protección puede valer la pena.

3-4.2 Prevención y detección de errores

Es un hecho reconocido, los programadores de lat cometen errores. Aunque es muy actual el
trabajo en el campo de la ingeniería de software está dirigido a detener los errores en el fuente (el
programador), no hay posibilidades previsibles de que se cometan errores eliminado por
completo. Por lo tanto, es necesario ayudar al programador en la tarea de no solo prevenir sino
también detectar, identificar y corregir errores. UN un buen lenguaje de programación puede ser
un factor importante en esto. De hecho, los esfuerzos de Lampson et a1. (1977) en el diseño de
EUCLID, un lenguaje basado en PASCAL destinados a la expresión de programas del sistema que
deben verificarse, indica que un programa afectará significativamente la facilidad con la que se
puede verificar que un programa esté libre de errores. Sin dudas, habrá más investigación
conducido en esta área importante.

Uno de los servicios más fundamentales que realiza un lenguaje de alto nivel es hacer ciertas
clases de errores imposibles. Un buen ejemplo de esto es el viejo

Peligro en lenguaje ensamblador de ramificación en el medio de los datos. Dado un compilador


que funciona correctamente, no hay ninguna posibilidad de que un ALGOL el programador
cometerá alguna vez este error. Este es un excelente ejemplo de un punto hecho por Wirth (1974)
que los lenguajes de alto nivel no son solo ayudas de oficina, sino aún más valioso en que las
restricciones que imponen al programador en realidad ayuda a producir programas sin errores.

Un beneficio similar de los lenguajes de alto nivel es que ciertas clases de errores son hecho
improbable, aunque no imposible. Por ejemplo, el programador es mucho es menos probable que
se produzca un error de flujo de control en un lenguaje como PASCAL que en lenguaje
ensamblador, porque PASCAL proporciona construcciones que organizan el control fluye mejor
que las ramas condicionales del lenguaje ensamblador (¡o FORTRAN!).

Este tipo de prevención errM generalmente es una cuestión de proporcionarle al programador con
construcciones que son convenientes de usar, están cerca del programador procesos de
pensamiento, e imponer restricciones razonables en el programa.

Incluso con toda la ayuda posible en la prevención de errores, algunos errores seguirán hacerse.
Por lo tanto, es deseable que el lenguaje de programación detecte errores y hacerlos tan visibles
como sea posible, para que puedan ser eliminados.

La detección de errores se basa en una redundancia útil. Un error es detectado por notando que
dos porciones del programa, llevando algo de la misma información, no coinciden entre sí.

La declaración explícita de variables ejemplifica la redundancia beneficiosa. Cuando se hace


referencia a una variable, el compilador puede verificar que la variable sea de un tipo para que la
operación particular es significativa. Si el tipo es inapropiado, un error fue hecho.

Cabe señalar que no toda la redundancia es una redundancia útil. Una excelente ejemplo de
redundancia que no solo es inútil sino que puede ser activamente dañino es necesidad · en
FORTRAN de repetir las definiciones COMUNES en cada programa componente usándolos. Un
error en una de tales repeticiones generalmente pasa desapercibido y generalmente tendrá serias
consecuencias.

La redundancia útil se puede construir fácilmente en un idioma. Lo más significativo Las fuentes de
redundancia útil son declaraciones obligatorias de todas las variables y verificación de tipo fuerte
(del tipo utilizado en ALGOL y PASCAL). Un problema lateral de la verificación de tipo fuerte es la
ausencia de conversiones automáticas o coacciones que intentan convertir el tipo de datos
proporcionado en uno que es "obviamente" deseado en el contexto. Tal fraude detrás de escena
derrota gran parte del valor de verificación de tipo y puede dar como resultado un código oscuro.
Por ejemplo, en ALGOL 68 (que tiene coerciones), si x es un puntero a entero, la variable que no
puede ser alterado por la tarea

x:= x + 1

es x mismo! Desreferencia (ALG ~ L 68 terminología para persiguiendo punteros), conversión,

etc., debe estar presente solo si se solicita explícitamente.

Las declaraciones predeterminadas, como en FORTRAN, son un buen ejemplo de innecesario


pérdida de redundancia útil. Las declaraciones predeterminadas interfieren seriamente con el tipo
comprobando, alentando la falta de programación, y a menudo son tan complejos como para ser
confuso (¿cuántos seres humanos conocen los incumplimientos exactos de PL / I?).
Otra área de redundancia útil que no se usa tan bien como debería ser es selección por nombre en
lugar de por posición. Todos saben que es mucho más fácil y más claro decir "x.size" en lugar de "x
[5]" cuando se refiere al campo "tamaño" de la estructura "x" En general, se reconoce que una
declaración CASE que requiere que el programador etiquete las alternativas con los valores que las
causan para ser seleccionado es mucho más claro y menos propenso a errores que una versión sin
etiqueta.

Habiendo discutido algunos de los aspectos más importantes de la prevención de errores, deja que
volvemos nuestra atención a la detección de errores. La detección de errores debe ser confiable.

La detección de errores no fiable es peor que ninguna, ya que los programadores tienden a confía
en él cuando no deberían. Esto puede provocar errores que son casi imposible de encontrar. Un
ejemplo de esto es el tipo de "unión" de ALGOL 68 (p. el valor de una variable puede ser real o
entero), que no se puede verificar en tiempo de compilación. Si uno combina módulos compilados
por separado en un solo programa, es casi imposible proporcionar una verificación
completamente confiable para sindicatos sin gastos ruinosos.

La detección de errores también debe ser precisa. El usuario debe ser informado exactamente
dónde y cómo se produjo el error, para que pueda analizarse y corregirse.

Aunque el mayor factor individual en esto es el compilador (ver Capítulo 5), el lenguaje el diseño
también tiene un gran impacto. Por ejemplo, un idioma con puntero sin restricciones las variables
(por ejemplo, PL / I) harán la detección y el análisis del puntero fugitivo errores casi imposibles

Un aspecto importante de la detección de errores se refiere a la cuestión del tiempo de


compilación frente a la detección en tiempo de ejecución. En términos generales, los idiomas
deben diseñarse para permitir la detección de errores en tiempo de compilación. Esto es
generalmente mucho más barato y generalmente hace que la generación de mensajes de error
inteligentes sea mucho más fácil. Tambien es más fácil (¡y mucho más seguro!) continuar una
compilación para encontrar más errores que continuar la ejecución después de un error. Siempre
que sea posible, las reglas del lenguaje deben ser diseñados para que puedan ser implementados
fácilmente en tiempo de compilación. Por ejemplo, es generalmente es difícil y costoso detectar
en tiempo de ejecución que una variable de puntero apunta a un objeto del tipo incorrecto. Si las
variables del puntero se declaran no como "POINTER" pero como "POINTER TO type", dicha
detección de errores se puede hacer casi completamente en tiempo de compilación.

Algunas clases de errores simplemente no se pueden descubrir en tiempo de compilación,


ejemplo, el problema general de decidir si un subíndice de matriz está dentro del rango.

Las rutinas de soporte en tiempo de ejecución deben configurarse para que las verificaciones en
tiempo de ejecución sean simples y no excesivamente caro. Idealmente, debería ser tan simple y
barato de hacer el tiempo de ejecución comprueba que las verificaciones se pueden dejar para
ejecuciones de "producción", cuando la detección de errores es aún más vital. Como dice Hoare
(Hoare, 1973), "¿Qué haría pensamos en un entusiasta de la navegación que usa su chaleco
salvavidas cuando entrena en seco tierra, pero se lo quita tan pronto como se va al mar? "

Un ejemplo de cómo la verificación en tiempo de ejecución puede hacerse poco práctica es la


combinación de circunstancias en PL / I que hace posible obtener "punteros colgantes" (punteros
que apuntan a objetos desasignados ahora). Este tipo de error no se puede encontrar en el
momento de la compilación y es difícil y costoso verificarlo en tiempo de ejecución

3-4.3 Usabilidad

Pasamos ahora al tema de la usabilidad. Es obviamente importante que un lenguaje sea fácil de
usar. Ciertos aspectos de esta propiedad merecen una mención específica. El más importante
consideración, legibilidad, ya se ha discutido y por lo tanto no estar cubierto aquí.

Es deseable que las construcciones de un idioma sean fáciles de aprender y de recuerda. Una vez
que los programadores están familiarizados con el idioma, no debería ser necesario para que
consulten los manuales constantemente. El lenguaje debe ser tan simple y directo que la forma
correcta de hacer algo es razonable aparente. Por otro lado, no debería haber muchas formas
diferentes de hacer el lo mismo, ya que el programador caerá impotente tratando de decidir cuál
es el mejor. El mejor ejemplo de violación general de este aspecto de la usabilidad es ALGOL 68,
que puede hacer casi cualquier cosa, si el programador solo puede imaginar ¡cómo! PL / I es un
buen ejemplo de un lenguaje que proporciona muchos diferentes formas de hacer algo (la mayoría
de los idiomas se conforman con un tipo de entero; PL / I ¡tiene tres!).

Otro principio importante es "sin sorpresas", también conocido como la "regla de menos asombro.
"Un programador que ha hecho el esfuerzo de aprender el idioma debería ser capaz de predecir el
comportamiento de una construcción con precisión si puede ser predijo en absoluto. PL / I es el
principal mal ejemplo ~ está - cubierto de construcción esque no hacen lo que el programador
piensa, como se ejemplifica con FIXED división (véase Hughes, 1973).

Algunos lenguajes son muy adorados por su flexibilidad. La flexibilidad es como redundancia:
puede ser útil o inútil. Obviamente es necesario que las lenguas ser capaz de satisfacer demandas
que no pueden ser anticipadas por completo por sus diseñadores.

Sin embargo, la flexibilidad inútil puede ser un problema. Un ejemplo de esto es la característica
de ALGOL 60 que permite que un nombre variable sea redeclarado en un BEGIN interno bloque: el
compilador tiene que hacer frente a varias variables que tienen el mismo nombre, surgen
posibilidades desagradables para los errores del programador, y la característica es rara vez
utilizado (con la obvia excepción de los subprogramas). En general, inútil la flexibilidad debe
eliminarse siempre que sea posible. Si no interfiere demasiado en serio con otras consideraciones,
un idioma debe ser conciso. Desafortunadamente, la concisión tiende a interferir fuertemente con
legibilidad (por ejemplo, en APL). También hay un conflicto con la redundancia útil en esa
información repetitiva (la esencia de la redundancia) a menudo requiere más escribir (por
ejemplo, declarar todas las variables). La concisión debería ser probablemente dada la prioridad
más baja entre los diversos problemas de diseño.

3-4.4 Eficacia de programación

Este tema se distingue de la anterior, usabilidad, por una bastante borrosa límite. Hablando en
términos generales, esta subsección se refiere a los aspectos de ingeniería del software del uso del
lenguaje en lugar de la conveniencia para el individuo programador.
Una gran preocupación en ingeniería de software es la grabación de las decisiones hecho durante
el desarrollo del programa. El programa en sí es el mejor lugar para grabar tales decisiones. El
lenguaje de programación debe facilitar una declaración clara de las intenciones del programador.
Esto implica, en particular, que la decisión debería no ser oscurecido debajo del montículo de
detalles requeridos para implementarlo.

Siempre que sea posible, el lenguaje debe permitir a los programadores declarar su quiere y hace
que el compilador realice la implementación. La mayoría de los constructos de alto nivel los
idiomas hacen esto hasta cierto punto. En los casos demasiado frecuentes donde la complejidad
es demasiado grande para el compilador o donde la cuestión de cómo implementar el la decisión
es en sí misma una decisión importante, el lenguaje debe cumplir con la implementación los
detalles deben estar claramente separados de la declaración de la decisión original. UN algunos
idiomas, como LIS (Ichbiah et al., 1973) y Ada, tienen secciones de especificación e
implementación en sus programas. El procedimiento la noción proporciona esta separación para el
código ejecutable. Ideas sobre cómo lograr la separación de cosas como las estructuras de datos
está mucho menos desarrollada.

Además de expresar claramente las decisiones, también es deseable poder localizar el efecto de
los cambios de decisión. Tales cambios son inevitables; no debería será necesario volver a escribir
todo el programa cuando se requiera un cambio. Esta idea conduce directamente a la noción de
abstracción, que es el concepto que un programador puede hacer uso de una noción particular
(por ejemplo, el tipo de datos "lista") sin necesidad de conocer los detalles de su implementación.
Esto simplifica enormemente el uso de tales segmentos de programa predefinidos, y es un factor
clave en el control de complejidad de los programas. Si se agrega la restricción de que el usuario
no solo no lo hace necesita saber los detalles de implementación de la abstracción, pero de hecho
no tiene manera para usar dicho conocimiento, la implementación de la abstracción puede ser
cambiado sin alteraciones a los programas que lo usan.

Obviamente, es deseable que un lenguaje de programación sea compatible con la abstracción.

Los idiomas existentes lo soportan hasta cierto punto; el procedimiento nuevamente es el básico
herramienta. Desafortunadamente, se necesitan herramientas más poderosas para respaldar,
digamos, una "lista" abstracción correctamente Esta ha sido un área activa de investigación (Liskov
y ZiIles, 1974; Horning, 1976; Shaw, 1976; SIGPLAN, 1976), y los desarrollos en él pueden se espera
que sea importante para el diseño del lenguaje.

La cuestión del apoyo a la abstracción conduce también a la cuestión del apoyo para diversas
técnicas de construcción de programas, como la programación descendente.

Hay algunos problemas para proporcionar dicho soporte. En particular, mientras hay muchos
métodos diferentes, los nuevos todavía están evolucionando, y ningún método es claramente
superior a todos los demás. Si un lenguaje se va a usar con una programación específica
metodología, se debe prestar una atención definida a hacer que el idioma ayude el uso del
método; de lo contrario, el diseñador debe tratar de evitar la aplicación de cualquier técnica
particular.

El aspecto final de la efectividad de la programación a discutir es que los idiomas debería


desalentar el engaño. Los trucos de programación lindos e inteligentes son altamente perjudicial
para la legibilidad y rara vez se justifica. Aunque el lenguaje no puede hacer tales cosas imposibles,
un lenguaje que sea claro, legible y directo al menos obstaculizará tales actividades. En particular,
el diseñador de lenguaje debe tener en cuenta que ciertas características del lenguaje fomentan el
engaño. Tales características se caracterizan generalmente por tener un propósito legítimo pero
ser excesivamente general para ese propósito. Esto deja el camino abierto a los programadores
"inteligentes" para trata de exprimir tanta actividad como sea posible en la función. Los más
conocidos ejemplo de esto es la estructura de prioridad del operador y operadores potentes y
complejos de APL, que conducen al infame "one-liner" cuando sus capacidades son abusado Otro
ejemplo de mal diseño de precedencia del operador está en C (Kernighan) y Ritchie, 1978), que
tiene aproximadamente una docena de niveles de prioridad.

3-4.5 Compitability

Un idioma debe ser compilable. Menos obviamente, debería ser lo más simple posible para
compilar sujeto a ninguna reducción importante en su eficacia comunicativa y usabilidad. La
escritura del compilador ya es un trabajo complejo, y cualquier aumento en su la complejidad
puede hacer que la tarea sea inmanejable en el sentido de que el el producto será más grande,
más complejo y, por lo tanto, menos confiable.

La complejidad se puede introducir en las fases de análisis y síntesis de el proceso de compilación.


Idiomas que demandan una cantidad significativa de contexto para ser analizados con éxito son
difíciles para los escritores del compilador si están utilizando una técnica de análisis ad hoc o una
más formalizada método de análisis basado en tablas [por ejemplo, una precedencia simple (Wirth
y Weber, 1966), precedencia extendida (McKeeman, 1966), LR (k) (Knuth, 1965; DeRemer, 1969),
o LL (k) (Rosenkrantz y Stearns, 1970) método]. Este tipo de complejidad es comúnmente
introducido en idiomas en los que la coma y / o paréntesis son solía cumplir muchos roles
diferentes Por ejemplo, un paréntesis puede usarse para subexpresiones grupales, adjunte
argumentos en una llamada a procedimiento, incluya parámetros en una definición de
procedimiento, rodee los subíndices de una referencia de matriz y establezca fuera de expresiones
lógicas, argumentos de unidad ON y nombres de archivo, todo en un idioma llamado PL / I. Es
posible que se requiera un análisis sintáctico: dos o tres, por lo que se ralentiza bajó muchísimo el
compilador y aumentó el tamaño de la tabla de análisis.

La generación de código también puede hacerse más difícil si es demasiado complejo las
características del lenguaje se incluyen innecesariamente, especialmente si se usan con poca
frecuencia y / o puede simularse mediante construcciones más simples. El infame ALGOL 60
función de paso de parámetro llamada por nombre (o llamada como si por macro) ejemplifica tal
construcción difícil de compilar. Se necesita la generación de una sección de objeto código [a
menudo llamado "thunk" (lngerman, 1961)] para cada argumento. Este código debe ser
reevaluado en tiempo de ejecución cada vez que el parámetro formal correspondiente sea
referenciado La práctica ha demostrado que los esquemas más simples, como el call-by-reference
y call-by-value, son métodos de paso de parámetros suficientemente potentes.

Además de los problemas en la compilación, debería pensarse en herramientas de depuración,


herramientas de medición y otras ayudas para programar las pruebas. Tal las instalaciones son
bastante importantes para los usuarios de un idioma si los programas deben ser verificados
correctamente. Estas nociones están elaboradas en Sec. 3-5.5, Estructura de compilación.
Tanto como sea posible, siempre se deben hacer intentos para completar el lenguaje de diseño
antes de entrar en una implementación de compilador. A menudo, sin embargo, la disponibilidad
de un idioma no se aprecia del todo hasta la implementación escenario. En este sentido, la
complicidad viola el ideal de tener un diseño distinto y las etapas de implementación más que
cualquier otro objetivo de diseño de lenguaje de programa.

3-4.6 Eficiencia

"Se cometen más pecados informáticos en nombre de la eficiencia (sin necesidad lograrlo) que por
cualquier otra razón única, incluida la estupidez ciega ".

(Wulf, 1972a).

La eficiencia ha sido el tema más excesivamente sobreenfatizado en la historia del desarrollo del
lenguaje de programación. ¿Hay justificación para producir un programa que se ejecuta veinte
veces más rápido que la competencia, pero falla a la mitad ¿las carreras? La eficiencia es
importante después, no antes, de la confiabilidad.

La eficiencia debe considerarse en el contexto del entorno total, recordando que las máquinas son
cada vez más baratas y los programadores están recibiendo más caro. Por ejemplo, generalmente
no es deseable gastar un esfuerzo para mejorar la eficiencia de un programa en un 10 por ciento
porque el ahorro simplemente no justificar la inversión.

También vale la pena recordar que la eficiencia no es solo velocidad-espacio, E / S el acceso y los
patrones de búsqueda también están involucrados en la eficiencia. Ineficiencia espacial en
particular puede ser tan importante como la ineficiencia del tiempo, y se está volviendo más
importante a la luz de los recientes desarrollos en minicomputadoras y microcomputadoras.

Si bien ha sido exagerado, la eficiencia no puede ser ignorada enteramente. La declaración "ya no
tenemos que preocuparnos por la eficiencia porque el hardware será tan barato que no
necesitamos molestar "es parcialmente cierto, pero solo en parte Diferencias en la eficiencia de 10
o 20 por ciento, o incluso 30 o 40 por ciento, puede ser tolerado Cuando la diferencia es un factor
de 2 o un factor de 10, la la situación ya no es tolerable, por las siguientes razones (Hoare, 1973):

1. Por barato y rápido que sea el hardware, es más barato y más rápido cuando ejecutando un
programa eficiente.

2. La velocidad y el costo de los periféricos no mejoran en ningún lugar como tan rápido como los
de las CPU.

3. Los usuarios no deberían tener que sacrificar la legibilidad y la confiabilidad para obtener su
dinero vale la pena por el hardware mejorado.

Siendo este el caso, es necesario determinar las causas principales de la ineficiencia.

La mayor causa individual de ineficiencia es una falta de coincidencia entre hardware y el lenguaje.
A veces esto indica revisiones al idioma, por lo general para soltar características que el hardware
no puede soportar de manera eficiente.
Intentar hacer coincidir un idioma demasiado cerca del hardware puede dar como resultado un
palabrotas. El mejor ejemplo de esto son las restricciones de FORTRAN IV sobre los subíndices de
matriz. Estas restricciones adaptan FORTRAN admirablemente bien al direccionamiento hardware
de IBM 709. pero esto ya no constituye una ventaja, y el las restricciones del subíndice son
ciertamente una de las características más irritantes de

FORTRAN.

Otra degradación de la eficiencia surge de construcciones "trampas explosivas", inocentes


construcciones que se ejecutan increíblemente ineficientemente. Esto es realmente es un caso
especial del problema de falta de coincidencia, mencionado anteriormente. pero se merece
discusión explícita. El diseñador del lenguaje debe esforzarse por evitar situaciones donde un
cambio menor en un programa produce un cambio masivo en la eficiencia. Tal bruto la
imprevisibilidad molestará a los usuarios. La velocidad y el espacio requieren ¥ tiendas de una
construcción debería reflejarse aproximadamente en su tamaño y complejidad a medida el
programador lo ve: las operaciones más costosas deberían requerir más escritura. A en cierta
medida, esta es otra aplicación de la regla "sin sorpresas" mencionada más temprano.

Aceptar que la ineficiencia puede ser un problema, ¿cuáles son las posibles curaciones para ¿eso?
El que se presenta invariablemente es una optimización extensa en el compilador (ver Capítulos 12
y 13). La optimización de los compiladores puede ser muy útil, particularmente donde el recurso
escaso es el espacio Además, las características específicas también pueden ayudar

(Por ejemplo, un lenguaje GOTO-Iess requerirá mucho menos esfuerzo de compilación para el
análisis de las rutas de flujo de control).

El compilador de optimización, sin embargo, no se puede presentar como la respuesta completa.

Un buen compilador de optimización es difícil y requiere mucho tiempo para escribir. Porque
mayor tamaño y mayor complejidad, probablemente será menos confiable que una compilador no
optimizador. Se debe tener gran cuidado para que la semántica de la el lenguaje no cambia por la
optimización. Finalmente, los optimizadores generalmente funcionan mucho más lentamente que
los compiladores más simples, lo que puede ser un factor sorprendentemente significativo incluso
en instalaciones que piensan que pasan la mayor parte de su tiempo en la producción carreras.

Una forma muy desatendida de aliviar el problema de la eficiencia es la de dar consejo de


optimización para el compilador. Aunque no siempre es claro exactamente qué forma muc; h de
este consejo debería tomar. algunas ideas significativas están disponibles que puede hacer una
gran diferencia. Una mejora de velocidad de orden de magnitud puede resultar de poder
aconsejar al compilador que ciertas variables serán muy utilizados y deberían recibir una atención
especial con respecto al tiempo de acceso (tales la atención podría tomar la forma de colocarlos
en registros en lugar de en el principal almacenamiento). Mejoras de espacio considerables
pueden ser el resultado de poder asesorar al compilador de que una estructura de datos grande
debe empaquetarse tan estrechamente como sea posible independientemente del tiempo de
acceso.

Finalmente, se pueden obtener mejoras significativas en la eficiencia haciendo que el lenguaje lo


suficientemente simple que es fácil generar código razonablemente eficiente.
PASCAL (Jensen y Wirth, 1975) es un buen ejemplo de esto. PASCAL omite la mayoría de las
construcciones menos eficientes y las construcciones que tiene son simples suficiente y suficiente
para que una pequeña cantidad de esfuerzo pueda producir código eficiente.

3-4.7 Independencia de la máquina

Aunque una de las esperanzas originales para los lenguajes de alto nivel era que lo harían ser
independiente de la máquina, esta esperanza no se ha realizado plenamente. Más reciente los
idiomas siguen siendo fuertemente dependientes de la máquina, y la transportabilidad de los
programas sufre como consecuencia.

Antes de hablar de la independencia de la máquina, la definición de máquina independiente debe


ser aclarado Una definición viable es la siguiente: un idioma es independiente de la máquina si y
solo si un programa que se ha compilado y ejecutado correctamente en la máquina X, cuando se
transporte a la máquina Y, se ejecutará exactamente con mismo resultado dado la misma entrada.

Tenga en cuenta que el hecho de que un idioma no sea independiente de la máquina no lo hace
necesariamente descartar el transporte de programas. A menudo un subconjunto del lenguaje
será independiente de la máquina (por ejemplo, un subconjunto que no incluya punto flotante)
aritmética). Incluso en un idioma que es muy dependiente de la máquina (por ejemplo, uno que
incluye aritmética de coma flotante o un tipo de datos "palabra"), a menudo es posible evite las
dependencias de la máquina lo suficiente como para que los programas específicos sean
transportables.

La mayoría de las dependencias de máquina resultan de especificar parámetros en términos de


máquina particular más que en términos de las dimensiones reales involucradas. Los El mejor
ejemplo de esto es la especificación de variables enteras. La mayoría de los idiomas solo diga
"entero" y no hay indicación de qué tipo de precisión se necesita. COBOL y PL / I especifican la
cantidad de dígitos requeridos. El enfoque más simple es especificar los números más pequeños y
más grandes que estarán contenidos en un variable particular, como se hace en PASCAL.

PASCAL, de hecho, vale la pena estudiarlo como un ejemplo de cómo hacer un lenguaje casi
independiente de la máquina sin dolor. Otros idiomas importantes que se acercan a PASCAL en
independencia de la máquina son FORTRAN y COBOL; sin embargo, la independencia de su
máquina se ha producido como resultado de esfuerzos en la estandarización en lugar de a través
del diseño del lenguaje original.

Un área de extrema dependencia de la máquina es la aritmética de coma flotante. Los La causa


fundamental de esto es que todavía no existe una manera suficientemente general de definir la
precisión de los números y las operaciones de una manera independiente de la implementación.
Sin embargo, IEEE ha propuesto un estándar para punto flotante números y, con suerte, esto se
incorporará en el futuro diseño del lenguaje esfuerzos.

Problemas similares surgen en relación con el juego de caracteres. El bit exacto los patrones que
representan un personaje dado generalmente no son relevantes, pero la comparación la secuencia
de los caracteres varía lo suficiente de máquina a máquina para hacer independencia de la
máquina muy difícil. Por lo general, es seguro asumir que
1. Los dígitos están en orden.

2. El alfabeto en mayúsculas está en orden.

3. El alfabeto en minúsculas está en orden.

4. El espacio en blanco precede a cualquier carácter imprimible.

Tenga en cuenta que "en orden" significa 'a' <'b' pero no prohíbe la existencia de x tal que 'a' <x
<'b'. Los "caracteres de control" no imprimibles también causan problemas, especialmente para
E / S.

Un aspecto que es bastante difícil de hacer independiente de la máquina es la cuestión de cómo el


final de una línea está marcado en E / S de caracteres. Algunas máquinas usan una "nueva línea"
carácter, mientras que otros se basan en una longitud fija o un recuento de longitud. Un enfoque
que a menudo se adopta es incluir control de alimentación de línea como parte de las primitivas
de E / S, incluso si la máquina usa un carácter de "nueva línea". Otras preguntas de E / S, como
acceso directo archivos, a menudo son tan dependientes de la máquina que cualquier lenguaje
que incorpore construcciones especiales para ellos están destinadas a dependencias de máquinas
considerables.

Cabe señalar que la independencia de la máquina no es necesaria para todos idiomas. En


particular, los lenguajes destinados a escribir sistemas operativos o otro software especializado
dependiente de la máquina inevitablemente tendrá un significativo pegree de la dependencia de
la máquina. Para algunos lenguajes orientados a la aplicación (p. lenguajes de control en tiempo
real), es cuestionable si es necesario eliminar todas las dependencias de la máquina.

3-4.8 Simplicidad

La simplicidad es una pregunta importante en el diseño de cualquier idioma. Está claro que la
mayoría los usuarios prefieren un lenguaje simple. De hecho, un lenguaje que no sea simple lo
hará generalmente fracasan en muchos otros aspectos, como lo demuestra la falta de
abrumadores soporte para PL / I y ALGOL 68. Si bien la falta de simplicidad puede dar como
resultado lenguaje no aceptado, la simplicidad en sí misma no implica un buen lenguaje. BASIC es
ciertamente un lenguaje simple, pero no está especialmente bien diseñado y la mayoría

Deben evitarse las versiones, si es posible, para cualquier tipo de desarrollo de software serio.

Sobre la base de la historia de los lenguajes de programación, una o dos declaraciones se puede
hacer sobre la mejor manera de lograr la simplicidad (Wirth, 1974). Sencillez no se logra por falta
de estructura; el resultado de esto es el caos. La simplicidad es no se logra a través de la
generalidad del límite; el resultado de esto es un lenguaje que es imposible de dominar o
implementar por completo.

La simplicidad se logra mejor mediante la restricción de objetivos, gran cuidado en cuanto a


legibilidad y obviedad, y la base del lenguaje en torno a unos pocos conceptos bien definidos y
simples.
3-4.9 Uniformidad

La uniformidad se define como "hacer lo mismo de la misma manera independientemente de


contexto. "Si se adopta como un principio de diseño del lenguaje, puede ayudar a reducir el
número de cosas que los programadores tienen que pensar al mismo tiempo, ya que no necesita
considerar el contexto para saber cómo actuará una característica.

Como muchos otros aspectos que se han considerado anteriormente, la uniformidad puede ser
útil o inútil según cómo se aplique. Un ejemplo de utilidad uniformidad es la noción en ALGOL de
la expresión; en cualquier lugar que necesites valor aritmético, puede usar cualquier elíptica. En
general, la uniformidad inútil es hacer que una construcción se comporte de la misma manera en
dos contextos que son lo suficientemente diferente que el programador no espera un
comportamiento idéntico y no tiene uso para ello. Un posible ejemplo de uniformidad inútil es la
fusión de las ideas de "expresión" y "declaración" en ALGOL 68. Con el único y algo

Excepción discutible de usar un IF en una expresión, esta construcción es de uso cuestionable en la


mayoría de los programas.

Un subtema de "uniformidad" que merece consideración es la cuestión de eliminando casos


especiales. Hacer algo de una manera la mayor parte del tiempo y luego otra forma en un caso
particular (posiblemente por razones de implementación) debería ser evitado Un buen ejemplo de
esto es el método utilizado para asignar un valor a una etiqueta variable en PL / I: es
completamente diferente del método utilizado para asignar un valor a un elemento de una matriz
de etiquetas. Tales casos especiales tienen tres específicos problemas:

l. Ellos complican el lenguaje.

2. Conducen a diferencias de implementación, ya que algunas personas implementarán ellos


fielmente y otros "mejorarán" el diseño obviamente malo eliminándolos.

3. Tienden a "endurecerse" y no pueden eliminarse incluso después de que han sobrevivido a su


utilidad, ya que esto invalidaría las implementaciones existentes.

El punto 3 se ilustra por el hecho de que todavía vivimos con el IBM 709, como endurecido en
FORTRAN.

3-4.10 Ortogonalidad

La ortogonalidad es otra filosofía de diseño, prominente en gran parte debido a su uso extensivo
en ALGOL 68. La idea básica de la ortogonalidad es tener varios conceptos generales, cada uno de
los cuales funciona por sí mismo sin conocimiento del estructura interna de los demás. Buenos
ejemplos de esto de ALGOL 68 son valiosos acceder (por ejemplo, variables simples, accesos a la
matriz) y operadores aritméticos (p. "+"). Las primitivas que acceden al valor producen un valor;
no les importa cómo es usado. Los operadores aritméticos combinan dos valores; no les importa
cómo se obtienen valores

Aunque la ortogonalidad puede dar como resultado mejoras considerables en la simplicidad, el


ejemplo de ALGOL 68 proporciona una advertencia: es dudoso si ortogonalidad es útil como
sustituto de la simplicidad. El hecho adicional de que muy general ortogonalidad parece ser más
difícil de lograr que la simplicidad apoya los argumentos contra concentrarse en la ortogonalidad
con exclusión de todo lo demás.

La alternativa a la ortogonalidad (que a veces se ha caracterizado como "proporcionar todas las


funciones en todas partes") a veces se llama "diagonalidad" ("proporcionar características donde
sea necesario o útil "). Por ejemplo, la ortogonalidad del acceso al valor y la aritmética,
mencionada anteriormente, es incuestionablemente valiosa. Sin embargo, no es tan claro que es
valioso extender su ortogonalidad a no importar si los valores son simples valores o matrices. La
dificultad de una extensión directa a matrices es que cosas como:

a: = a / a [l]

tener resultados dependientes de la implementación. Aquí hay un caso donde un poco menos de
ortogonalidad (es decir, un poco más de "diagonalidad") parece ser necesario. La estructura del
operador que funciona tan bien para operandos escaladores simples simplemente no se extiende
limpiamente a matrices. Tenga en cuenta que esto no excluye a todos los operadores de matriz;
asignación y la comparación sigue siendo útil y directa.

3-4.11 generalización y especialización

la filosofía básica de la generalidad es que "si nosotros permitimos esto, entonces vamos a
permitir todo similares así." De manera similar a la ortogonalidad, esta noción es una ayuda para la
simplicidad, pero no un sustituto para la simplicidad. Si lleva demasiado lejos, generalidad puede
producir características raramente usadas, propensos a errores que pueden ser difíciles de
implementar. La implementación de cadenas en ALGOL 68 proporciona un ejemplo. Una adecuada
implementación de cadenas permite longitud variable, sin límites.

Los diseñadores de ALGOL 68 decidieron generalizar ALGOL 68 permite los límites de matrices
«flexibles» para cambiar en cualquier momento y hace cadenas sólo un caso especial de esto.
Resulta que, flexibles y arreglos de discos son la pesadilla de un implementador de uso marginal
excepto para proporcionar cuerdas. Un método alternativo es definir "cadena" como una primitiva
de algunos ejecutores del subconjunto han hecho de ALGOL 68. Otro caso especial de generalidad
versus especialización vale la pena mencionar. Muchas idiomas tienen funciones incorporadas que
toman cualquier número de argumentos, mientras que muy rara vez es que funciones el usuario
pueden hacer esto. Solamente funciones de E/S y algunas funciones especiales como MAX y MIN
realmente necesitan un número variable de argumentos. En la mayoría de los casos, los usuarios
no necesitan listas de argumentos de longitud variable para sus propios procedimientos si el
lenguaje ya proporciona estos casos especiales.

3-4. ~ 2 otro lenguaje de filosofías

de diseño modularidad es la filosofía básica en PL / I; la idea es que los usuarios aprenden un


subconjunto apropiado a sus propias necesidades para que no se necesita memorizar toda la
lengua (inmensa). Esta noción falla como sustituto de "simplicidad en la que funciona bien sólo si
programadores nunca cometen un error. Cuando comete un error, puede bien tienen que
entender toda la lengua para decidir lo que sucedió y por qué, porque el error puede provocar
muy bien activatio:1 de características desconocidas para ellos. En una escala algo más pequeña,
el uso de valores predeterminados a menudo consigue una especie de modularidad en el que los
usuarios están trabajando efectivamente en un sublenguaje que mejor se adapta a sus
necesidades. Esto suele ser una característica útil, siempre que el usuario entienda la lengua
entera y la estructura de los valores por defecto. Esto requiere normalmente que se utilizan los
valores predeterminados sólo en casos donde son absolutamente evidentes.

Minimality es la noción que la lengua debe contener el mínimo absoluto de construcciones que
posiblemente puede sobrevivir. Esto es generalmente una buena filosofía, pero no si lleva
demasiado lejos. Una máquina de Turing es muy mínima, pero usted no quiere programar uno. Es
importante distinguir entre un conjunto mínimo de constructos, que es el conjunto más pequeño
posible y un conjunto utilizable mínima, que es el más pequeño conjunto que todavía satisface el
requisito básico de la usabilidad razonablemente bien.

Un ejemplo de que la doctrina de "conjunto mínimo de construcciones" se aplica más


frecuentemente es en relación con las estructuras de control, generalmente con referencia a un
conjunto que no contiene el GOTO. Puede ser demostrado eso flujo secuencial y el lazo de tiempo
son suficientes para generar todas las demás construcciones (incluso el IF), pero un conjunto
mínimo de esto dista mucho de ser utilizable. Diseñadores de lenguaje de programación deben
intentar tener las construcciones de su lengua provienen generalmente de más o menos el mismo
nivel de abstracción.

La breve discusión a seguir ilustra esta idea bajo dos epígrafes: evitar construcciones que son
demasiado primitivas para encajar bien en el lenguaje y evitar construcciones que son de muy alto
nivel caber bien. Una lengua que razonablemente bien se retira de los pasivos del hardware no
debe contener uno o dos constructos que traen de nuevo. Por ejemplo, para evitar introducir el
hardware "rama" (es decir, el GOTO), ofrecen un buen conjunto de primitivas de control de alto
nivel. Para evitar la reintroducción de ese constructo problemático, el puntero sin restricciones,
tampoco puso algunas restricciones como restringirlo para apuntar sólo a los objetos de un tipo
específico o acabar con ella completamente mediante la aplicación de algo como recursiva-datos
de Hoare tipos (Hoare, Dahl y Dijkstra, 1972). Para evitar la reintroducción de la "palabra" en una
situación donde una palabra contiene, digamos, un número de 12 bits y bits de la 4 bandera,
organizar la estrategia de asignación de almacenamiento de su compilador para que

PACKED RECORD [ count: 0 to 4095; f1agl,flag2,f1ag3,flag4 : BOO LEAN; ];

resultados de asignación de almacenamiento de información deseadas y comprobación de todo


tipo se conserva. También es generalmente indeseable tener construcciones de un nivel mayor de
abstracción incluido. Esto no es tanto porque desorden del lenguaje como porque es difícil
presentar un conjunto razonablemente general sin definir esencialmente un todo nuevo lenguaje.
Los usuarios querrán saber por qué, cuando usted incluye una primitiva que ordena un archivo,
usted no podría incluir una primitiva similar a ordenar un array. Bajo circunstancias especiales,
puede ser necesario incluir algunos tales construcciones. Por ejemplo, incluso en un idioma que
admite cadenas como vectores de longitud fija de caracteres, es útil tener constantes de cadena
literal como "' una cadena,,, como una constante de tipo"matriz de carácter." En general, sin
embargo, estas construcciones sólo hacen los usuarios más conscientes de que no tienen una
lengua entera en el nivel superior de abstracción.
3-5 DISEÑO DETALLADO

Esta sección discute, en diversos niveles y desde varios puntos de vista, algunos de los detalles que
van a un idioma. Algunas áreas del diseño del lenguaje se han investigado bien que es posible
establecer directrices muy concretas; Esto se hace siempre que sea posible. Sin embargo, muchos
temas aún no están en tal s.tage, e intenta sólo para dar la discusión general sobre tales asuntos.
El diseñador de lenguaje leyendo que esta sección debería reconocer que la mayor parte de esta
sección presenta opiniones de consenso en lugar de hechos. Diseñadores deben, por todos los
medios, sienta libres de seguir consejos diferentes o a pulsar hacia fuera en sus propias (véase, sin
embargo, Sec. 3-3 y Hoare, 1973, sobre los peligros de demasiada inventiva).

Esta sección pretende proporcionar una base para aquellos que tengan la intención de golpear
hacia fuera, y una base para aquellos que necesitamos armar un idioma y hacer no desean innovar
extensivamente. La sección está organizada sobre la base de los distintos tipos de estructuras de
que un programa de exposiciones, y se pone énfasis en cómo estas estructuras están influenciadas
por el lenguaje de programación en uso. En este contexto se discuten las implicaciones para el
diseño del lenguaje. Las estructuras se ordenan esencialmente en una secuencia de abajo hacia
arriba, trabajando desde el nivel más bajo al más alto nivel

3-5.1 Microestructura

Lo que aquí se llama microestructura básicamente cubre las cuestiones del diseño del lenguaje
que afectan el aspecto de la lengua pero no cambian su semántica. El principio más importante de
la microestructura de la lengua es que el significado de una construcción, como un operador,
debería ser obvio desde su aparición. En otras palabras, los símbolos de la lengua deben ser
fácilmente reconocido por lo que son y lo que hacen. , El aspecto más visible y más bajo nivel de la
microestructura es el conjunto de caracteres utilizado. Demasiado grande un conjunto de
caracteres puede provocar desconcierto de los usuarios sobre el significado de los jeroglíficos
desconocidos en un programa.

Demasiado pequeño un juego de caracteres a menudo resulta en sintaxis algo tensas, como
testigo el uso excesivo de los paréntesis en PL / I. El conjunto de caracteres debería ser tan
estándar como sea posible para evitar el cambio de programas o el lenguaje en sí mismo cuando
se mueve entre máquinas. En general, el mejor juego de caracteres aparece que el ASCII de 7 bits
conjunto. Esta es la versión en Inglés de la ISO internacional Standard Character Set y es el
estándar de facto para la mayoría de la industria. Le falta quizás una docena caracteres útiles, pero
en general es conveniente para la mayoría de lenguajes de programación.

El conjunto de caracteres de EBCDIC de 8 bits en general tiene sobre el mismo surtido de


caracteres disponibles pero sufre de la existencia de varias versiones y gran dependencia de
fabricantes específicos. Es probable que la 48-juego de caracteres, que fue utilizado en años
anteriores de la industria informática, estará desfasado. Es claramente difícil intentar aplicar los
lenguajes de programación de hoy en este juego de caracteres pequeños. Para ser legible a un
compilador, un programa debe introducirse en la máquina de alguna manera. La tarjeta
perforada \s ya no un medio de entrada utilizado.

La tendencia actual es hacia la entrada on-line, ya sea en medios de almacenamiento magnético


para posterior transferencia a la computadora o, cada vez más, directamente en la computadora
través de terminales o estaciones de trabajo de tiempo compartido. Muy a menudo el
programador no la tipificación si comparten o se utilizan estaciones de trabajo; mayoría de los
otros métodos emplea a especialistas en entrada de datos. Cualquier sistema está en uso,
programas deben ser fáciles de escribir. Algunos principios generales pueden ajustarse hacia abajo
para mecanografiar fácil. Al usar símbolos no alfanuméricos de carácter multi, tratar de evitar
combinaciones que requieren el mecanógrafo presiona la tecla shift para un personaje y lanzar
para el próximo; tales secuencias velocidad escribiendo dramáticamente. Los mejores símbolos de
caracteres múltiples son aquellos que repiten el mismo personaje dos veces, en lugar de utilizar
dos caracteres diferentes. Keyboards differ, and hence if an installation has several different
terminal types, they quite possibly all have different keyboards. This is significant if - programmers
do their own typing, since it is hard to become familiar with one keyboard if you have to take
whichever terminal is free. The major result of this is that even programmers who can touch-type
have to look at the keys for characters other than the letters, the digits, and the following special
symbols:. / ? ( ). Some of the other character placements are nearly standard, but that is not good
enough. Try to use keywords or combinations of the above characters for frequently used
symbols. Ideally, it is easier to type short symbols rather than long ones (such as keywords). Esto
no es, sin embargo, una razón válida para usar un personaje oscuro cuando una palabra clave bien
escogida sería mucho más clara. Otro factor que afecta a la elección general de los símbolos es
que la longitud de un determinado símbolo debe reflejar ciertas características semánticas.
Símbolos del operador, los operadores menor prioridad deben tener más símbolos (es
sorprendente cuánta diferencia esto hace que la legibilidad). Por lo general, palabras clave es
preferible a las combinaciones no alfanuméricos. Utilizar nonalphanumerics solamente donde el
significado es absolutamente obvio. Cuando usted está pensando en un operador de resto, "REM"
o "MOD" es mucho más obvio que «j / "o"%." Palabras clave puede utilizarse provechosamente
como signos de puntuación así como los operadores; "Pasar x a y" es mucho más claro que "mover
x: y" o "MOVE (x, y)." Es casi imposible generar una buena representación para algunos símbolos
dados los actuales conjuntos de caracteres. Ejemplos de esto son el operador de asignación (flecha
izquierda), el operador no-igual (igual de fuerte signo) y el operador de exponenciación (flecha
hacia arriba). Asignación es especialmente dolorosa, ya que es conveniente disponer de un buen
símbolo de la flecha izquierda para eliminar la práctica de FORTRAN de usar "=" para la asignación.
Lo mejor que puede hacer en ASCII, "<-", lamentablemente es algo difícil de escribir: «< "es
cambiado de puesto,"-"no es (en la mayoría de los teclados). Igual no es casi tan mala, ya que
ASCII también carece de un buen símbolo para"no" (la tilde"-" es casi siempre colocado muy alto
en la línea, lo que es inadecuado). Si "#" es gratis, es probablemente el mejor; Si no"< >" o "j =." La
exponenciación es un problema menor, y la solución de FORTRAN, "••," razonablemente bien es
aceptada. A menudo es conveniente disponer de varios tipos de soportes; Aunque ASCII
proporciona tres ("("... '') "," ["'" "]", "{" "" "}") a veces se necesitan más. Las comillas francesas
"<<"...">>" a menudo puede ser utilizado; combinaciones de los soportes existentes tales como
"[)"... "(]"son utilizables, pero puede ser difícil de escribir y no son atractivas.

Generalmente es una buena idea utilizar un símbolo para un único propósito, especialmente si es
un operador. Es lamentable, desde el punto de vista de análisis y comprobación de errores, que
más programación remanente de idiomas de las matemáticas la Convención de usar"-"para
negación unaria y resta. Si el diseñador se siente experimentar, tal vez"NEG" podría ser utilizado
para nega ción. Cuando una adecuada combinación de caracteres no alfanuméricos no es
evidente, se debe elegir una palabra clave para un símbolo. Ciertos principios generales pueden
establecerse para la elección de dichas palabras clave. Palabras clave debe ser pronunciable. Es
más fácil recordar una sílaba que una secuencia de letras sin relación, impronunciable. Palabras
clave generalmente debe ser elegido por lo que no es probable que duplicar identificadores
definidos por el usuario. Incluso si hay alguna forma de distinguir los dos, la posibilidad de
confusión es alta. Al elegir las palabras clave, trate de usar los verbos y Preposiciones;
programadores tienden a usar los sustantivos y adjetivos como nombres de variables. Palabras
clave debe deletreado la manera que el usuario les esperaría. Si tiene que utilizar "Tamaño" como
operador, no escribe "SIZ". Tales deletreos peculiar causará más problemas que ahorran. Si es
posible, no tiene dos palabras con ortografía muy similar (por ejemplo, "PROGEND" y "PROCEND").
Abreviar palabras clave sólo cuando la abreviatura es absolutamente evidente. Esto ahorra escribir
considerable y se utiliza con frecuencia. Sin duda está justificado abreviar "Procedimiento" a
"PROC" o "Precursor" a "PRED"; abreviar "Externo" a la "EX" o "REAL" a "RE" no es. Abreviar
"Entero" a "INT" es probablemente justificado. A veces será necesario tener un par de palabras
clave sucesión de algo. Una práctica popular en los últimos años, en particular por ALGOL 68
(Lindsey y van der Meulen, 1972; Van Wijngaarden et aI., 1975), ha sido invertir la ortografía de la
palabra clave principal para obtener la clave final. Esto trabaja parte del tiempo; pero muchos de
los resultados son horribles. Es probablemente mejor usar sólo los pares siguientes, que están
ganando gran aceptación:

Si tienes que terminar una construcción que comienza con "SELECT", usar "ENDSELECT," no
"TCELES." Otra alternativa, que es más difícil de diseño, es tratar de encontrar una palabra clave
que "cabe" como palabra clave final (ejemplo:... LAZO... REPEAT"). También es posible utilizar un
par de palabras clave (ejemplo: "lazo... END LOOP") en lugar de una sola palabra clave, si no grave
confusión se introduce en otra parte. También, tratar de evitar el uso excesivo de una palabra
clave; la PLII "Final" es un bruto violador de esto. Un asunto adicional que es importante con
respecto a palabras clave es cómo decirles aparte de identificadores definidos por el usuario.
Existen tres enfoques distintos:

1. palabras clave es "reservado" y no puede usarse como identificadores.

2. palabras clave distinguen de identificadores en base a contexto.

3. palabras clave está precedido por un carácter especial para marcarlos.

Generalmente, alternativa 1 obras mejor-es simple y, teniendo en cuenta una cuidadosa selección
de palabras clave, rara vez causa problemas. Alternativa 2, que utiliza en PL / I, signitlcantly
complica el analizador. Probablemente fue adoptado en PL / porque el número de palabras clave
en algunas implementaciones era tan grande que ningún usuario podría razonablemente ser
esperaba para evitarlos todos. Idiomas de un tamaño más realista no tiene esos problemas.
Alternativa 3 es utilizado en las implementaciones de ALGOL 60 y ALGOL 68. Consiste en escribir
extra y hace que el programa no se puede leer. La aplicación solo más acertada del ALGOL 60
(Burroughs uno) utiliza la alternativa 1. Identificadores definidos por el usuario merecen alguna
discusión. El conjunto básico generalmente es letras y dígitos, excepto el primer carácter debe ser
una letra. Si dispone de letras mayúsculas y minúsculas, debe considerar equivalente. Hay pocas
cosas más confuso que tener "A" y "a" actuar como variables independientes. Se ha convertido en
costumbre de agregar algunos caracteres adicionales a la lista de "letras", para mayor comodidad.
Probablemente el más importante es el carácter de subrayado '_', que permite, en efecto, el uso
de "en blanco ~" dentro de identificadores. Esto proporciona una gran mejora en la legibilidad.
Con técnicas modernas de manejo de cadenas no es justificación para limitar la longitud de
identificadores, ya sea explícitamente ("máximo 6 caracteres") o implícitamente ("sólo los 6
primeros son significativos"). Este enfoque no requiere almacenamiento ilimitado, ya que algunos
identificadores serán más de 15 caracteres (Alejandro, 1972). Un "límite de silencio", dicen, 255
caracteres nunca ser superados o incluso se acercaba, especialmente si los identificadores no
pueden dividirse en los límites de la línea. Otro aspecto de la microestructura que merece especial
atención es el diseño de la Convención de comentario. Debe ser breve (no "comentario"), y debe
ser fácil de escribir (no "I *"). Como Hoare (Hoare, 1973), probablemente el mejor tipo de
Convención de comentario es que es bastante común en montadores pero infrecuentes en
idiomas de alto nivel: un símbolo particular comienza un comentario, que luego se extiende hasta
el extremo de esa línea. Esto elimina los comentarios "runaway" de PL / I y este Convenio resulta
para ser sorprendentemente cómodo de usar. Una cuestión importante es la elección de un
símbolo de comienzo. Mientras que varios símbolos se han utilizado para este tipo de
comentarios, el requisito de mecanografiar fácil puede eliminar muchos de ellos. Además, estos
símbolos un carácter son a menudo útiles como operadores. Idealmente, tal símbolo debe: 1. ser
un símbolo de dos caracteres, preferiblemente ambas el mismo carácter. 2. ser un símbolo
raramente, si siempre, utilizado como un operador, con ningún significado obvio como tal. 3. se
compone de caracteres situados en el mismo lugar en todos los teclados.

El símbolo de "lo" es un ejemplo de una buena opción. Sobre el aspecto del programa, la mayoría
idiomas han seguido plomo de ALGOL en declarar que el espacio en blanco es significativo sólo en
eso (excepto en cadenas) separa dos símbolos y no puede ocurrir dentro de un símbolo. Si el
sistema tiene un carácter de "tabulación", es útil tratar la misma manera. También es razonable
para el tratamiento de fin de línea del mismo modo, excepto que también puede terminar
Comentarios. Es más deseable exigir construcciones en ciertas columnas. Una última cuestión
relativa a la apariencia de la lengua: a menos que se planea utilizar un formateador de texto
automático de fuente como parte del compilador, es una buena idea tener algunos medios de
causar un pase a la siguiente página del listado. Si el sistema tiene un carácter forma-alimentación
disponible, es un asunto sencillo para arreglar para que el escáner del compilador ignorar
caracteres tales como tokens. Cuando la fuente es la salida, el carácter de alimentación de forma
generaría el salto requerido a la siguiente página.

3-5.2 estructura de expresión

la expresión es a menudo la unidad fundamental de la computación en un idioma. Sus


componentes, los operadores y accesos de valor, son demasiado peculiares lenguajes específicos
para recibir mucho tratamiento aquí, pero algunos comentarios pueden hacerse sobre las
expresiones en general. Un tema relacionado con las expresiones es la cuestión de la orden de
evaluación. El método normal de determinar el orden de evaluación se basa en dos niveles:
soporte explícito y operador vinculante. Sucesión explícita comprende ambos entre paréntesis y la
sucesión general proporcionada por los límites de la expression. En este sentido, cabe destacar
una idea que tienen algunos idiomas (como LISP): tiene dos tipos de construcciones de paréntesis,
[]"y"()", para que sea más legibles expresiones complejas. Operador vinculante es el aspecto más
familiar de la orden de evaluación. Existen tres sistemas básicos de enlace: de izquierda a derecha,
de derecha a izquierda y la prioridad. De izquierda a derecha no se puede recomendar; la única
razón por la que se utiliza en todo (absolutamente con frecuencia en ensambladores) es que es
simple. De derecha a izquierda es notable porque se utiliza en el APL. Se usa principalmente
debido a la gran variedad de operadores, lo que es difícil intentar asignar precedencias a todos
ellos. Un enfoque alternativo para un idioma con muchos operadores es asignar prioridades a
algunos de los operadores más sencillos y requieren soporte explícito del resto. Experimentos en
diseño del lenguaje (Gannon, 1975) han indicado que regla de derecha a izquierda "natural" de
APL es de hecho más difícil entender que las reglas de prioridad simple. Una cosa que debe
evitarse es el gran número de niveles de prioridad, al igual que en C, que puede confundir en lugar
de iluminar. Restricción a un pequeño número de niveles de prioridad se puede hacer mejor,
considerando que los operadores son, psicológicamente hablando, en diferentes niveles. El deseo
de reducir el número de niveles no debe llevar al diseñador a colocar, en el mismo nivel, los
operadores que son psicológicamente en diversos niveles. Por ejemplo, la decisión de PASCAL a
pone "y" en el mismo nivel que"." y "O" en el mismo nivel como "+" puede haber sido un error:
simple comparaciones múltiples tales como "una banda = c = d" son ilegales; Esto debe estar
escrito "(a = b) y (c = d)". Un aspecto menor del operador vinculante es la relación de los
operadores unarios y binarios. En muchos idiomas t4is se complica considerablemente por el
hecho de que no todos los operadores unarios tienen el mismo pnonty; NO se da a menudo una
prioridad muy baja. Si bien existe cierta justificación para ello (para poner los operadores lógicos
en el mismo rango de prioridad), es probablemente más fácil de adoptar el enfoque de ALGOL 68
(Van Wijngaarden et ai., 1975): todos los operadores unarios tienen una prioridad más alta que
todos los operadores binarios. Matter related to expression structure is the much-praised notion
of the "expression language", the idea that every construct returns to value and may be used
anywhere a value is required. At first sight, the concept looks appealing. The whole idea of the
"statement" vanishes. It becomes possible to use an IF or CASE to select which of several values
should be used at a particular point in an expression. Unfortunately, a deeper examination shows
that the loss of "statement" is more than counterbalanced by the additional complexity in the
behavior of "expression." Además, todos pero los casos más sencillos de utilizar un complejo
construyen dentro, digamos, una expresión aritmética se convierten en ilegibles. También es muy
claro a qué valor debe devolverse, por ejemplo, un bucle. Si los usuarios pueden proporcionar sus
propios valores, esto empeora las cosas. El lenguaje de expresión es una idea novedosa, pero en
realidad parece aumentar la complejidad, amplía el campo para el engaño y la inteligencia a
expensas de la legibilidad y no añade ninguna capacidad útil. La única excepción posible a esto es
la expresión condicional: una simple IF-expresión que elige uno de dos valores. Esto es
razonablemente fácil de entender y a veces es útil; argumentos en contra de él son la poca
frecuencia de uso y la dificultad de llegar a una sintaxis concisa y legible. La alternativa a un
lenguaje de expresión es el lenguaje de la declaración, donde se hace una distinción entre las
declaraciones y expresiones. Control construcciones son declaraciones y no son expresiones
válidas. Expresiones un valor de rendimiento y, salvo el uso indebido de llamadas de función, no
suelen tener efectos secundarios. Este tipo de organización corresponde a la forma
programadores normalmente pensamos sobre sus programas: una "declaración" realiza una
acción, una "expresión" produce un valor.

Estructura de datos 3-5.3

En este inciso, se considerarán cuatro aspectos de la estructura de datos. En primer lugar, se


presenta una discusión de las formas alternativas para las declaraciones de datos. A continuación,
un resumen de la variedad de tipos de datos que están disponibles en lenguajes de programación
hoy en día y se debe considerar si se lleva a cabo un nuevo lenguaje diseño. A continuación se
describen los efectos de las estrategias de asignación de almacenamiento de información en
aspectos de diseño del lenguaje. El tema final se aborda una discusión sobre el alcance de las
variables en un lenguaje de programación. Las declaraciones de una lengua son el medio por el
que nonprocedural información es transportada al compilador. Mientras que existe un gran
número de formas especializadas de declaraciones para satisfacer necesidades específicas, esta
discusión se centrará en los tres requisitos comunes de la mayoría de lenguajes de programación:
las declaraciones de constantes, tipos y variables.

Recientemente se ha vuelto evidente que es muy útil para poder dar nombres a constantes. Esto
evita el uso de "números mágicos" en el cuerpo del código. En este programa de manera
legibilidad está mejorada y es mucho más fácil cambiar el valor de, digamos, un tamaño de la
tabla. Algunas idiomas (véase Clark y Horning, 1971) han incluso adoptado la posición que, a
excepción de 0, 1 y 2, todos constantes numéricas que ocurre en el código deben ser nombres
dados por las declaraciones constantes. Por consiguiente, "números mágicos" puede no haber en
cualquier lugar excepto en tales declaraciones. Otro aspecto de la declaración de constantes es
que es útil para poder escribir una constante para cualquier tipo de datos en el programa.

Por ejemplo, debe ser posible escribir una constante matricial. Tales constantes en gran parte
eliminaría la necesidad para inicializar arreglos de discos, como más inicializadas matrices son
realmente constantes de matriz, impedirían que el programador de destruir accidentalmente el
contenido de dicha constante. Asimismo, es útil, esencialmente como una forma de abreviatura,
para ser capaces de dar nombres a los tipos y luego usar esos nombres en la declaración de
variables o en la construcción de tipos más complejos. Estas declaraciones de tipo pueden ahorrar
una gran cantidad de la escritura al tiempo que también mejora la legibilidad. La sintaxis de las
declaraciones de constante y tipo puede ser similar, como en el siguiente:

CONST table_size = 437;

TYPE big_array = ARRAY [1 TO 1000, 1 TO 1000] OF integer;

Las similitudes entre los dos pueden traer un pensamiento tentador: ¿por qué no generalizar tanto
para una instalación sin parámetros macro? La razón básica para evitar esto es que las constantes
y tipos son, en general, lo único que se declararía. Por lo tanto, un constructo tal había sería una
invitación abierta para programación complicado "inteligente". Si el diseñador no decide permitir
la inicialización de variables, la sintaxis de declaraciones de variables se vuelve bastante simple.
Una sintaxis muy teadable es el utilizado por PASCAL:

V AR a, b, c : integer
Nota que esta sintaxis es realmente conveniente sólo cuando toda la información sobre, digamos,
arreglo de discos de límites se localiza en la especificación del tipo. Legibilidad de esta forma
disminuye rápidamente si la lista de nombres de variables es desordenada para arriba con otras
cosas como ini tialization. Ahora pasemos toa discusión de tipos de datos en lenguajes de
programación. La referencia básica para los tipos de datos moderno es obra de Hoare (Hoare, Dahl
y Dijkstra 1972). El trabajo de Wirth en PASCAL (Jensen y Wirth, 1975) es digno de mirar como una
aplicación práctica de muchas de las ideas de Hoare. Puesto que el material sobre este tema se
basa pesadamente en estos dos documentos, no se hace mención de estas referencias. Hehner
(1975) y Tennent (1975) presentan también algunas ideas interesantes.

La base de todas las nociones de estructura de datos es el tipo de datos. ¿Qué es un tipo de datos?
Aunque ha habido considerables diferencias de opinión sobre esto, la opinión más frecuente es
que un tipo de datos es un conjunto de valores y un conjunto de operaciones en estos valores. Por
ejemplo, "entero" podría ser el conjunto de valores "..., - 2, - 1,0,1,2,... "y el conjunto de las
operaciones" +, -, *, /, +-. " Tenga en cuenta que la presencia de las operaciones como parte de la
definición implica también que los miembros del conjunto de valores no pueden ser elegidos
arbitrariamente; debe haber cierta coherencia entre ellos para que las mismas operaciones se
aplican a todos. Existen tres enfoques distintos tipos de lenguajes de programación.

La primera es que ninguno en absoluto, característico del lenguaje ensamblador y algunos idiomas
de "grado medio". La conveniencia de tipo esté bien establecida; así no se dará más atención a su
ausencia. Los dos enfoques restantes se denominan "duros" y "suave" escribiendo. En ambos
enfoques un valor tiene un tipo asociado. La distinción fundamental entre los dos enfoques es que
en un lenguaje con tipos blandos cualquier variable puede contener cualquier valor (como
SNOBOL y APL), mientras que en un lenguaje duro escribió un tipo específico está asociado con
cada variable y la variable puede contener sólo valores pertenecientes a ese tipo! dejó de valores.
En general, ahora se acepta que escribir suave hace un lenguaje un poco más pequeña y algo más
conciso, duro escribiendo es muy superior desde el punto de vista de error en tiempo de
compilación comprobación (Wirth, 1974) y no presenta ningún inconveniente notable . Discusión
posterior asumirá duro escribir.

Aunque no hay diseñadores de dos lenguaje acuerdan sobre exactamente cómo la amplia gama de
tipos de datos debe subdividirse, tipos generalmente pueden agruparse en tres categorías:
simples, compuestos y complejos. Estos términos son relativos y los límites entre ellos son difusos.
Los tipos de datos simples son en el nivel relativamente primitivo de una lengua; se utilizan para
construir tipos más complejos y se proporcionan generalmente más o menos directamente por la
máquina sobre la cual se implementa el compilador. Un asunto de interés general en tipos simples
es la cuestión de la ordenación; ¿que i-s, se consideran los valores de un tipo en una secuencia
específica? Básicamente determina si las comparaciones como "<" son válidos para todo simples
tipos y también pueden influir en el bucle de contado de la lengua. En algunos ~ ases, tales como
números, ordenar es obviamente necesario. Para algunas otras situaciones, la naturaleza precisa
del ordenamiento es menos evidente.

Comentarios específicos se realizará en este punto a lo largo de la subdivisión. El tipo más


elemental de tipo simple es lo que se ha llamado el tipo de enumeración, en la que el
programador simplemente da una lista de identificadores como el conjunto de valores. Ejemplos
de este caso son:

TYPE color = (red, green, blue)

TYPE job_status = (executing, waiting, terminated)

Tenga en cuenta que estas declaraciones muestra constituyen las declaraciones de los
valores"rojo," "verde", etc., así como los identificadores "color" y "job_status." Las operaciones
válidas sobre los valores de esos tipos son asignación y comparaciones igual y no igual. Las
constantes de este tipo son simplemente los nombres dados. Obviamente, dentro de la máquina
los valores de un tipo de enumeración se representan como números enteros pequeños, pero esto
no es visible para el programador; "rojo" es no un valor de tipo "integer". No está claro si los tipos
de enumeración deben ser ordenados o no; tal vez el programador debe ser capaces de pedir un
tipo de enumeración específica a ser ordenado.

Sin ellos, el programador se conduce con frecuencia a simularlas mediante números enteros, un
proceso que funciona pero es propensa a errores. Cualquier idioma para trabajo serio de software
debe tener tipos de enumeración. Probablemente la forma más común de un tipo de datos es el
número. Es realmente muy poco lo que puede decirse acerca de números de punto flotante, otros
que son, en este momento, dependiente de la máquina en la implementación del lenguaje casi
todos. Por lo tanto, la discusión se limita en gran medida a números enteros. Mientras que el tipo
simple "integer" es bastante claro, hay dos notables modificaciones que se pueden hacer. La
primera modificación consiste en la idea de la "subrange," un tipo que tiene, según su valor, un
subconjunto de los enteros. Ejemplos de esta modificación son los siguientes:

TYPE cents = 0 TO 99

TYPE day_oLmonth = 1 TO 31

TYPE inning = 1 TO 9

Sub rangos permiten que el compilador optimizar la asignación de almacenamiento de


información, ya que es fácil determinar exactamente cuánto espacio necesita una variable de
rango sub. Sub tipos de gama son una forma mucho más legible de esta haciendo que cosas como
"shorCinteger." Subranges también son independientes de la máquina, mientras que "entero" no
es. Por último y lo más importante, subranges proporcionan redundancia muy valiosa; fácilmente,
el compilador puede generar código para comprobar que el valor de ser asignado a una variable
de subrango dentro el subrange. Una cierta cantidad de esta comprobación puede hacerse aún en
tiempo de compilación. Mientras que sub gamas son sin duda valiosas, un aspecto en el que están
un poco débiles está en definición formal. La estrecha relación entre sub y enteros complica el
proceso de definirlas porque el programador espera naturalmente a ser utilizable en cualquier
lugar son enteros.

Esto significa que una definición formal debe considerar sub valores de rango a convertir
automáticamente a valores enteros siempre que sea necesario. Probablemente la mejor solución a
esto es decir que un tipo subrango es realmente "entero" en disfraz, y que el respeto sólo en que
no es idéntico a "entero" es que una variable de subrango puede almacenar sólo un subconjunto
de todo el"entero" sistema de valor. También puede ser útil tener sub rangos de otros tipos
de"integer" (p. ej., subranges del "carácter"). Sería mejor decir que cualquier tipo pedido puede
tener gamas sub definidas. La otra modificación notable "entero" es uno que también es aplicable
a punto flotante.

La idea, que es originalmente debido a Hoare (1973), es que uno debe ser capaz de asociar
unidades (por ejemplo, kilogramos, ticks de reloj, metros por segundo) con variables numéricas.
Esto es esencialmente una forma de proporcionar redundancia más útil. El compilador puede
comprobar que las operaciones realizadas sobre valores numéricos eran de hecho válidas. Por
ejemplo, agregar kilogramos para las garrapatas del reloj es sin duda error de alguien; debe
asignarse un valor obtenido multiplicando los metros por segundo por segundos sólo en una
variable que contiene metros. Un centro completo para especificar este tipo de cosas sería algo
complejo (considerar, por ejemplo, conversiones de pies a metros, el uso de Newton como un
sinónimo para kilogrammeters por segundo cuadrado), pero la idea es novela y es compatible con
Ada. Después de los tipos de datos numéricos, al "carácter" tipos probablemente son lo más
frecuentemente usados.

Es lamentable que hardware actual no admite cadenas de longitud variable verdaderamente bien.
El resultado es una multiplicidad de enfoques a los personajes: 1. "Carácter" es un tipo simple,
manteniendo un carácter. Cadenas se implementan como conjuntos de caracteres. 2. secuencias
pueden variar en longitud, pero deben tener una longitud máxima especificada para fines de
asignación de almacenamiento de información. 3. secuencias pueden variar arbitrariamente en la
longitud (eficiencia sanciones son aceptadas por usabilidad). Un cuarto enfoque, el uso de cadenas
de longitud fija, no serán considerados. La primera aproximación esencialmente puentea el
problema. Las propiedades de la matriz son ahora la clave para la utilidad de secuencias. Por
desgracia, las matrices son absolutamente inadecuados para la implementación de cadenas; uno
puede aceptar la cadena pobre manejo que resulta, como en PASCAL, o bien intentar estirar las
propiedades de la matriz y quedar generalmente con resultados no deseados, como ALGOL 68.

El segundo enfoque es posiblemente el mejor compromiso; Lamentablemente, tiene las


características habituales de los compromisos. No es tan simple como la alternativa 1 o como se
puede usar como alternativa 3. Si la lengua debe funcionar sin el extenso soporte de tiempo de
ejecución (por ejemplo, para escribir un sistema operativo) o debe ser muy eficiente, este es
probablemente el método a utilizar. Restricciones ambientales o eficiencia permitiendo, el tercer
enfoque es el mejor. La pérdida de eficiencia a menudo puede ser sorprendentemente pequeña, y
la ganancia en usabilidad, como exhibió en SNOBOL puede ser considerable. El último de los tipos
simples es "Boolean", que es de uso poco frecuente, pero muy esencial cuando ocurren. PL / me
acerco es generalizar la variable booleana en una cadena de bits análogos a una cadena de
caracteres.

Desde las medidas reales (Alejandro, 1972) muestran que la inmensa mayoría de cadenas de bits
son realmente un poco larga, la generalización parece haber sido innecesario. Para la mayoría de
las idiomas, booleano es suficiente. Aunque Boolean podría definirse como un tipo de datos, el
enfoque más sencillo es simplemente para predefinir, como PASCAL, de tipo booleano = (false,
true) si se dispone de tipos de enumeración, este enfoque satisface todas las exigencias y no
necesidades más complejidades en el compilador.
Ahora que hemos terminado con los tipos simples, el siguiente tema de discusión es el tipo
compuesto. Estos son tipos construidos en formas bastante sencillas de tipos más simples. La
variedad más obvia de tipo compuesto es la matriz. Dos aspectos merecen comentario. En primer
lugar, debe considerarse a extender subíndices a los tipos de datos que no sean números enteros.
General números floating-point son obviamente inadecuados como subíndices, tipos de
enumeración y caracteres solo pueden a menudo provechosamente emplearse como subíndices.
El enfoque adoptado en PASCAL de hecho aprovecha de esto. Las dimensiones de una matriz están
dado en términos de los tipos de sus subíndices:

TYPE nonsense = ARRAY [color, 1 TO 99] OF integer

(véase la definición del color anterior). La segunda cuestión relativa a matrices trata de arreglos
dinámicos, cuyos tamaños están determinados en la entrada del bloque en lugar de en tiempo de
compilación. Arreglos dinámicos añadir a la complejidad de tiempo de compilación y, por esta
razón, han quedado fuera algunos idiomas. Sin embargo, pueden ser muy útiles en aplicaciones
donde tamaño de la matriz depende de datos de entrada. El aumento de complejidad es
compensado a menudo por la utilidad de matrices dinámicas, y por lo tanto en muchos casos los
arreglos dinámicos deben incluirse en un idioma. El otro tipo de datos compuesto es el registro,
conocido a veces, equivocadamente, como la "estructura." Para muchas aplicaciones es sólo tan
fundamental como la matriz. La falta de registros es probablemente un factor importante en el
rechazo de ALGOL 60 hy la comunidad informática.

Un adorno bastante útil del registro es el registro de la variante de etiquetado. Esto permite que
las situaciones frecuentes que la estructura de parte del registro depende de los datos en una
parte anterior del mismo:
El disco tiene algunos campos fijos (por ejemplo, "given_names") seguidos por un campo etiqueta
que selecciona qué variante del resto del disco está en uso. El campo etiqueta se llama "sexo" y es
del tipo de enumeración "(hombre, mujer)." El campo etiqueta es seguido por las variantes, que
en este caso son "married_name" y "maiden_name" o "lasLname." Con tal disposición, el
compilador puede establecer controles de tiempo de ejecución para asegurarse de que cualquier
referencia, por ejemplo a "maiden_name," se hace referencia a un registro cuyo campo "sexo" es
"hembra". PASCAL y Ada soportan esas instalaciones. Relleno de COBOL tiene la interesante
noción de ser capaz de utilizar"" como un campo de nombre en un registro. Esencialmente, esta
característica permite al programador un campo anónimo del tamaño dado. Esto puede ser útil si
se procesa un archivo de registros de varios programas, algunos de los cuales ignoran ciertos
campos. Un tipo de datos compuestos menos obvio es una forma simple de conjunto.

Mientras que los sistemas en general son parte de un sujeto había cubierto más tarde por
"complejos" tipos, una forma restringida de conjunto merece mención, ya que se puede
implementar simplemente y eficientemente pero sigue siendo bastante útil. Si el número de
posibles elementos de un conjunto es muy pequeño, es posible implementar el sistema asignando
simplemente un campo de bits. Se asigna un bit para cada posible elemento. El bit es 1 o 0 para
indicar si el elemento está en el conjunto. Con este esquema, conjunto de manipulaciones se
realizan fácilmente por instrucciones lógica de la máquina. Por ejemplo, un conjunto sobre un tipo
de enumeración

TYPE colorset = SET OF color;

(ver definición de "color" antes) puede ser especialmente útil. El tipo de datos compuesto final
que se considerará es el puntero. El puntero se conoce como un compuesto tipo en contraposición
a un tipo simple ya que es más sabiamente utilizada en conjunto con y no independiente de otro
tipo de datos. De hecho, es bastante amplio acuerdo que indisciplinado de punteros es, en todo
caso, incluso peor que indisciplinados uso del GOTO. Hay dos soluciones de diseño de lenguaje de
programación importantes para esto. La primera solución es recursiva los tipos de datos de Hoare
(Hoare, Dahl y Dijkstra, 1972), que eliminan el uso explícito de un puntero en conjunto. La idea
básica detrás de los tipos de datos recursivos es que en lugar de tener, decir, punto de un campo
de un registro a otro registro, el segundo registro es conceptualmente un campo de la primera. Un
tipo de datos de reCUfSlve es uno en el cual ocurre el nombre del tipo se define en su propia
definición.

Tal noción se ha utilizado en la definición de una lista en LISP y en la definición de un patrón


recurrente de SNOBOL 4, en particular. Árboles también se pueden definir de tal manera. En
realidad la aplicación en el nivel del equipo es el mismo, a través de un puntero, pero los tipos de
datos recursivos ocultan completamente del programador. Un tipo de datos recursivos es un
concepto relativamente nuevo, y aún es demasiado pronto decir si resultará ser decididamente
superior a la alternativa, es decir, manteniendo el puntero pero colocando restricciones en él. La
segunda solución depende básicamente de qué restricciones deben colocarse en el puntero.
Existen dos restricciones que parecen cumplir los requisitos. El primero es exigir el puntero para
que apunte a un objeto de un tipo específico. En otras palabras, no se declara una variable de tipo
"Puntero"; uno declara que es de tipo, digamos, "Puntero a entero." Esta restricción solo elimina la
mayoría de los peligros de punteros.
La segunda restricción, que ha sido sugerida por Wirth (1974), es que no hay "dirección de '
operador; es decir, debe ser imposible hacer que un puntero apunte a una variable de nombre.
Indicadores deben apuntar sólo en almacenaje del montón anónimo, dinámicamente asignado.
Esto evita en gran medida la confusión posiblemente grave derivadas hacen referencia a la misma
ubicación de almacenamiento bajo varios nombres diferentes. La última variedad de tipo de datos
es el tipo de datos complejo, que es un esfuerzo de implementación no triviales que, arriba no
trivial y una probabilidad significativa que lejos de ser óptimo para algunos usuarios una
implementación particular. Ejemplos de algunos tipos de estos datos son li5ts, sistemas generales,
árboles, pilas y colas.

Mientras que este tipo de datos ~ pueden implementarse mediante punteros o tipos de datos
recursivos, tal implementación ~ ons son muy difíciles de cambiar si se descubre que necesitan
ajuste (por ejemplo, si la aplicación es muy rápida, generalmente resulta que el espacio de
almacenamiento es el real cuello de botella). La solución a largo plazo parece ser la idea de la
abstracción de datos. La noción básica de la abstracción de datos es que debe ser posible definir
una "interfaz" para un tipo de datos. Esta interfaz especifica las distintas operaciones qué valores
del tipo sin mencionar cómo se implementan los valores o las operaciones.

La puesta en práctica sí mismo entonces consiste en especificar cómo se implementan los valores
(en términos de tipos de datos más primitivos) y cómo las operaciones se ponen en ejecución (un
procedimiento para cada operación). El usuario puede usar el tipo de datos sin saber ni
preocuparse por los detalles de la implementación y la aplicación se puede cambiar sin reescribir
programas de usuario. Abstracciones de datos están en las primeras etapas experimentales, y no
hay acuerdo en los detalles. Para aquellos que deseen investigar más a fondo, Horning (1976) es
una introducción más detallada a las ideas presentadas aquí, Liskov y Zilles (1974) da una
excelente demostración de la utilización de abstracciones de datos y Shaw (1976) y el resto de la
expedición SIGPLAN (1976) constituyen un excelente resumen del tema.

Una característica de algunos lenguajes es el realizar conversiones automáticas o cocrcions. Un


ejemplo de una conversión es convertir la cadena "123" en el valor numérico 123 si la cadena se
agrega a una variable numérica. Coerci ~ ns han sido mencionado en el artículo 3-4.2. Hay buenas
razones por qué estas características no deben incluirse en limpiamos lenguas. Complicar
excesivamente la lengua, obstaculizar extensibilidad genuino (por ejemplo, abstracción de datos) y
seriamente impedir legibilidad debido a violación frecuente y excesivo del principio de menor
sorpresa. Esto no excluye la inclusión de las conversiones de datos y puntero persiguiendo en
nuevas lenguas; simplemente significa que este tipo de cosas siempre debe solicitarse
explícitamente y no debe suministrarse a espaldas del programador. Instalaciones de conversión
explícita pueden tomar uno de dos formas.

La forma más simple es que de las funciones que toman un valor de 1 tipo y devuelve un valor de
otro tipo (por ejemplo, una función "redondo" que "real" y volver "integer"). La alternativa es el
enfoque de ALGOL 68, que tiene un operador especial que tiene un valor de entrada y un tipo de
datos y convierte el valor de entrada para el tipo especificado.

La asignación de almacenamiento para variables en un programa es, en sus detalles, el negocio del
compilador y el run-time ~ ystem. Ciertas políticas generales, sin embargo. son parte del diseño
del lenguaje. Existen básicamente cuatro formas de asignación variable; examinará a su vez. con
comentarios sobre cada uno. La forma más simple y más antigua de asignación es estática,
inmortalizado en FORTRAN y en instalaciones propias de ALGOL 60. Ahora parece que la mayor
utilidad de la asignación estática es su uso no en los procedimientos pero a nivel mundial.

Mayoría de los casos donde se utilizaría el ALGOL 60 de estilo propio puede ser mejor organizada
como un conjunto de variables estáticas con un conjunto de procedimientos para operar con ellos.
Esto hace asignación estática correspondiente a la abstracción de datos y modularidad (véase Sec.
3-5.5). La forma más popular de la asignación, por ALGOL 60, es local, dinámica o automática. La
simplicidad de la utilidad y la aplicación de asignación local hacen que sea la forma de elección de
variables normales dentro de los procedimientos.

La tercera forma de asignación, de retención, es que no se ha utilizado extensivamente. Esto es


diferente de asignación estática y local, en que almacenamiento se asigna en la entrada a un
bloque o procedimiento, pero no es necesariamente liberado en salida-permanece asignado y otra
vez puede llegar a ser accesible a través de diversos mecanismos. Actualmente parece que es la
utilidad principal de la retención en la implementación de algoritmos de retroceso, pero todavía se
pueden encontrar aplicaciones más amplias. La forma definitiva de la asignación es otro familiar. la
asignación a petición explícita del programa de almacenamiento anónimo ("montón" allocatiori es
el término de ALGOL 68), con el programa de un seguimiento de un puntero. Un proceso
exactamente análogo se enciende, ligeramente menos visiblemente, cuando los tipos de datos
recursivos están en uso. Una forma de montón de asignación es una función estándar de muchos
lenguajes de programación (por ejemplo, 68 de ALGOL y PL / I).

Un punto relevante a la aplicación es que mientras que los programadores tienen la libertad de
solicitar asignación de montón almacenamiento de en cualquier momento, que probablemente no
debería también tener la libertad de hacer su desafectación. Esto resultará más ciertamente en
"punteros colgantes." Recuperación de almacenamiento de información que no está accesible
para el programa debe ser la responsabilidad del sistema de tiempo de ejecución, no el
programador. La última cuestión importante sobre estructuras de datos es el alcance de nombres.
El propósito original de restringir el alcance de nombres adentro ALGOL 60 fue en gran medida
facilitar la asignación de almacenamiento de información. Asignación de ámbito de aplicación y
almacenamiento están acoplados ya no tan fuertemente como lo fueron una vez. Sin embargo, las
restricciones en el alcance de nombres todavía están presentes para ayudar en la reducción de la
complejidad de los programas. Con el fin de mantener la complejidad de los programas y ~
egments de programas de alcance humano, es necesario restringir las interacciones entre los
diferentes segmentos.

Acceder a una variable dada es una de las más importantes interacciones entre las diferentes
partes de un programa, y para este ámbito de la razón las restricciones han continuado en
lenguajes de programación. Algunas observaciones deben hacerse, sin embargo, sobre la
naturaleza de las reglas de alcance que se utilizan. Una de las preguntas más fundamentales de la
organización de ámbitos es, ¿cuál es el área básico de las restricciones de alcance? La respuesta
más común es el originado por ALGOL 60·-the BEGIN-END hlock. Esto aparece. sin embargo, al ser
una estructura excesivamente general. Mul.tiple anidar bloques con las variables declaradas en
cada nivel comienzan a exceder la capacidad limitada de la mente humana para el manejo de
nidos. La alternativa base para las restricciones de alcance es el procedimiento. En lugar de
asignación de variables y control de su accesibilidad con bloques arbitrarios, se pueden realizar
dichos controles en el procedimiento de entrada y salida. Este enfoque ha estado en uso durante
mucho tiempo (después de haber sido introducido en FORTRAN) pero sin embargo, cuando se
combina con la asignación de local en lugar de estática, es bastante factible, como atestiguan su
uso moderno en PASe AL.

Puesto que es algo más simple que el control de alcance de BEGIN-END, puede ser recomendado.
Un aspecto algo más restrictivo de control de alcance es el que ha sido mencionado varias veces
en otras partes de esta sección. es decir, garantizar que una variable no es accesible por dos
nombres diferentes. Ha sido sugerido (Tennent, 1975) que el compilador debe, de hecho,
comprobar tales situaciones y considerarlos errores. En general, se trata de una idea con algún
mérito; en la práctica, podría convertirse en algo difícil cuando se consideran construcciones de
punteros. Incluso si no pueden señalar con punteros a las variables nombre, todavía se pueden
tener dos punteros apuntando a la misma pieza de almacenamiento anónimo. Tipos de datos
recursivos podrían aclarar algunos de estos problemas. Otro aspecto de alcance que es una
restricción que muchos compiladores hacer cumplir es el requisito que las entidades (variables,
procedimientos, etc.) deben ser declaradas antes de utilizarlos.

Para las variables y constantes no es demasiado desagradable. Puede ser conveniente establecer
algunas extensiones para permitir, por ejemplo, los tipos de datos recursivos o tipos de datos con
los indicadores que se refieren unos a otros. El mayor problema con la regla de declarar antes de
usar es para procedimientos. Es realmente conveniente para poder llamar a un procedimiento que
ocurre más adelante en el programa, incluso si uno no tiene la clásica situación de mutuamente
los procedimientos recursivos. Es una grave molestia tener que reordenar los procedimientos en
un programa para el compilador y no el programador.

Poco de esfuerzo en el compilador probablemente se justifica para evitar tener que introducir esta
restricción. Un punto final que es una cuestión de alcance, pero no a veces reconocido como tal es
la cuestión de los nombres de campo en un registro. En algunas idiomas, notablemente COBOL,
estos nombres son accesibles desde el exterior sin la calificación adicional. En muchas idiomas
modernas (por ejemplo, PASCAL), "a.x" debe escribirse "a.x" incluso si no hay ninguna otra"x" en
el programa. La elección del enfoque aquí no es evidente; el enfoque de COBOL puede implicar
ciertamente mucho menos escribir, pero el enfoque de PASCAL probablemente mejora la
legibilidad y ciertamente simplifica el compilador y la semántica de la lengua. Ciertamente, si se
toma el enfoque de COBOL, es vital que el compilador busque ambigüedad posible. no debe ser
posible tener dos interpretaciones diferentes del mismo nombre en el mismo lugar por
reorganizar las declaraciones.

3-5.4 control estructura

ha existido mucha controversia con respecto a la mejor opción para primitivas de estructura de
control. Gran parte del debate se centra en la inclusión o exclusión del GOTO. Casi universalmente
es aceptado que es mejor utilizar construcciones de alto nivel de control en lugar del GOTO sin
restricciones. Es también ampliamente aceptado que un conjunto adecuado de construcciones de
alto nivel de eliminaría la necesidad de y el deseo del GOTO. Lo que no se conviene así en es lo que
debería abarcar tal conjunto.
Es obviamente necesario ser capaz de agrupar varias instrucciones para la ejecución en secuencia.
Hay poca disputa de que la instrucción IF y algún tipo de lazo que termina en una condición
booleana son deseables. Casi como indiscutible es algún tipo de sentencia CASE para opciones
multiway y alguna forma de controlar una variable de índice del bucle FOR. La mayor área de
controversia se centra alrededor de "escape" construye y construye para el manejo de
excepcionales condiciones que a menudo. pero no siempre, son uno y el mismo. Claramente, esta
discusión tiene algunos elementos de mínima frente mínimo útil tema mencionado en la
consideración de la filosofía de diseño en 3-4 segundos. Nadie conflictos que construye un
programa que utiliza el escape pueden también escribirse sin ellos. La pregunta básica es si es
mucho más fácil de leer y escribir programas con tales construcciones disponibles-más
investigación es necesaria en esta área.

El resto de esta subsección discutirá varias construcciones en detalle. La estructura de control más
simple es la combinación de varias declaraciones en una sola sentencia, con declaración de BEGIN-
END de ALGOL. Idiomas sin tal construcción (FORTRAN, BASIC) sufren gravemente de su ausencia.
Teniendo en cuenta su conveniencia, hay tres métodos distintos para la obtención de tal
combinación: soportes explícitos, la sucesión de construcciones y sucesión por indentación.
Soportes explícitos son mejor caracterizados por el par de ALGOL 60 BEGIN-END (aquí considerado
sin la posibilidad de incluir declaraciones dentro de ella). COMENZAR... Es una opción; hay otros.
En particular, PL utiliza el... EXTREMO, que es posiblemente una variedad débil debido a la
similitud y la construcción de bucle, C (Ritchie, 1974) utiliza {...}.

Todos los esquemas explícitos sucesión desafortunadamente tienen un defecto. Tratan la casos
"una declaración de" y "varias declaraciones de" manera diferente (uniformidad de una violación
del""). A menudo es necesario, para la depuración y modificación. para poner varias declaraciones
donde antes sólo uno estaba presente. Cualquier esquema de sucesión de explícito requiere
soportes para insertarse cada vez que esto se hace, que es una molestia importante. Esta
dificultad se resuelve por el método de auto-bracketing-construcciones. en que cualquier control
construcción que contienen algunas declaraciones está organizado para tener puntuación
distintivo sucesión el punto donde se produzcan las declaraciones. Por ejemplo, se puede añadir
una palabra clave que un IF para que sea la sucesión:

IF expression THEN statements FI

or

IF expression THEN statements ELSE statements FI

Es comparativamente fácil de hacer que todo el conjunto de construcciones de control auto-


bracketing. eliminando soporte explícito. Las diferentes palabras clave terminar cada constructo
auto bracketing también añaden redundancia valiosa. Auto-bracketing con-estructuras han sido
utilizadas exitosamente en varios idiomas (Van Wijngaarden et aI., 1975; Clark, 1971; Wirth, 1982).
La posibilidad final, sucesión de indentación, utiliza la naturaleza bidimensional del programa para
hacer el compilador sabe que el otro en

IF x

THEN
IF Y

THEN

ELSE

pertenece al IF externo en lugar de a la una interna. Este enfoque, que ha estado alrededor
durante bastante tiempo, parece interesante pero contiene algunos posibles peligros. La pérdida
de las palabras clave que es una importante pérdida de redundancia. Esto puede causar problemas
si se añade una nueva línea con un nivel de sangría incorrecta o si requieren de revisiones a una
sección de un programa de cambios generalizados en el nivel de sangría. También, indentación no
es generalmente significativa en lenguajes naturales: programadores no están acostumbrados a
considerar como significativos. Cuando las declaraciones se agrupan, es obviamente necesario
separarlos unos de otros. La Convención casi universalmente aceptada es utilizar el punto y coma
";".

Un tema menor del debate es si el punto y coma debe separar declaraciones ("BEGIN...; ... ; ...
FINAL") o les ("BEGIN...; ... ; ...: FINAL "). Usando como separador es tal vez más elegante y a veces
puede ser más fácil de analizar, pero las medidas reales (Gannon, 1975) muestran que la forma de
terminación-punto y coma es mucho menos propenso a errores. El constructo de control más
importante siguiente es la instrucción IF, que ofrece dos vías de acción. En esta elección, es muy
posible que una de las dos acciones es nula, lo que resulta en un familiar

IF boolean

THEN

statements

FI (the FI together with THEN serves to bracket the statement sequence). However, the second
action is not always null, which leads to the equally familiar

IF boolean

THEN

statements

ELSE

statements

FI

que proporciona para ambas acciones. Mediciones por Alexander (1972) de la frecuencia relativa
de estas dos formas revela que aproximadamente el 35 por ciento de IFs tienen un ELSE. Este
número es demasiado pequeño para justificar que requiere el otro; por lo que la IF debe aceptarse
como teniendo ambas formas. Un tema \\"111(.:h is relevant to several constructs, including the
IF, is the question"what is a Boolean expression?" Hasta la fecha, esta pregunta ha sido respondida
en al menos tres maneras diferentes: 1. "Booleana" es un tipo de datos independientes, con
constantes "TRUE" y "FALSE". Ciertos operadores, como los operadores de comparación,
resultados booleanos. 2.

"Boolean" equivale a "entero." True y false son distinto de cero y cero, respectivamente.
Rendimiento de los operadores de comparación 1 u o 3. "Boolean" equivale a "entero." True y
false son pares e impares, respectivamente (es decir, se distinguen por el bit de orden inferior del
entero). Operadores de comparación rendimiento 1 u O. De estos tres, el primero es preferible. La
dificultad con el segundo es que los programadores "inteligentes" a menudo usa su conocimiento
de cómo se evalúa la expresión booleana para escribir "IF x" donde debe escribir "IF x = 1." La
resultante pérdida de legibilidad es considerable. Objeciones similares se aplican a la tercera
alternativa.

Otro problema con los métodos de segundo y la terceros es que la confusión de "Boolean" y
"entero" interfiere con la comprobación de errores. Una generalización natural de la IF es de una
opción de dos vías a una opción de n-way. A menudo esta elección se hace en base al valor de una
variable de tipo entero. Se han propuesto varias formas de la construcción del caso, y varios temas
son de importancia. Primero, muchos mayores construcciones caso dependen el orden textual de
las alternativas para decidir a qué caso corresponde a que alternativa:

CASE choice-expression

OF

ESAC

statementsl OR

statements2 OR

(Tenga en cuenta que "o" se utiliza como un delimitador, no como el operador "o") Orden textual
lamentablemente abre posibilidades de error si un caso es olvidado o extraviado. Un esquema
mucho mejor es uno en que cada caso se etiqueta con los valores que la causan a elegir:

CASE choice-expression : 1 TO 10

OF

1: statements OR

2,3: statements OR

4 THRU 7: statements OR

10: statements

ESAC

Es generalmente deseable para especificar el rango de valores posibles de la elección. partl\' fllr
una implementación más fácil y en parte para brindar redundancia valiosa. Se ciefinitely i ~ útil ser
capaz de especificar la misma acción para más alld de valor a él capaz de dar un rango de valores
en lugar de escribirlos todos. Aras de la legibilidad es altamente deseable que los valores utilizados
en la lahels se limite a simples constantes.

Tenga en cuenta también que con este esquema no es realmente ninguna razón para restringir el
tipo de elección-expresión para ser "entero"; sería igualmente válida para tomar una decisión
sobre la base de un valor de tipo "carácter" o la enumeración, por ejemplo. Implementación
eficiente debe decidir qué limitaciones deben colocarse sobre el tipo de la opción expressioh. Otra
cuestión que es relevante es la cuestión de la aplicación precisa de tal caso. Por lo menos dos
alternativas sugieren ellos mismos: o bien utilizar el valor de la expresión de elección al índice en
una tabla de direcciones de la acción. o evaluar la expresión de la opción almacenar su valor y
comparar sucesivamente con cada etiqueta de valor.

Cada esquema tiene sus ventajas. Indexación es más rápido, mientras que (si las gamas se pueden
utilizar como etiquetas) control secuencial puede reducir noticeahl\ de requisitos de
almacenamiento '. Puede ser que vale la pena dar al programador una opción o permiten la
coll1piler elegir basado en una fórmula de optimización. Una pregunta natural que surge es ¿qué
pasa si el valor de la expresión de elección hace no correspolld a ninguna de las lahels valor? ¡Esto
definitivamente no debería causar un salto al azar a algún enfermo el código! La estrategia
habitual aquí es incluir una cláusula ELSE se ejecuta si ninguno de las alternativas seleccionadas:

CASE ....

ELSE

statements

También se ha sugerido (Weinberg et aI., 1975), con cierta justificación, que sería útil disponer de
una declaraciones de col1tainll1g cláusula (tal vez un "También") que serían ejecutados si
cualquiera de las alternativas marcadas fue ejecutado. Por ejemplo, esto proporcionaría una
manera simple de manejar asuntos como establecer una bandera de "actuar" o eliminación de un
elemento de datos procesados con éxito. A veces se sugiere que la sintaxis de IF debe combinar (l
con eso de la caja, ya que el si es realmente un caso especial del caso. Un argumento sólido, sin
embargo, es que la inmensa mayoría de las construcciones de elección utilizado IFs (Alejandro,
1972), y que la popularidad de la IF (particularmente con ninguna cláusula ELSE) justifica el sintaxis
especial para mejorar la legibilidad. ESAC

Es una construcción de caso más generalizada que ha sido propuesta por Dijkstra (1975) y
Weinberg et al (1975) de la forma CASE

ESAC

Boolean: statements OR

Boolean: statements OR

De esta forma, se evalúa cada uno de los "booleanos"; Cuando una de ellas es verdadera, se
ejecuta la correspondiente sección de "declaraciones". No está claro lo que debía suceder si dos o
más de los "booleanos" ambos suceden ser verdad: posiblemente debe usarse el primero de ellos
es verdadero. Esto puede proporcionar una forma más general útil de la comprobación secuencial
mencionado anteriormente como una aplicación plausible de lo habitual. Esta forma aparece
potencialmente muy útil, especialmente si está equipado con un otro y una cláusula también.

El papel de Weinberg también tiene algunas otras ideas interesantes en las estructuras de
elección. Otro constructo mayor control es el lazo. Una gran mayoría de sus usos se contabilizan
por el simple caso en el cual terminación ocurre sobre la base de una condición booleana. Uno de
los problemas es donde se debe comprobar la condición de terminación: al principio de cada
iteración, en el extremo, o incluso en el medio. Los tres casos son útiles. Por lejos la mejor solución
es la propuesta por Dahl (en Knuth, 1974) en la que el cheque es potencialmente en el medio:

LOOP

statements

UNTIL Boolean:

statements

REPEAT

Ninguno de los grupos de Estados puede ser null. Esta construcción abarca las tres formas.
También, en general, elimina la necesidad de tener construcciones especiales que terminan la
ejecución del bucle o reiniciar la iteración (escrito a menudo "salida" y "Ciclo," respectivamente).
Una cuestión menor en bucles de condición booleana es terminar la repetición en la condición
booleana ser verdadero ("hasta") o ser falso ("tiempo").

La opción tradicional es mientras. La escuela mientras que afirma que hasta el que centra la
atención en la terminación del bucle mientras que el tiempo centra donde pertenece, a saber, la
naturaleza de la repetición. La escuela hasta que afirma que el tiempo centra en la mecánica de la
repetición mientras que el hasta centra donde pertenece, es decir, con la condición (booleana)
que el bucle está trabajando. La otra forma notable de lazo es el iterativo para lazo, que altera el
valor de una variable de índice de algún modo predefinido en cada iteración. Aunque el bucle FOR
podría construirse por el usuario desde un bucle de Boolean-condición ordinario, este proceso de
construcción es complicado y propenso a errores, y por lo tanto es una buena idea tener un built-
in para el lazo.

Para lograr legibilidad razonable y fiabilidad, un bucle FOR tiene algunas restricciones bastante
extensas. Modificación de la variable de índice no se debe permitir dentro del bucle; el compilador
debe hacer cumplir esta restricción. Los valores utilizados para definir los parámetros de la
alteración de la variable de índice deben ser evaluados en la entrada al bucle y no deberían ser
alterados después de eso. No está claro a lo que el valor de la variable de índice debe estar en
salida del loop; una solución sencilla es decir que el mismo constituye la declaración de la variable
índice y que es accesible sólo dentro del bucle la variable de índice. Esto también alivia el
programador del fastidio de tener que declarar la variable de índice. El problema que queda es la
naturaleza de la variación de la variable índice.

El enfoque tradicional es especificar un valor inicial, valor final y el incremento. Muchos


diseñadores (Hoare, 1972) tienden a considerar este había y detrimento de la legibilidad, sobre
todo cuando se aplica a números de punto flotante. El enfoque adoptado por PASCAL es limitar el
incremento del bucle 1 o -1 dando los límites del bucle como "x a y" o "x p y" y prohibir las
variables índice de coma flotante. Por Alejandro (1972) se ha observado que esto cubre la gran
mayoría de los casos. Tenga en cuenta que no hay ninguna razón particular por la variable índice
no debe ser, por ejemplo, de tipo "carácter" en lugar de "entero." Consideremos el ejemplo:

FORi FROM 'a' TO 'x'

DO

END

(Nótese el uso de "De" donde algunas formas de utilizan el operador de asignación; "Desde" es
probablemente más clara). Es deseable excluir números de punto flotante, sin embargo; las
peculiaridades de la aritmética floating-point complican demasiado las cosas y así interfieran en la
legibilidad. En idiomas con el "conjunto" como un tipo de datos básico. puede ser conveniente
tener una para que simplemente las secuencias a través de los miembros de un conjunto
proporcionado por el programador:

FOR x IN a_set

DO

END

Aquí la variable Índice x simplemente asume a su vez el valor de cada miembro del conjunto. ¿En
qué orden debe asumir la variable de índice los distintos valores? Quizá la solución menos ofensiva
es decir que el orden es definido por la aplicación y no debe ser dependía en programas. Aunque
el apPlroach de PASCAL para el bucle FOR se puede recomendar, diseñadores que deseen
experimentar tienen varias opciones. El bucle para 109 diseño de lenguaje de programación
basada en sistema discutido previamente es útil en una lengua que maneja conjuntos bien. Otra
propuesta, que ha sido experimentado por Gannon (1975), es un bucle FOR de la forma:

FOR x IN arrayname

DO

END

donde en cada iteración, la variable x del índice se refiere a un elemento de la matriz (es decir,
cualquier referencia a x se refiere a ese elemento de la matriz). Este bucle tiene la ventaja decidió
que anidado bucles no son necesarios para trabajar con matrices multidimensionales. Su
limitación es los bucles no son siempre para el manejo de la matriz. El procedimiento es otro
constructo de control fundamentales. El procedimiento es una de las herramientas más
importantes para romper grandes problemas en pequeños trozos, y esto tiene implicaciones para
su diseño. En particular, la interfaz de llamada de procedimiento debe ser eficiente y debe ser
posible comprobar los tipos de parámetros y resultados completamente.

Uno de los aspectos menos estandarizadas de procedimientos es el asunto del parámetro que se
pasa, aunque ahora parece que dos tipos de parámetros se utilizan más a menudo. El primero es el
parámetro de paso por valor, donde se evalúa una expresión y el resultado está a su disposición
como parámetro. El otro tipo útil de parámetro es el parámetro de pasada como variable (también
llamado paso por referencia), en el que una variable se pasa en modificarse. Esto puede
implementarse pasando un puntero o copiando en el valor inicial y copia el valor final.

Una menor importancia de la nota es que la sintaxis de paso por valor y paso como variable debe
ser distinta, por lo que el programador sabe qué variables pueden ser modificados por un
procedimiento. Tal vez debería decir: procedure_name (x, y, z AR V) pasar x y y como valores y z
como una variable. La utilidad de procedimientos propios que pasan como parámetros es
discutible para algunos tipos de idiomas. Si esta característica es rara vez se utiliza el diseñador de
lenguaje probablemente justificado en omitir, ya que puede ser simulado con no demasiada
dificultad, es difícil de implementar y hace que sea difícil para completar comprobación de errores
de tiempo de ejecución. Los mismos comentarios se aplican a los parámetros de paso-como-if-hy-
macrosubstitution de ALGOL 60.

El otro lado de la cuestión del paso de parámetro es the·matter de devolver un valor desde un
procedimiento. Algunas idiomas hacen una distinción entre las funciones, que devuelven valores y
procedimientos, que no. Tal distinción es probablemente más apropiada cuando; como en una
versión temprana de PASCAL, no se permiten funciones con efectos secundarios (es decir..
modificar parámetros o variables globales). Sin duda corresponde bien a la idea intuitiva del
programador de "hacer algo" versus "que rinde un valor." Sin embargo, cabe señalar que es muy
difícil hacer cumplir una regla de no efectos secundarios.

El método real de devolver un valor desde un procedimiento de valor de retorno es un problema


para los que se han encontrado varias soluciones: 1. una construcción explícita de retorno se
utiliza con disposiciones para virar en un valor, por ejemplo, "RETURN(x)." 2. un valor es devuelto
asignando el valor en el nombre del procedimiento como si fuera una variable. 3. en un lenguaje
de expresión (véase Sec. 3-5.2), de cuerpo ejecutable del procedimiento como un todo ha vuelto
un valor, que es el. Las desventajas de lenguaje de expresión de un estrecho esto hasta la primera
o la segunda alternativa. La segunda alternativa es menos atractiva, porque implica el uso del
nombre de procedimiento de una manera algo extraña, pero tiene la ventaja de que no es junto a
una construcción de control de retorno.

Un diseñador que no quiere devolver desde procedimientos excepto por "que fluye del extremo"
probablemente terminará por adopción 2 alternativas; mayoría de los otros diseñadores
probablemente optarán por la comodidad y la limpieza de la alternativa 1. Como una última
cuestión de la sintaxis debemos abordar la cuestión de lo que debería ser una llamada a un
procedimiento. ALGOL y sus descendientes simplemente usan el nombre del procedimiento como
una declaración. Esto parece el enfoque más sencillo para situaciones que impliquen un valor
devuelto. Por desgracia, hace llamadas de procedimiento puro ve como accesos variable, que es
probablemente indeseable, ya que los procedimientos no devuelven valores. Por esta razón,
algunos diseñadores de la lengua han introducido una palabra clave especial (por ejemplo, llamar)
para delinear claramente tqe dos tipos de invocaciones de procedimiento.

Ninguna consideración de procedimientos debe pasar por alto la cuestión de si debería aplicarse
como subrutinas o macros. Aunque es habitual la ejecución de la subrutina, se ha sugerido que el
programador debe ser capaz de decidir qué método utilizar, independientemente de los
contenidos del procedimiento sí mismo. Al diseñar un lenguaje para la implementación de
software de sistema, u otras tareas donde la velocidad de ejecución puede ser crítico, esta
flexibilidad puede ser beneficiosa. Sin embargo, cabe señalar que hay problemas en asegurar que
el procedimiento considera el medio ambiente de cualquier manera.

Se han realizado algunos estudios (Wulf, 1972b) de la cuestión más general de especificar la
interfaz a un procedimiento (es decir, convenciones de llamada) de una manera
languageindependent. Este simplificar la vinculación entre diferentes lenguas, permiten
optimización de secuencias de acoplamiento para situaciones críticas y simplificar el manejo de
cosas tales como listas de parámetros de longitud variable. La cuestión es, sin embargo, es
bastante complejo porque las dependencias de la máquina del arrastramiento; sin embargo ha
aparecido ninguna solución excelente. Las construcciones de control más controvertidas
actualmente son las construcciones de "escape", que permiten salidas desde el concepto de "solo
entrada, solo salida" de las estructuras de control. El retorno mencionado anteriormente con
respecto a procedimientos es un escape, y algunas lenguas no lo tienen. Generalmente, el retorno
es suficientemente útil y su funcionamiento es suficientemente claro que pocas personas se
oponían a él.

Otro muy restringido pero construcción de escape muy valiosa es la instrucción de parada, que se
utiliza para terminar la ejecución del programa. PARADA es inestimable para hacer frente a
situaciones de error; debería estar disponible en cualquier idioma que funciona bajo un sistema
operativo (es decir, que puede asociar una acción significativa parada). De las construcciones más
controversiales, prohably el mejor es de Zuhn situationdriven, publicitado por Knuth (1974). Un
ejemplo es aproximadamente como sigue (con algunas alteraciones en la sintaxis original):

donde el cuerpo de la construcción ("buscar algo") puede contener cualquier número de


declaraciones del formulario

La ejecución de una instrucción de señal C; 1Uses ejecución inmediata de las correspondientes


"instrucciones" después de la causa y, a continuación, ternllnation de la construcción entera. El
uso de la situación los nombres "encontrado" y... noLfound"en este ejemplo ilustra el punto
principal de la construcción. El bucle de búsqueda puede terminar de dos maneras, cada uno que
requiere una acción diferente, y ninguno de los dos está realmente "anormal" en comparación con
el otro. El principal valor de esta particular construcción se encuentra en la separación de la causa
de la salida (terminación de la búsqueda) de las acciones tomadas como consecuencia de ello, tal
modo renderizado más legible el código de búsqueda y el código de acción.

Se ha hecho algún trabajo en particular por Brinch Hansen (1973, 1974, 1975). en estructuras de
control para la gestión de procesos paralelos. Esta área está madurando y se está convirtiendo en
cada vez más importante tener estas características en un lenguaje, al menos para algunas áreas
de trabajo tales como software de comunicaciones. Un área importante de las estructuras de
control que no ha sido completamente investigado es la cuestión del manejo de excepciones.
¿Cómo funciona un construir estructuras de control para hacer frente a informar y tratar las
condiciones de error, ya sea detectada por el hardware, los controles de tiempo de ejecución o el
programa de usuario? Existe un número de preguntas abiertas, y sin duda se realizará más
investigación en esta área. Un aspecto menor de estructuras de control es la pregunta: ¿donde
comienza el programa cxecution?

Es un enfoque bueno y legible tomado en PL / I en el que la ejecución comienza con una llamada a
un procedimiento llamado "principal". Esto también proporciona un medio elegante de pasar
parámetros a un programa como parámetros "principal." C (Ritchie, 1974) proporciona un tipo de
construcción similar. Una estructura útil que no ha aceptado ampliamente es el coroutine.
Coroutines puede considerarse como una generalización de los procedimientos o una simulación
especializada, altamente eficiente de los procesos de paralIel. La idea es que un coroutine activo
puede pasar control a otro coroutine activo. Cuando devuelve el control a un coroutine
anteriormente invocado, este coroutine lleva en donde se quedó, reasumir la ejecución
inmediatamente después de la construcción de control de cambios como resultado de la última
transferencia de control.

Coroutines son particularmente útiles para las situaciones donde una parte de un programa
"alimenta" otra parte un flujo de datos; muchos programas se pueden caracterizar de esta
manera. Una manera fácil de implementar un programa es mediante procesos paralelos;
Lamentablemente, esto es prohibitivamente ineficiente en la mayoría de los sistemas. Coroutines
pueden hacerse casi tan eficientes como llamadas a procedimientos. No está claro si el número de
coroutines activadas debe ser constante en tiempo de ejecución (es decir, un coroutine es una
clase especial de procedimiento) o variable en tiempo de ejecución (es decir, un coroutine es una
clase especial de invocación de procedimiento).

Parece que la primera alternativa maneja más req ui remen ts. Dos construcciones muy útiles, que
no pueden pertenecer estrictamente bajo "estructuras de control" pero que no claramente
pertenecen en cualquier otra cosa, son las afirmaciones y las relaciones de invariancia. Estas
construcciones basicalIy ofrecer al programador una oportunidad para (booleanas) condiciones
que deben cumplirse en un punto en el programa (afirmaciones) o por todas partes en el
programa (relaciones de invariancia) del estado. Estas nociones no se han aplicado a lenguajes
utilizados para la producción de programación principalmente debido a la dificultad en probar
grandes programas correctos. Otra vez, esto puede cambiar como producto de la investigación en
esta área y sin duda el desarrollo de EUCLID (Lampson et aI., 1977) es un paso en esta dirección.
Para más información en esta área vale la pena leer el informe sobre Euclides.

Aunque las afirmaciones y las relaciones de invariancia se tratan simplemente como otra forma de
comentario, son valiosos. También es posible (Clark 1971) para que el compilador genere código
para comprobar en tiempo de ejecución que las condiciones son verdaderas. Esta característica
puede ayudar a verificación considerablemente. Por último, existe la posibilidad de poder aplicar
las técnicas de prueba de teorema automatizadas a tales afirmaciones; Esto simplificaría
enormemente escribir programas correctos.

Un problema con este enfoque es que los lenguajes de programación actuales no suelen ser en un
alto suficiente nivel para expresar afirmaciones complejas fácilmente. Una serie de construcciones
de control ha sido propo ~ ed para operaciones no deterministas y "retroceso" (desentrañar los
efectos del código anterior para volver a intentarlo), como se discute en Prenner et al (1972) y
Bobrow y Rafael (1974). Estas construcciones son particularmente aplicables en inteligencia
artificial. Intencionalmente hemos ignorado la construcción ir porque la experiencia actual indica
que con un decente sistema de control de construcciones, el GOTO es simplemente innecesario
(Wulf, 1972a). Un común pensamiento entre los partidarios del GOTO es: "quizás necesito; algo
podría venir ". La respuesta parece ser: "Nada nunca lo hace." No puede enfatizarse demasiado
fuertemente que esta declaración se aplica sólo si se proporciona un buen conjunto de
construcciones de no ir. Muchos idiomas son algo deficientes en esta materia. Las siguientes
construcciones de control son claros fracasos o intentos primitivos haciendo lo que ha hecho
mejor y más simplemente: 1. variables de la etiqueta. 2. modificación de la instrucción dinámica
(por ejemplo, ALTER de COBOL). 3. el excesivamente restrictivas FORTRAN-loop. El diseñador
aconseja bien a ignorar a estos.

3-5.5 Compile Structure

"Estructura de compilación" es el término usado aquí para cubrir ciertos aspectos de una lengua
que atan muy fuertemente con el proceso de compilación. Los temas principales de interés son las
directivas para el compilador, las operaciones dentro de la lengua que se realizan en tiempo de
compilación en lugar de en tiempo de ejecución y la cuestión de la compilación separada de
diferentes "módulos" de un programa. A menudo es deseable dar el compilador ciertos elementos
de información que no puede expresarse fácilmente en la lengua sí mismo. El enfoque más común
a esto es tener una forma especial de comentario (el término "pragmática" del ALGOL 68 parece
ser la palabra más adecuada para él) que no tiene ninguna importancia a la lengua sí mismo pero
que contiene directivas para el compilador, en lugar de texto comentario normal.

Un método para distinguir pragmats de comentario normal es que comience con el delimitador de
comentario normal, que es seguido por una secuencia de caracteres. Esta secuencia debe ser
extremadamente improbable en el texto del comentario. Por ejemplo:

/ / This is a comment.

/ /:: This is a pragmat.

El contenido real de una pragmática dependen, por supuesto, compilador; una pragmática puede
contener ajustes de opciones, consejos de optimización, las especificaciones de formato de lista y
así sucesivamente. Otra, quizá mejor, manera de manejar pragmats es definirlos en la estructura
de la frase de la lengua para que el analizador puede analizarles y construir árboles para ellos si es
necesario. Idiomas varían grandemente en la cantidad de manipulación posible en tiempo de
compilación. Es notable que rara vez se utiliza la mayoría de las instalaciones para operaciones de
tiempo de compilación; uno o dos constructos específicos parecen llenar necesidades más.
El más notable tal facilidad es la capacidad de r ~ quest que el compilador insertar el contenido de
otro archivo de código fuente en un punto específico en su secuencia de entrada (PL / I «%
incluyen» instalación). Una lengua debe tener esta característica o un servicio equivalente. Una
pragmática podría ser utilizado para especificar la inclusión.

El otro gran tiempo de compilación es compilación condicional ~ ahility ignorar selectivas partes de
la entrada de texto en base a condiciones de compilación tllne. Si bien hay varias maneras de
hacer esto, el método utilizado hy ALGOL 68 (Van Wijngaarden et al., 1975) es muy notable, ya
que no requiere la introducción de otra construcción en el lenguaje. El ALGOL 68 es simplemente
para comprobar las expresiones de selección de IFs y casos y ee si pueden evaluadas en
compilación tllne. Si puede, sólo uno de la declaración de alternati\e secuencias necesitan ser
compilados (nota que es todavía accesible para verificación de error los otros).

Este enfoque es simple de implementar y funciona bien. La cuestión de la compilación separada ha


generado un considerable debate. Su conveniencia no está en duda, como quien nunca ha
implementado un sistema de software grande testificará a su valor. El hecho de que casi todos los
idiomas systcmsimplementation incluyen a algún tipo de evidencia adicional de esta facilidad de
compilación separada. La principal dificultad es el pobre soporte para compilación separada en
sistemas actuales. Es vital para un buen sistema de separado-compilación que existir alguna forma
de verificar la exactitud (por ejemplo, que los tipos de datos) de las llamadas a módulos
compilados por separado. Por desgracia, editores de vinculación más recientes (las herramientas
generalmente para unir módulos) no incluyen instalaciones para tal comprobación.

El resultado de cajas es que escritores compilador generalmente deben implementar su propio


sistema de acoplamiento. que no necesariamente es un programa independiente. Una pregunta
importante de diseño del lenguaje es: ¿Cuál debe ser la forma del "módulo" compilable por
separado? La respuesta tradicional es "un procedimiento"; por desgracia, esto hace Iwt satisfacen
los requisitos frecuentes para tener los elementos de datos "estáticos" o "propio" como:-.ociated
con un procedimiento o un grupo de varios procedimientos. El mejor enfoque es, probablemente,
uno en el que un módulo contiene procedimientos y elementos de datos de hoth. Puede ser
deseable limitar el alcance de los elementos de datos para el módulo en sí, por lo que se puede
acceder desde fuera a través de los procedimientos. Tal arreglo proporciona una forma cruda de
las instalaciones de abstracción de datos mencionadas anteriormente.

Si el lenguaje contiene una de las más complejas técnicas de abstracción de datos, esta técnica
puede utilizarse como base para la definición de "módulo". ADA es compatible con muchas de
estas ideas y un debate de estas características se da en segundos 3-8. Es conveniente especificar
las posibles interacciones entre módulos en lugar de adoptar una política de "todo vale"; algunos
trabajos se ha realizado sobre este (DeRemer, 1976,) ' especialmente como estos conceptos se
refieren a la ejecución de las instalaciones de la abstracción de datos.

También podría gustarte