Page 1

Elementos de Programaci´on. ETSIT. 1o C, Apuntes del profesor Juan Falguerasa 2001/02 19 de enero de 2003 a Estos apuntes no constituyen ning´ un compromiso sobre el contenido de la asignatura

3 Descripci´ on de algoritmos Contenido 3. Descripci´ on de algoritmos 3.1. Gram´ aticas de los lenguajes de programaci´on . . . . . . . . 3.1.1. Componentes de un lenguaje formal . . . . . . . . . 3.1.2. Gram´ aticas. Jerarqu´ıa de Chomsky . . . . . . . . . . 3.1.3. La jerarqu´ıa de Chomsky de los Lenguajes Formales 3.1.4. Propiedades de las gram´ aticas . . . . . . . . . . . . . 3.1.5. Formas normales . . . . . . . . . . . . . . . . . . . . 3.1.6. Diagramas sint´ acticos . . . . . . . . . . . . . . . . . 3.2. Tipos de lenguajes de programaci´on: lenguajes imperativos 3.2.1. Paradigmas de los lenguajes . . . . . . . . . . . . . . 3.2.2. El paradigma imperativo . . . . . . . . . . . . . . . 3.2.3. El paradigma declarativo . . . . . . . . . . . . . . . 3.2.4. Historia de los lenguajes de programaci´on . . . . . . 3.2.5. El papel de los lenguajes de programaci´on . . . . . . 3.2.6. Cualidades de los lenguajes . . . . . . . . . . . . . . 3.2.7. Dominios de las aplicaciones . . . . . . . . . . . . . 3.3. El teorema de las estructuras . . . . . . . . . . . . . . . . . 3.4. Las estructuras fundamentales de control de flujo . . . . . . 3.4.1. Secuencia . . . . . . . . . . . . . . . . . . . . . . . . 3.4.2. Selecci´ on . . . . . . . . . . . . . . . . . . . . . . . . 3.4.3. Iteraci´ on . . . . . . . . . . . . . . . . . . . . . . . . . 3.5. Pseudolenguaje (v. C1.0.1) . . . . . . . . . . . . . . . . . . 3.5.1. ALGORITMO . . . . . . . . . . . . . . . . . . . . . 3.5.2. DECLARACIONES . . . . . . . . . . . . . . . . . . 3.5.3. TIPOS . . . . . . . . . . . . . . . . . . . . . . . . . . 3.5.4. SUBALGORITMOS . . . . . . . . . . . . . . . . . . 3.5.5. ACCIONES . . . . . . . . . . . . . . . . . . . . . . . 3.5.6. Prioridad de operadores . . . . . . . . . . . . . . . . 3.5.7. Acciones . . . . . . . . . . . . . . . . . . . . . . . . . 3.5.8. Ejemplos de modificaci´ on de variables . . . . . . . . 3.6. Diagramas de Control de Flujo . . . . . . . . . . . . . . . . 3.7. Nociones sobre reconocimiento de lenguajes . . . . . . . . . 3.8. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.8.1. Referencias de consulta . . . . . . . . . . . . . . . .

3.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Descripci´ on de algoritmos En los primeros temas se ha hecho una presentaci´ on hist´ orica/social, por un lado, cient´ıfica, por el otro de la inform´ atica. Socialmente los ordenadores han tenido una influencia, han evolucionado m´ as o menos r´ apidamente y han influido en otros terrenos, cient´ıficos o no.

1 2 2 3 5 6 6 7 7 8 8 9 10 12 13 14 14 15 15 16 17 18 18 18 19 20 20 21 21 21 21 22 25 26


3.1

Gram´ aticas de los lenguajes de programaci´ on

2

En el cap´Ĺtulo anterior, se abord´ o el apasionante tema de Âżqu´e es capaz de resolver, cu´ ales son los “l´Ĺmites intelectivosâ€? de la computaci´ on resolviendo problemas? As´Ĺ, vez hecha una introducci´ on externa, social, hist´ orica y f´Ĺsica de los computadores, hemos visto en el segundo tema hasta qu´e problemas podemos llegar a abordar con los computadores; con qu´e eficiencia y un modelo muy simple de m´ aquina de computar, la m´ aquina de Turing. En este tema, conocidos ya los l´Ĺmites de la computabilidad, el ordenador en sus principales caracter´Ĺsticas, nos introduciremos en la teor´Ĺa de los lenguajes, medios para comunicarnos con las m´ aquinas; y los tipos de lenguajes. Veremos inicialmente las grandes l´Ĺneas en los lenguajes de programaci´ on, lenguajes imperativos y declarativos, para tomar inmediatamente el camino de los primeros y dejar los u ´ltimos. Presentaremos un sencillo teorema que nos permitir´ a plantear definitivamente las necesidades de cualquier lenguaje que describa cualquier algoritmo. Veremos descritos algoritmos elementales mediante un lenguaje directo convenido, el pseudolenguaje, que nos mantendr´ a independientes de cualquier lenguaje o dialecto concretos de programaci´ on.

3.1.

Gram´ aticas de los lenguajes de programaci´ on

Los lenguajes se rigen por sus gram´ aticas. Dentro de este contexto, la Gram´atica Formal es una herramienta que permite analizar y manipular la estructura de “conversaci´on�. Una Gram´atica Formal especifica la estructura de un Lenguaje mediante la construcci´on de una colecci´on de reglas que pueden usarse sistem´ aticamente para generar todas y cada una de las posibles comunicaciones legales entre los participantes. 3.1.1.

Componentes de un lenguaje formal

Definici´ on 3.1 Un alfabeto es un conjunto finito no vac´Ĺo de s´Ĺmbolos indivisibles. Por ejemplo, el alfabeto anglosaj´ on consiste en 26 letras may´ usculas y 26 min´ usculas. El castellano, de 28 de cada. Usualmente se denota un alfabeto mediante T . Definici´ on 3.2 Una cadena ( string) sobre un alfabeto T es una secuencia finita de s´Ĺmbolos de T. El n´ umero de s´Ĺmbolos de una cadena x es llamado su longitud, y se denota mediante |x|. Es conveniente la introducci´ on de la cadena vac´Ĺa, denotada , que no contendr´a absolutamente ning´ un s´Ĺmbolo. La longitud de  es 0. Definici´ on 3.3 Sean x = a1 a2 . . . an e y = b1 b2 . . . bm dos cadenas. La concatenaci´on de x e y, denotada por xy, es la cadena x = a1 a2 . . . an b1 b2 . . . bn . As´Ĺ, para una cadena x cualquiera, x = x = x. Para cualquier cadena x y n entero n ≼ 0, diremos que xn es la cadena formada por la concatenaci´on de x n veces. Definici´ on 3.4 El conjunto de todas las cadenas sobre un alfabeto T se denota T ∗ y el conjunto de todas las subcadenas no vac´Ĺas sobre T se denota T + . El conjunto vac´Ĺo de cadenas se denota ∅. Definici´ on 3.5 Para cualquier alfabeto T , un lenguaje sobre T es un conjunto de cadenas sobre T . Los miembros de un lenguaje se llaman tambi´en palabras del lenguaje. Los conjuntos L1 = {01, 11, 0110} y L2 = {0n 1n | n ≼ 0} son dos lenguajes sobre el alfabeto binario {0, 1}. La cadena 01 est´ a en ambos lenguajes, mientras que la 11 est´a en L1 pero no en L2 . Dado que los lenguajes son sencillamente conjuntos, las operaciones est´andard sobre conjuntos tales como uni´ on âˆŞ, intersecci´ on ∊ y complemento A tambi´en se aplican a los lenguajes. Para los lenguajes es u ´til tambi´en el introducir dos operaciones m´as: concatenaci´ on y cierre de Kleene. Definici´ on 3.6 Sean L1 y L2 dos lenguajes sobre T . La concatenaci´ on de L1 y L2 , denotada como L1 L2 es el lenguaje {xy | x ∈ L1 , y ∈ L2 }.


3.1

3

Gram´ aticas de los lenguajes de programaci´ on

Definici´ on 3.7 Sea L un lenguaje sobre T . Definimos L0 = {} y Li = LLi−1 para i ≼ 1. El cierre de Kleene de L, denotado como L∗ , es el lenguaje: [ Li L∗ = i≼0

y el cierre positivo de Kleene, denotado como L+ , como el lenguaje: [ L+ = Li i≼1

En otras palabras, el cierre de Kleene de un lenguaje L consite en todas las cadenas que pueden ser formadas mediante la concatenaci´on de palabras de L. Por ejemplo, si L = {0, 01}. entonces LL = {00, 001, 010, 0101} y L∗ incluye todas las cadenas de d´Ĺgitos binarios en las cuales cada 1 est´a precedido de un 0. L∗ es la misma que L+ excepto que L+ excluye . N´otese que, para cualquier lenguaje L, L∗ siempre contiene  y L+ contiene  si y s´olo si L lo contiene. N´otese adem´as que T ∗ es en efecto el cierre de Kleene del alfabeto T cuando es visto como lenguage de palabras de longitud 1, y T + no es otro que el cierre positivo de T . 3.1.2.

Gram´ aticas. Jerarqu´Ĺa de Chomsky

Los componentes de una Gram´ atica Formal son s´Ĺmbolos y reglas. Las gram´aticas contienen dos tipos b´asicos de s´Ĺmbolos: terminales , que habr´ a uno asignado a cada Palabra del Lenguaje; no terminales , que podr´Ĺan entenderse como las plantillas para las Frases del lenguaje. Cada plantilla admite usualmente n´ umero muy alto de frases concretas. Cada s´Ĺmbolo terminal expresa la forma que habr´ an de tener las frases. Hay adem´as un s´Ĺmbolo especial reservado, el s´Ĺmbolo inicial I. En resumen, aunque todo lenguaje est´a basado en un vocabulario, en el terreno de la Gram´atica Formal, sus elementos no se llamar´ an normalmente palabras sino s´Ĺmbolos (b´asicos). Por otro lado a las secuencias de s´Ĺmbolos del lenguaje se le llamar´an frases y ser´an correctas o incorrectas seg´ un est´en bien o mal formadas dentro de la gram´atica, sintaxis o estructura del lenguaje. La Gram´ atica Formal no s´ olo permite decidir si una cierta secuencia de palabras es una frase de ese lenguaje, sino que, tambi´en, algo que es m´as importante, dotan a la frase de una estructura que ayuda a encontrar su significado, ya que cada frase imprime un contexto a sus palabras. Al significado de las frases se le denomina sem´ antica y est´a, naturalmente, ligado a la sintaxis. Definici´ on 3.8 Una gram´ atica es una cu´ adrupla (T, N, P, I), donde: 1.

T es un conjunto finito no vac´Ĺo de terminales, llamado alfabeto.

2.

N es un conjunto finito no vac´Ĺo (disjunto de T ) de variables o frases no terminales.

3.

P es un conjunto finito de producciones o reglas de la forma Îą ::= β donde Îą ∈ (T âˆŞ N )∗ N (T âˆŞ N )∗ y β ∈ (T âˆŞ N )∗ . Dicho de otro modo, Îą es una cadena de terminales y noterminales conteniendo al menos un no terminal y β es una cadena de terminales y no terminales.

4.

I ∈ N es un no terminal especial llamado s´Ĺmbolo inicial.


3.1

Gram´ aticas de los lenguajes de programaci´ on

4

Ejemplo 3.8.1 Sea G1 = ({0, 1}, {A, B, C, U }, P, A) donde P contiene las siguientes producciones: A ::= CB A ::= CU B ::= AU C ::= 0 U ::= 1 describe el conjunto {0n 1n | n â&#x2030;Ľ 1}. Ejemplo 3.8.2 Sea G2 = ({0, 1, 2}, {A, B}, P, I) donde P contiene las siguientes producciones: A ::= 0AB2 A ::=  2B ::= B2 0B ::= 01 1B ::= 11 describe el conjunto {0n 1n 2n | n â&#x2030;Ľ 0}. Ejemplo 3.8.3 Construir una gram´ atica G3 que contenga las sentencias en espaË&#x153; nol. El alfabeto T contiene todas las palabras en espaË&#x153; nol. N contendr´Ĺa los no terminales, que coreesponder´Ĺan a los componenetes estructurales de las sentencias en espaË&#x153; nol, por ejemplo, <sentencia>, <sujeto>, <predicado>, <nombre>, <verbo>, <art´Ĺculo>, etc. El s´Ĺmbolo inicial podr´Ĺa ser <sentencia>. Algunas producciones t´Ĺpicas ser´Ĺan: <sentencia> ::= <sujeto><predicado> <sujeto> ::= <nombre> <predicado> ::= <verbo><art´Ĺculo><nombre> <nombre> ::= dionisio <nombre> ::= algoritmo <verbo> ::= escribe <art´Ĺculo> ::= un Para poder explicar c´ omo una gram´atica puede generar un lenguaje necesitaremos de los siguientes conceptos: Definici´ on 3.9 Sea G = (T, N, P, I) una gram´ atica. Una forma sentencia de G es cualquier cadena de ternimnales y no terminales que es una cadena sobre T â&#x2C6;Ş N . Definici´ on 3.10 Sea G = (T, N, P, I) una gram´ atica y Îł1 y Îł2 dos formas sentencia de G. Decimos que Îł1 deriva directamente a Îł2 , y se denota Îł1 7â&#x2020;&#x2019; Îł2 , si Îł1 = Ď&#x192;ÎąĎ&#x201E; y Îł2 = Ď&#x192;βĎ&#x201E; y Îą ::= β es una producci´ on de P . Por ejemplo, la forma sentencia 00A11 deriva directamente la sentencia 00CB11 en la gram´atica G1 y B2B2 deriva directamente BB22 en la gram´atica G2 de los ejemplos 3.8.1 y 3.8.2, respectivamente. Definici´ on 3.11 Sean Îł1 y Îł2 dos formas sentencia de la gram´ atica G. Decimos que Îł1 deriva Îł2 y se denota Îł1 7â&#x2020;&#x2019;â&#x2C6;&#x2014; Îł2 si existe una secuencia de (cero o m´ as) formas sentencia Ď&#x192;1 , . . . , Ď&#x192;n tales que Îł1 7â&#x2020;&#x2019; Ď&#x192;1 7â&#x2020;&#x2019; . . . 7â&#x2020;&#x2019; Ď&#x192;n 7â&#x2020;&#x2019; Îł2 Por ejemplo, en la gram´ atica G1 , A 7â&#x2020;&#x2019;â&#x2C6;&#x2014; 0011 ya que A 7â&#x2020;&#x2019; OB 7â&#x2020;&#x2019; 0B 7â&#x2020;&#x2019; 0AU 7â&#x2020;&#x2019; 0A1 7â&#x2020;&#x2019; 0OU 1 7â&#x2020;&#x2019; 00U 1 7â&#x2020;&#x2019; 0011 y en la gram´ atica G2 , A 7â&#x2020;&#x2019;â&#x2C6;&#x2014; 001122 ya que A 7â&#x2020;&#x2019; 0AB2 7â&#x2020;&#x2019; 0AB2B2 7â&#x2020;&#x2019; 00B2B2 7â&#x2020;&#x2019; 0012B2 7â&#x2020;&#x2019; 0011B22 7â&#x2020;&#x2019; 001122


3.1

5

Gram´ aticas de los lenguajes de programaci´ on

Definici´ on 3.12 Sea G = (T, N, P, I) una gram´ atica. El lenguaje generado por G, denotado por L(G), se define como: L(G) = {x | x â&#x2C6;&#x2C6; T â&#x2C6;&#x2014; , I 7â&#x2020;&#x2019;â&#x2C6;&#x2014; x} Las palabras en L(G) tambi´en son denominadas sentencias de L(G). En los ejemplos, se ve claramente que L(G1 ) contiene todas las cadenas de la forma 0n 1n , n â&#x2030;Ľ 1 y L(G2 ) contiene todas las cadenas de la forma 0n 1n 2n , n â&#x2030;Ľ 0. Y aunque s´olo hemos dado una definici´on parcial de G3 , sabemos que G3 contiene sentencias tales como â&#x20AC;&#x153;dionisio escribe un algoritmoâ&#x20AC;? y â&#x20AC;&#x153;algoritmo escribe un algoritmoâ&#x20AC;?, pero no sentencias tales como â&#x20AC;&#x153;un escribe un algoritmoâ&#x20AC;?. 3.1.3.

La jerarqu´Ĺa de Chomsky de los Lenguajes Formales

La introducci´ on de las gram´ aticas formales data de los 40 [Pos43]. Aunque el estudio riguroso de los lenguajes mediante la gram´ atica no comenz´o hasta los 50 [Cho56]. Veremos ahora c´omo varias restricciones en la forma de las producciones en las gram´aticas pueden afectar la potencia de la gram´atica en s´Ĺ y en la propia representaci´on de los lenguajes. En particular, veremos c´omo los lenguajes regulares y los lenguajes de patrones se pueden generar todos mendiente gram´aticas con diferentes restricciones. Las gram´ aticas pueden dividirse en cuatro clases mediante un gradual incremento en las restricciones en la forma de las producciones. Esta clasificaci´on se debe a Chomsky [Cho56, Cho63] y es por esto que se la llama jerarqu´Ĺa de Chomsky. Definici´ on 3.13 Sea G = (T, N, P, I) una gram´ atica. 1.

G es tambi´en llamada una gram´ atica de tipo 0 o gram´ atica irrestringida. Sus producciones son del tipo (T â&#x2C6;Ş N )+ ::= (T â&#x2C6;Ş N )+

2.

G es de tipo 1 o sensible al contexto si cada producci´ on Îą ::= β en P es o bien una forma I ::=  o satisface |Îą| â&#x2030;¤ |β|.

3.

G es de tipo 2 o libre de contexto si cada producci´ on ι ::= β en P satisface |ι| = 1, esto es, ι es un s´ olo no terminal.

4.

G es de tipo 3 o lineal o regular si cada producci´ on tiene una de las tres posibles formas: A ::= aB

A ::= a

A ::= 

donde A y B son no terminales y a es un terminal. A los lenguajes correspondientes a estas gram´aticas se les llama de la forma correspondiente. Particularmente a un lenguaje de tipo 1 se le llama tambi´en lenguaje sensible al contexto, mientras que a los de tipo 2 se les llama lenguajes libres de contexto. Los lenguajes de tipo 3 son lenguajes regulares, es decir, pueden ser generados por expresiones regulares, y viceversa. En los lenguajes libres de contexto, ι ser´a sustituible por β sin importar el lugar en el que se encuentre. Las gram´ aticas utilizadas por los analizadores sint´acticos en los compiladores son de tipo 2. La notaci´ on BNF, que veremos m´ as adelante es una notaci´on particular para gram´aticas de tipo 2. Todos los lenguajes derivados de gram´aticas de tipo 2 pueden ser parseados (an´alizados y compilados), habiendo adem´ as algunos subconjuntos de los mismos, usualmente utilizados en las definiciones de los lenguajes de programaci´on que pueden ser parseados en forma especialmente eficiente. Las gram´ aticas regulares, aunque no son lo suficientemente generales para describir la sintaxis de un lenguaje de programaci´ on, sin embargo, estas gram´aticas son ampliamente utilizadas para los analizadores l´exicos de los compiladores ya que describen las entidades b´asicas que conforman un lenguaje de programaci´ on. Los lenguajes descritos por una gram´atica de tipo 3 son f´acil y eficientemente analizables y, en particular siempre se pueden describir mediante una m´aquina de estados finitos.


3.1

Gram´ aticas de los lenguajes de programaci´ on

6

Teorema 1 Para cada i = 0, 1, 2, la clase de tipo i de lenguaje contiene propiamente la clase de lenguajes de tipo i + 1. Como ejemplo, podemos demostrar que el conjunto {0n 1n | n â&#x2030;Ľ 1} es libre de contexto pero no regular y que los conjuntos {0n 1n 2n | n â&#x2030;Ľ 0} y {xx | x â&#x2C6;&#x2C6; {0, 1}â&#x2C6;&#x2014; } son sensibles al contexto pero no libres del contexto. Es, sin embargo un poco m´as complicado construir un lenguaje que sea de tipo 0 pero no sensible al contexto. Las cuatro clases de lenguajes de la jerarqu´Ĺa de Chomsky est´an tambi´en completamente caracterizadas en t´erminos de m´ aquinas de Turing y sus formas restringidas. Se sabe que un lenguajes de tipo 0 es exactamente aqu´el que es reconocido por m´aquinas de Turing, los sensibles al contexto por m´ aquinas de Turing funcionando en espacios lineales, los lenguajes libres de contexto, por m´aquinas de Turing cuya cinta opere como una pila (llamadas aut´omatas push-down) y los lenguajes regulares son los que son reconocidos por m´aquinas de Turing sin cinta ninguna (llamadas m´aquinas de estados finitos o aut´ omatas de estados finitos). 3.1.4.

Propiedades de las gram´ aticas

Dos gram´ aticas G y G0 se dicen equivalentes si los lenguajes que generan, L(G) y L(G0 ) son iguales. Esta equivalencia no significa que la derivaci´on sea la misma (tampoco, por lo tanto el ´arbol de parsing). Por ejemplo: G G0 A ::= Ax|y A ::= yB B ::= xB| Una gram´ atica se dice ambigua si permite m´as de una posible derivaci´on para una misma expresi´on. Por ejemplo: I ::= AA con A ::= x|xx da dos posibles formas de derivaci´on para la cadena xxx. 3.1.5.

Formas normales

Las formas normales son m´etodos de describir los lenguajes a trav´es de ciertas reglas. Uno de los usos m´as importantes que tienen es el de demostrar las propiedades de los lenguajes. Pueden, a veces, no ser f´ aciles de leer o comprender pero facilitan mucho m´as el an´alisis que presentaciones m´as particulares. Cualquier lenguaje libre de contexto puede describirse mediante la Forma Normal de Chomsky (CNF) o la Forma Normal de Backus (BNF). La forma normal de Backus es algo m´as legible que la de Chomsky. Tambi´en es llamada Forma Normal de Backus-Naur (ver el § 3.2.4). BNF es un metalenguaje utilizado para describir sistemas de producci´on que generen lenguajes libres de contexto. Los lenguajes generados utilizando BNF incluiran, naturalmente, un conjunto de terminales, de no terminales y una lista de producciones, y un s´Ĺmbolo inicial1 . Los terminales en BNF se indican de diversa forma seg´ un la bibliograf´Ĺa. Nosotros utilizaremos letras min´ usculas mientras que para los no terminales (variables) se utilizar´an may´ usculas. Adem´as BNF utiliza una serie de metas´Ĺmbolos que se han extendido m´as all´a de la teor´Ĺa de gram´aticas, como por ejemplo, a manuales de todo tipo de lenguajes: S´Ĺmbolo ::= | ALGO algo

Significado se define como alternativamente no-terminal terminal

1 A veces en las notaciones BNF no se especifica claramente cu´ al es el s´Ĺmbolo inicial, entendi´ endose ´ este por el grado de generalidad: el m´ as general es el s´Ĺmbolo inicial.


3.2

Tipos de lenguajes de programaci´ on: lenguajes imperativos

7

Con el paso del tiempo el BNF se ha ampliado dando lugar a un lenguaje m´as legible mediante la inclusi´on de indicadores de iteraci´ on y grupos: S´ımbolo [algo] { algo } (tal | cual)

Significado cero o una aparici´on de ese algo cero o m´as apariciones de ese algo grupo; o bien tal o bien cual

Por ejemplo, una definici´ on de un identificador en el lenguaje de programaci´on C, descrita en BNF ser´ıa: IDENT::=LETRA | IDENT LETRA | IDENT DIGITO con LETRA::= | [a..z] | [A..Z] y DIGITO::=[0..9] y usando EBNF: IDENT::=LETRA {LETRA | DIGITO} Algunas variantes de EBNF incluyen en forma de super´ındices y sub´ındices el m´ınimo y el m´aximo, respectivamente, de las posibles repeticiones. Por ejemplo {a}53 indica la posible repetici´on de la letra terminal a entre 3 y 5 inclu´ıdas veces. La notaci´ on EBNF evita la recursi´ on de BNF sustituy´endola por iteraci´on. 3.1.6.

Diagramas sint´ acticos

Otra forma de describir las reglas sint´aciticas de un lenguaje son los diagramas de Conway. Los s´ımbolos m´ as importantes se representan en la Figura 1.

Símbolo terminal

Símbolo NO terminal concatenación Figura 1: Significado de algunos s´ımbolos de los diagramas de Conway

3.2.

Tipos de lenguajes de programaci´ on: lenguajes imperativos

Cualquier notaci´ on que se d´e para la descripci´on de un algoritmo o una estructura de datos puede ser llamada lenguaje de programaci´on. Naturalmente, sin embargo, no todos los lenguajes de programaci´ on se plantean para ser implementados en los ordenadores. Se han desarrollado e implementado cientos de lenguajes de programaci´on diferentes. Ya en 1969 J. Sammet [Sam69] hace una lista de 120 lenguajes de programaci´on ampliamente utilizados; y desde entonces se han desarrollado un buen pu˜ nado de ellos m´as. La mayor´ıa de los programadores, sin embargo, no se aventuran a utilizar m´ as que unos pocos de ellos (mientras menos mejor), quiz´as uno o dos. En la pr´ actica, en cada lugar de trabajo se acuerda el utilizar un lenguaje u otro (C, Ada, FORTRAN, Matlab), como sistema de desarrollo, simplificando despu´es el intercambio de ideas y material entre los programadores. Sin embargo es interesante conocer las caracter´ısticas de los lenguajes de programaci´ on m´as importantes por distintas razones:


3.2

8

Tipos de lenguajes de programaci´ on: lenguajes imperativos

1.

Mejorar habilidad en el desarrollo de algoritmos efectivos, ya que conociendo c´omo se implementan las t´ecnicas de programaci´ on que usamos, las utilizaremos mejor.

2.

Aumentar el vocabulario de construcciones en programaci´on.

3.

Disponer de m´ as y mejor conocidas alternativas a la hora de elegir un lenguaje de programaci´on.

4.

Facilitar el aprendizaje de nuevos lenguajes.

5.

Hacernos una idea de distintos estilos de presentar y estructurar los algoritmos, para mejorar nuestro estilo.

3.2.1.

Paradigmas de los lenguajes

Podemos clasificar los lenguajes de programaci´on seg´ un el modelo o paradigma, colecci´on de posibilidades abstractas, que lo caractericen. Ver la Figura 2.

Imperativo

Procedural

Estructurado en bloques

Basado en objetos

Orientado a objetos

Proceso paralelo

Paradigmas de los lenguajes de programación

Lógico Declarativo

Funcional Base de datos

Figura 2: Jerarqu´ıa de paradigmas de los lenguajes de programaci´ on

3.2.2.

El paradigma imperativo

Se caracteriza por facilitar la computaci´on mediante cambios de estados en la m´aquina. Entendiendo por estado la configuraci´ on o valores tanto de la RAM, como de los diversos dispositivos variables que componen un ordenador. Bajo este paradigma es u ´til el ver la ejecuci´on como una secuencia de fotogramas cada cual evidenciando todos los valores interesantes bajo el control del programa imperativo. Al comenzar el programa se dan una serie de datos en determinadas localizaciones de la memoria y es tarea del programa especificar la secuencia de cambios que han de hacerse sobre esta informaci´on para conseguir el estado final de la memoria deseado. Para conseguir esto el programa imperativo se sirve de estructuras de datos cuya implementaci´on es bien conocida y condiciona la forma de los programas. Los programas imperativos dependen en mayor o menor grado de las operaciones realizables por el sistema operativo. El lenguaje FORTRAN fue el primer lenguaje imperativo con bloques de programa, que recog´ıan subrutinas, datos comunes, etc. Sin embargo de una manera plana, lo que lo excluy´o de la calificaci´on de estructurado en bloques. El t´ermino estruturado en bloques se refiere hoy d´ıa a la posibilidad de anidamiento de ´ambitos, esto es, el que un bloque pueda ser encajado dentro de otro y contener su propio ´ambito de variables sin interferencia alguna con el exterior. En los lenguajes estructurados por bloques, el procedimiento es el principal elemento de construcci´on. Ejemplos de lenguajes estructurados en bloques son el Ada, ALGOL60, Pascal. Un objeto en programaci´ on es un conjunto de elementos de informaci´on y de procedimientos para manejarla que forman un todo sobre el cual se puede trabajar ‘activando’ los procesos que


3.2

Tipos de lenguajes de programaci´ on: lenguajes imperativos

9

as´ı cambian de estado el objeto. Estos objetos capturan, pues, propiedades inherentes a entes complejos de la realidad y sirven para modelarla y facilitar su uso abstrayendo sus propiedades dentro del objeto. Para distinguir aquellos lenguajes que s´olamente est´an basados en objetos de los que adem´as poseen posibiliades de clases y herencia entre objetos, se utiliza el t´ermino, para ´estos u ´ltimos, de orientados a objetos. As´ı mientras que el antiguo Ada 83 estaba s´olamente basado en objetos, el Ada 95 est´a orientado a objetos, aunque para algunos no es a´ un totalmente orientado a objetos, como lo puede ser Smalltalk o Eiffel. El paradigma de la programaci´ on distribuida La programaci´on concurrente se ha dividido en dos grandes categor´ıas: sistemas fuertemente y d´ebilmente acoplados. El t´ermino distribuida se refiere a lenguajes para sistemas d´ebilmente acoplados, como el que podr´ıa dar soporte a un grupo de empleados trabajando sobre una base de datos u ´nica simult´aneamente y comunic´andose mediante el paso de mensajes a trav´es de canales de comunicaci´on tales como enlaces punto a punto redes de ´ area local (LAN). En este tipo de lenguajes no es necesaria la compartici´on de memoria pero s´ı hay que resolver otros tipos de problemas. Lenguajes como el Ada permiten la compartici´on de recursos mediante el mecanismo de rendevous. Otros lenguajes m´ as recientes permiten ambos tipos de enfoque, por ejemplo Occam, Linda, Concurent Prolog. 3.2.3.

El paradigma declarativo

Un lenguaje declarativo es aquel en el que se especifica una relaci´on o funci´on. Cuando se programa en forma declarativa no se hacen nunca asignaciones a variables, no existen las variables. El int´erprete o compilador del lenguaje en particular gestiona la memoria de manera transparente al programador. Estos lenguajes son me m´as “alto nivel” que los lenguajes imperativos ya que el programador est´ a a´ un m´ as alejado del modelo de m´aquina u ordenador. Los tres paradigmas declarativos han sido tomados de la matem´atica: la l´ogica, la teor´ıa de funciones y el c´ alculo relacional. La programaci´ on l´ ogica se basa en un subconjunto del c´alculo de predicados y presenta las acciones o sentencias en forma de cla´ usulas de Horn. El c´alculo de predicados aporta axiomas y reglas de las que se pueden deducir nuevos hechos a partir de otros hechos dados. Una cla´ usula de Horn permite tan s´ olo deducir un hecho de cada sentencia. Un sistema de cla´ usulas de Horn permite un m´etodo mec´ anico particular de prueba llamado resoluci´ on. Un programa basado en la l´ ogica consiste en una serie de axiomas o hechos, reglas de inferencia y un teorema o consulta a comprobar. La salida ser´a ‘cierto’ si los hechos apoyan la consulta, ‘falso’ en otro caso. Prolog es el modelo de este tipo de lenguajes, aunque existen diversas sintaxis y aproximaciones para su evaluaci´ on. La programaci´ on funcional Los lenguajes funcionales puros operan s´olo sobre funciones. Una funci´on siempre devuelve, y como m´ aximo un s´olo valor, despu´es de recibir una lista de par´ametros, que pueden ser los resultados de las llamadas a otras funciones. No se permiten asignaciones a variables globales ni, los llamados, efectos laterales. Las funciones pueden incluso ser valores que pueden ser pasados a otras funciones y devolverse valores funcionales. Esto u ´ltimo permitir´a a los programas funcionales modificarse a s´ı mismos, ‘aprender’. En la pr´ actica existen varios lenguajes funcionales y en casi todos ellos se permiten algunos efectos laterales, particularmente importantes, como los de la entrada y salida de datos, que implican la modificaci´ on de estados externos a las funciones. Como en el caso de la programaci´on l´ogica existe un prototipo, el LISP, de lenguaje funcional, pero como en aqu´el, se incluyen en las implementaciones pr´ acticas muchas posibilidades no puristas.


3.2

Tipos de lenguajes de programaci´ on: lenguajes imperativos

10

El paradigma base de datos Las propiedades que distinguen a los lenguajes dise˜ nados para el manejo de las bases de datos son la persistencia y el control del cambio. Las entidades base de datos no desaparecen al terminar el programa, que una vez organizadas, son permanentes. Un sistema de gesti´ on de bases de datos incluye un lenguaje de definici´on de datos (DDL) para la descripci´ on de nuevos hechos o datos, y un lenguaje de manipulaci´on de datos (DML) para la interacci´on con las bases de datos existentes. Los lenguajes de bases de datos pueden estar embebidos en otros lenguajes de programaci´on para mayor flexibilidad. 3.2.4.

Historia de los lenguajes de programaci´ on

Los lenguajes de programaci´ on han evolucionado cont´ınuamente desde la aparici´on de los primeros en los 50. Las primeras versiones de FORTRAN y Lisp aparecieron durante los a˜ nos 50; Ada, C, Pascal, Prolog y Smalltalk en los 70; C++ y ML en los 80. Cuando en los 70, el departamento de defensa americano (DoD) realiz´o un estudio del estado de cosas encontr´o que se utilizaban m´ as de 500 lenguajes de programaci´on en sus distintos proyectos; este fue un motivo para el desarrollo por parte del DoD del lenguaje Ada. Las primeras tecnolog´ıas en computaci´on datan de los a˜ nos 30 a 40, antes de la Segunda Guerra Mundial. Estas primeras m´ aquinas fueron dise˜ nadas para resolver problemas num´ericos y fueron pensadas simplemente como calculadores electr´onicos. Naturalmente de esto deriv´o el que la mayor´ıa de las aplicaciones de entonces sean num´ericas. A principios de los 50 comenz´ o a aparecer la notaci´on simb´olica. Grace Hopper dirigi´o un grupo en Univac que desarroll´ o el lenguaje A-0 y John Backus desarroll´o el Speedcoding para el IBM 701. Ambos lenguajes fueron dise˜ nados para compilar expresiones aritm´eticas sencillas en c´odigo m´aquina ejecutable. El gran salto se dio de 1955 a 1957 cuando Backus dirigi´ o un grupo para desarrollar FORTRAN (FORmula TRANslator). Como en los lenguajes hasta entonces, FORTRAN estaba orientado a los c´alculos num´ericos, pero el objetivo se ampli´o con un lenguaje capaz de incluir estructuras de control, condicionales, ´ ordenes de entrada y salida, etc. Dado que hab´ıa poca confianza en que el lenguaje resultara competitivo frente al c´odigo desarrollado directamente a mano sobre las instrucciones del procesador (Ensamblador), se hizo un gran esfuerzo en conseguir una ejecuci´on eficiente y se introdujeron varias ´ ordenes dise˜ nadas espec´ıficamente para el IBM 704. As´ı nos encontramos en el lenguaje FORTRAN conceptos como el salto aritm´etico a tres caminos, concepto curioso exclusivo de a aqu´el procesador que no se ha seguido posteriormente. No se trataba de un lenguaje ‘elegante’, pero en aquellos d´ıas, el concepto de ‘elegancia’ en la programaci´on a´ un no se hab´ıa acu˜ nado. FORTRAN fue extremadamente u ´til; tanto que ha cambiado la programaci´on desde entonces. FORTRAN ha sido revisado en 1958 (FORTRAN II) y pocos a˜ nos despu´es (FORTRAN IV). FORTRAN IV se convirti´ o en un est´ andard en 1966 como FORTRAN 66 y se ha actualizado dos veces desde entonces: FORTRAN 77 FORTRAN 90. Sin embargo la candidad de c´odigo escrito para las antiguas versiones hacen d´ıficil evolucionar a los nuevos dialectos, que pr´acticamente s´olo se pueden preocupar de ser compatibles con todas las posibilidades anteriores. Tras el ´exito del FORTRAN y por miedo en Europa al dominio de IBM se organiz´o la GAMM (German society of applied mathematics) para el desarrollo de un lenguaje universal. En los Estados Unidos, la ACM (Association of Computing Machinery) tambi´en se propuso tal objetivo. Ambos comit´es se fundieron en uno bajo las directrices de Peter Naur y desarroll´o el IAL (Internations Algorithmic Language). El nombre ALGOL (ALGOrtihmic Language) fue inicialmente rechazado, pero su uso oblig´ o a que oficialmente se aceptara: finalmente el lenguaje se llam´o Algol 58. Se hizo una revisi´ on en el 60 que se llam´o Algol 60 (con una menor en el 62) convirti´endose en el est´andard de los lenguajes de la programaci´on acad´emica de los 60 y principio de los 70. Mientras que uno de los objetivos del FORTRAN era la eficiencia, los objetivos del Algol fueron diversos: 1.

La notaci´ on deb´ıa ser parecida a la de las matem´aticas usuales.


3.2

Tipos de lenguajes de programaci´ on: lenguajes imperativos

2.

Deber´ıa ser u ´til para describir algoritmos.

3.

Deber´ıa ser compilable en c´ odigo m´aquina.

4.

No deber´ıa estar ligado a ninguna arquitectura particular de computador.

11

Siendo estos unos objetivos ambiciosos para el a˜ no 1957, la independencia de la m´aquina hizo que Algol careciera oficialmente de instrucciones para introducir y presentar datos, debiendo ´estas ser escritas para cada hardware en cada m´aquina. Esto, naturalmente conllevaba incompatibilidades entre los programas hechos en diferentes m´aquinas. La exigida cercan´ıa a la sintaxis matem´atica hizo que las llamadas a subrutinas fuesen meras expansiones de macros. Esto introdujo el concepto de llamada por nombre en el paso de par´ametros, que es dif´ıcil de implementar en los compiladores. Aunque Algol no tuvo ´exito comercial en los Estados Unidos, s´olo algo en Europa, tuvo un gran impacto. Uno de sus herederos fue la versi´on de Jules Schwartz de SDZ del lenguaje IAL, JOVIAL (Jules’ Own Version of IAL), que se convirti´o en un est´andard para las aplicaciones de las fuerzas a´ereas americanas. Backus edit´ o la definici´ on del lenguaje Algol en 1960 mediante una notaci´on sint´actica compa´ rable a la gram´ atica libre de contexto desarrollada por Chomsky un a˜ no antes. Este fue el comienzo de la introducci´ on de la teor´ıa de las gram´aticas formales a los lenguajes de programaci´on. Debido al importante papel que tuvo Naur en el desarrollo del Algol, la notaci´on empleada recibe el nombre de BNF, o Backus Naur Form. Otro ejemplo de la gran influencia del Algol fue el de Burroughs, un vendedor de ordenadores que se fusiona con Sperry Univac para formar Unisys, al descubrir los trabajos de un matem´atico polaco llamado Lukasiewicz. Lukasiewicz hab´ıa desarrollado una interesante, aunque no muy revolucionaria matem´ aticamente, nueva t´ecnica que permit´ıa a las expresiones aritm´eticas ser escritas sin par´entesis mediante un potente proceso de evaluaci´on basado en una pila. Este descubrimiento tuvo un gran efecto en la teor´ıa de compiladores. Usando el m´etodo de Lukasiewicz, Burroughs desarroll´o el hardware del ordenador B5500 basado en una arquitectura de pilas e inmediatamente un compilador de Algol mucho m´ as r´ apido que ninguno de los hasta entonces existentes de FORTRAN. En este punto, la historia diverge. En el 60 se desarrolla el concepto de tipo definido por el usuario y ni FORTRAN ni Algol tienen tal capacidad. Simula 67, desarrollado por Nygaard y Dahl de Noruega introducen el concepto de clase en Algol. Esto dio a Stroustrup la idea para sus clases en C++ como una extensi´ on del C en los 80. Niklaus Wirth desarroll´o el Algol-W a mediados de los 60 como una extensi´ on del Algol. Este dise˜ no tuvo un precario ´exito, pero su Pascal dise˜ nado entre el 68 y el 70, se convirti´ o en el lenguaje de los computadores cient´ıficos de los 70. Hubo otro comit´e que intent´ o duplicar el ´exito del Algol 60 con el Algol 68, pero el lenguaje fue radicalmente diferente y mucho m´ as complejo de comprender e implementar eficientemente. Con la introducci´ on de la l´ınea de los 360 en 1963, IBM desarroll´o el NPL (New Programming Language) en sus laboratorios de Hursley en Gran Breta˜ na. Despu´es de algunas quejas del English National Physical Laboratory, el nombre se cambi´o a MPPL (Multi-Purpose Programming Language), que fue abreviado a PL/I. PL/I mezcl´o las facilidades num´ericas del FORTRAN con las capacidades de gesti´ on mercantil del COBOL. PL/I tuvo un ´exito moderado en los 70 y ha sido hoy d´ıa totalmente reemplazado por el C y el Ada. BASIC fue un subconjunto del FORTRAN f´acil de implementarse para ser interpretado en vez de compilado y de f´acil aprendizaje, satisfaciendo las necesidades de c´ alculo del no cient´ıfico, se ha extendido mucho m´as all´a de lo proyectado inicialmente. Lenguajes para los negocios Inmediatamente despu´es de los lenguajes para el c´alculo num´erico surgieron los lenguajes para los negocios. Grace Hopper dirigi´o un grupo en Univac para desarrollar Flowmatic en 1955, cuyo objetivo era el desarrollo de aplicaciones para negocios utilizando expresiones lo m´ as naturales del lenguaje ingl´es. En el 59 el DoD promovi´ o un encuentro para el desarrollo del Common Business Language (CBL), que deber´ıa de ser un lenguaje orientado a los negocios con expresiones lo m´as inglesas


3.2

Tipos de lenguajes de programaci´ on: lenguajes imperativos

12

posible. Una subdivisi´ on de este grupo public´o en el 60 lo que ser´ıa el COBOL (COmmon Business Oriented Language). COBOL fue revisado en el 74 y en el 84. Hoy d´ıa se sigue utilizando. Lenguajes para la inteligencia artificial El inter´es en el desarrollo de lenguajes para desarrollo de IA (inter´es artificial) comenz´ o en los 50 con el IPL (Information Processing Language) por la Rand Corporation. IPL-V fue ampliamente conocido, pero se extendi´o poco porque ten´ıa unas especificaciones de muy bajo nivel de abstracci´on. El gran salto se dio cuando John McCarthy del MIT dise˜ n´o el LISP (LIst Processing) para el IBM 704. El LISP 1.5 se convirti´o en un est´andard durante muchos a˜ nos. Recientemente Scheme y Common LISP han seguido esta evoluci´on. El lenguaje LISP fue dise˜ nado como un procesador de listas de los lenguajes funcionales. El dominio usual de los problemas para LISP es la b´ usqueda. Particularmente el desarrollo de juegos ha sido un terreno propio del LISP dado que un programa de LISP usualmente puede desarrollar movimientos arb´ oreos (como listas enlazadas) y posteriormente moverse por el ´arbol buscando la estrategia ´optima. Un paradigma alternativo fue tambi´en el del procesado de cadenas donde la soluci´on habitualmente involucraba la transformaci´on de los textos de un formato a otro. M´aquinas traductoras autom´ aticas en las que cadenas de s´ımbolos eran sustituidas por otras, fueron tambi´en dominio del LISP. El lenguaje COMIT de Yngve del MIT fue un intento inicial para este tipo de trabajos. Cada instrucci´ on del programa era muy parecida a una regla de producci´on de un lenguaje de contexto libre y representaba un conjunto de posibles reemplazos que podr´ıan darse si la cadena se encontraba en los datos. Debido a que Yngve mantuvo su propiedad sobre el c´odigo, un grupo en la en los laboratorios de la AT&T Bell decidi´o desarrollar su propio lenguaje, que result´o ser el SNOBOL. Mientras que el LISP fue dise˜ nado para aplicaciones de procesado de listas de prop´osito general, el Prolog fue un lenguaje de prop´osito especial cuyas estructuras b´asicas de control y la estrategia de implementaci´ on se basaron en conceptos de l´ ogica matem´atica. Lenguajes para sistemas Debido a la necesidad de eficiencia, durante a˜ nos fue el ensamblador el lenguaje utilizado para el desarrollo de sistemas incluso mucho despu´es de que otras ´areas de aplicaci´on utilizaran ya lenguajes de alto nivel. Se dise˜ naron muchos lenguajes para la programaci´on de sistemas, tales como el CPL y el BCPL, pero nunca llegaron a una amplia expansi´on. El lenguaje C cambi´ o todo esto con su llegada con un entorno competitivo, el UNIX, totalmente escrito en C, durante el comienzo de los 70. Los lenguajes de alto nivel han demostrado ser efectivos en estas ´areas, tanto como otros. 3.2.5.

El papel de los lenguajes de programaci´ on

Aunque inicialmente los lenguajes fueron desarrollados con el objetivo acuciante de la eficiencia, debido sobre todo al alto coste de los ordenadores (cientos de millones de pesetas) frente al de los programadores (dos millones de pesetas por a˜ no), teniendo que competir siempre con la efectividad de la programaci´ on directa sobre el hardware en ensamblador, la necesidad de conseguir programas correctos de unas 30.000 instrucciones, llev´o a mediados de los 60 (con la llegada del FORTRAN, el COBOL, el LISP y el Algol) a un nuevo enfoque del problema. Los ordenadores se fueron haciendo m´ as baratos y los costes de la programaci´on mayores. Esto forz´o la necesidad de trasladar los programas escritos de unos ordenadores a otros y el mantenimiento de tales productos se convirti´ o tambi´en en un gran costo a˜ nadido. De manera que en vez de compilar grandes programas en grandes y caros ordenadores, la tarea de los lenguajes de alto nivel se convertir´ıa en falicilitar el desarrollo de programas correctos para la resoluci´on de problemas ´areas dadas de aplicaciones. En los 70, madura la tecnolog´ıa de los compiladores, t´ıpicamente se ten´ıa al FORTRAN para aplicaciones cient´ıficas, al COBOL para los negocios, al JOVIAL para las aplicaciones militares, al LISP para las de inteligencia artificial y para las aplicaciones empotradas en sistemas fijos, el Ada. Sin embargo ha habido una evoluci´ on natural de estos lenguajes debido a: 1.

La capacidad de los computadores, que ha aumentado enormemente en poco tiempo.


3.2

Tipos de lenguajes de programaci´ on: lenguajes imperativos

13

2.

Las aplicaciones a las que se destinan los ordenadores, que se ha ampliado bastante.

3.

Los m´etodos de programaci´ on, que han ido evolucionando para tener en cuenta nuevos conceptos relativos al ambiente de trabajo en grandes aplicaciones y al ambiente de uso de las mismas.

4.

Las nuevas t´ecnicas de implementaci´on de los lenguajes que han introducido nuevas t´ecnicas de programaci´ on.

5.

Los estudios te´ oricos, especialmente la formalizaci´on matem´atica en la b´ usqueda de la correcci´on, han introducido nuevas t´ecnicas de programaci´on.

6.

La estandarizaci´ on que ha promovido un fuerte conservacionismo en la evoluci´on de los lenguajes.

3.2.6.

Cualidades de los lenguajes

¿Qu´e hace que un lenguaje sea bueno? Los m´etodos de dise˜ no de los lenguajes a´ un tienen mucho que mejorar. Los motivos para el ´exito o el fracaso de un lenguaje son muchas veces externos a ellos mismos, como por ejemplo, el caso de los lenguajes COBOL y Ada, promovidos por entidades poderosas. En otros casos este ´exito se lo ha dado el apoyo por parte de diversos fabricantes, como le pas´ o al FORTRAN. A veces es sencillamente el unirlos a excelentes textos para describirlos, como le ocurri´ o al SNOBOL4 durante los 70. Mientras que el Pascal y el LISP se han visto apoyados por el estudio te´ orico que de ellos han hecho los estudiantes de dise˜ no de lenguajes a la vez que lo usaban. Independientemente de estos factores externos, lo que deber´ıa determinar si un lenguaje debiera sobrevivir ser´ıa: 1.

Claridad, simplicidad y unicidad. Que podr´ıamos resumir en integridad conceptual. La sintaxis del lenguaje afecta la facilidad de lectura de los programas con ´el escritos y la legibilidad de los programas es fundamental. Los lenguajes cr´ıpticos (como APL) o el uso de operadores ocultos (como el espacio en SNOBOL4) que alteran, sin uno verlos, el significado, son muy perniciosos en el mantenimiento de los programas.

2.

Ortogonalidad, o independencia de cada construcci´on respecto de las otras de manera que se puedan combinar libremente y ser entendidas sin considerar los contextos de cada una.

3.

Naturalidad para la aplicaci´ on a programar. El lenguaje FORTRAN tiene tanto ´exito en parte debido a que las expresiones matem´aticas se parecen mucho a las que se utilizan en las mismas matem´ aticas.

4.

Apoyos para la abstracci´ on, permitiendo construir estructuras nuevas a las que se pueda referir mediante sintaxis sencillas y que incluyan todas las propidades de los objetos reales representados. Del Pascal surgi´ o el Ada y del C el C++ por su mayor soporte a la abstracci´on.

5.

Facilidades para la verificaci´ on como base para la construcci´on de grandes programas fiables mediante el uso de estructuras sint´ acticas sencillas y de sem´anticas lo m´as simples posibles.

6.

Entornos de programaci´ on adecuados y completos que faciliten la labor de los desarrolladores. Pocos lenguajes se definen inicialmente con este problema resuelto. En este sentido uno de los m´as completos ha sido Smalltalk; tambi´en Ada.

7.

Portabilidad de los programas a los distintos sistemas mediante la minimizaci´on y el aislamiento de las partes m´ as dependientes del sistema particular para facilitar su localizaci´on y modificaci´ on f´ acil en los traslados.

8.

Costo de uso, como criterio fundamental que incluye el costo de ejecuci´on (necesidades de hardware y molestias en la instalaci´ on, etc.), de traslaci´on a otros sistemas (mientras m´as f´acil sea mayor el mercado que se abarca), costo de creaci´on, prueba y uso (para la preparaci´on del programador, etc.), costo de mantenimiento (muy variable con cada lenguaje).


3.3

14

El teorema de las estructuras

3.2.7.

Dominios de las aplicaciones

La elecci´ on del lenguaje depende fundamentalmente del dominio de la aplicaci´on a realizar. Los lenguajes han ido evolucionando en los u ´ltimos 30 a˜ nos tanto por su propia evoluci´on como por la aparici´ on de nuevos dominios y necesidades de los existentes. T´ıpicos dominios son los de los negocios, cient´ıficos, la construcci´ on de sistemas, Inteligencia Artificial, publicaci´on electr´onica (que ha hecho imprescindible en los u ´ltimos 20 a˜ nos), proceso de tareas automatizado, programaci´on de la interacci´on mediante la World Wide Web, nuevos paradigmas de programaci´on en desarrollo. Ver la tabla 1 Cuadro 1: Los lenguajes m´ as adecuados a los diferentes dominios

A˜ nos 60s

Hoy

3.3.

Aplicaci´ on Negocios Ciencias Sistemas IA Negocios Ciencias Sistemas IA Publicaci´ on Procesos Web N. Paradigmas

M´ as importantes COBOL FORTRAN Ensamblador LISP COBOL, Hojas de c´alculo FORTRAN, C, C++ C, C++ Lisp, Prolog procesadores de texto UNIX shell, Tcl, Perl HTML, Java ML, Smalltalk

Otros Ensamblador Algol, BASIC, APL JOVIAL, Forth SNOBOL C, PL/I, 4GLs BASIC, Pascal Pascal, Ada, Modula2 TEX, PostScript Marvel Perl, Tcl Eiffel

El teorema de las estructuras

El t´ermino de “Programaci´ on Estructurada” fue introducido por Dijkstra (entre 1965 y 1972) refiri´endose a la necesidad de una programaci´on m´as met´odica y rigurosa. Para unos signific´o codificar estructuradamente, con determinadas sentencias de control y criterios de estilo y documentaci´on, mientras que para otros fue toda una nueva concepci´on general de dise˜ no y desarrollo de programas. Objetivos de la programaci´ on estructurada En cualquier caso, la programaci´on estructurada (PE) intenta mejorar el proceso de la programaci´on mediante una adecuada organizaci´on de los programas y una mejora de los lenguajes de programaci´on, de forma que pudieran realizarse descripciones claras y precisas de las estructuras de datos y control. Esto lleva a programas m´as correctos, f´aciles de leer y modificar y m´ as f´acilmente verificables. Historia

Los momentos m´ as importantes de la PE son los siguientes:

1965 Dijkstra introduce el concepto de PE v´agamente y sin demasiado ´exito. 1966 Bohm y Jacopini definen un programa estructurado como un programa cuyo flujo de control pudiera expresarse usando s´ olo las tres estructuras b´asicas de control (secuencia, selecci´on, iteraci´on). De ah´ı probaron el “Teorema de las Estructuras” y suscitaron la pol´emica del GOTO m´ as adelante. M´as adelante mostraremos estas estructuras de control de flujo. 1968 Dijkstra en la ACM publica un art´ıculo contra el GOTO, con resonancia hasta 1975. En 1974, Donald E. Knuth publica un art´ıculo titulado “Structured Programming with GOTO statements”.


3.4

Las estructuras fundamentales de control de flujo

15

´ Ultimas opiniones sobre goto En cualquier caso Mills (1972) opina que “los programas estructurados deben caracterizarse no simplemente por la ausencia de GOTOs, sino por la presencia de estructura. . . La teor´ıa de la PE se refiere a la conversi´on de diagramas de flujo arbitrariamente grandes y complejos a formas standard que puedan representarse mediante la iteraci´on y anidamiento de varias estructuras l´ ogicas de control standard m´as peque˜ nas”. Para hacernos una idea de sus opiniones valgan los siguientes comentarios: Edsger Dijkstra’s Evaluations of Programming Languages (c. 1982) FORTRAN, “the infantile disorder”, by now nearly 20 years old, is hopelessly inadequate for whatever computer application you have in mind today: it is now too clumsy, too risky, and too expensive to use. PL/I – “the fatal disease” – belongs more to the problem set than the solution set. It is practically imposible to teach good programming to students that have had a prior exposure to BASIC: as potential programmers they are mentally mutilated beyond hope of regeneration. The use of COBOL cripples the mind; its teaching should, therefore, be regarded as a criminal offense. APL is a mistake, carried through to perfection. It is the language of the future for the programming techniques of the past: it creates a new generation of coding bums.

Programaci´ on descendente. Wirth Por otro lado, el paradigma de la “Programaci´on Descendente” (PD o Stepwise Refinement) es de Wirth en 1971. Este proceso considera la programaci´on como un proceso anal´ıtico que permite transformar especificaciones en programas. Wirth define ´esto en 1974: “La PD es la formulaci´ on de programas como jerarqu´ıas, estructuras anidadas de sentencias, y objetos de computaci´ on”. Jerarquizaci´ on de la estructuraci´ on Para llegar a esta formulaci´on jer´arquica es necesario aplicar una serie de refinamientos sucesivos que van desde la especificaci´on del problema hasta su resoluci´on expresada en un lenguaje de programaci´on detallando ya los pasos relativos al dispositivo en que se desarrolla.

3.4.

Las estructuras fundamentales de control de flujo

Las estructuras fundamentales de control de flujo son, como se dijo antes, al hablar del Teorema de Bohm y Jacopini, tres: secuencia, selecci´on o decisi´on e iteraci´on (repetici´on o bucle). Cualquier otra forma de control de la ejecuci´on de operaciones se podr´a, pues, convertir en ´estas. La ventaja de identificar estas y exigir s´olo el uso de estas est´a fundamentalmente en conocer as´ı r´apidamente el comportamiento de los programas y en poderlos analizar mejor. 3.4.1.

Secuencia

Cuando un proceso directo, se puede realizar con la informaci´on que recibe sin necesidad de desviar los pasos y de una u ´nica vez, el proceso es susceptible de ser ejecutado en una operaci´on o, como m´aximo, en una secuencia de operaciones m´as sencillas que compongan la acci´on u ´nica total. Los procesos secuenciales, cuando est´an compuestos por varias acciones, tienen la propiedad de que aquellas acciones se ejecutan cada una detr´as de la anterior, nunca de forma simult´anea (sino existir´ıa ‘paralelismo’, que es otro tema). Adem´as, y como corolario de lo anterior, hasta que no se termina de ejecutar la instrucci´ on precedente, no se puede ejecutar la siguiente. Normalmente, en programaci´ on, las acciones de los procesos secuenciales se suelen escribir una debajo de la otra acabando, en la mayor´ıa de los lenguajes de programaci´on, cada acci´on con un signo de punto y coma ‘;’. ... acci´on 1; acci´on que sigue a la 1; debo ser la acci´ on 3; ...


3.4

Las estructuras fundamentales de control de flujo

16

Como regla com´ un, los procesos secuenciales, compuestos de varias acciones simples que son una, continuaci´on de otra, se dan cuando se conoce con precisi´ on el punto de partida y el de llegada. Por ejemplo, tengo dos n´ umeros y quiero obtener su media: se tienen dos n´ umeros y se quiere obtener la media sumar los dos n´ umeros guardando el resultado; tomar el resultado guardado antes y dividirlo por 2 guardando el poner el resultado anterior donde sea conveniente; La ejecuci´ on de estos pasos o acciones se hace temporalmente en orden, cada acci´on debe agotarse y los resultados de cada acci´ on se van obteniendo “cuando les toca su turno”. No es lo mismo: guardar en r la suma de a y b; guardar en s la suma de r y a; que guardar en s la suma de r y a; guardar en r la suma de a y b; La mayor´ıa de las programas de ordenador son secuencias de instrucciones a las que los programadores se acostrumbran a leer. La lectura es siempre de arriba a abajo y cada instrucci´on es una l´ınea que, una vez ejecutada, puede haber cambiado el valor o el ‘estado’ de todo el programa. Ese nuevo estado, tras cada acci´on es el que se encontrar´a el programador para la acci´on que venga detr´as2 . Un ejemplo de algoritmo secuencial puede ser: c para diestros Algoritmo para abrir un envase TetraBrick Tomar y mantener el envase con la mano izquierda; Aplastar haciendo presi´on la esquina superior donde viene el dibujito de las tijeras; Doblar el cart´ on hacia un lado por la l´ınea punteada; Enderezar de nuevo el cart´ on; Rasgar el cart´ on por la l´ınea punteada en este ejemplo, el ser humano hace de int´erprete y computador. N´otese que cada instrucci´ on de una secuencia ser´a m´as o menos simple dependiendo del ‘ejecutor’. Se supone, en los algoritmos secuenciales que el ejecutor no fallar´a en ninguna de las ejecuciones de las acciones. 3.4.2.

Selecci´ on

¿Qu´e ocurre si pasa algo? Parece una pregunta ambigua, de hecho lo es, pues no hay referencia a nada si no se especifica qu´e es ese ‘algo’. En general, cada acci´on de una secuencia, como dijimos antes, puede ser ese ‘algo’ al que nos referimos. Esto es, nos preguntamos, ¿qu´e ocurre si alguna acci´on puede dar lugar a distintas formas de ejecutarse? Por ejemplo, si sencillamente tratamos de desarrollar un algoritmo para evaluar las dos raices de una ecuaci´on de segundo grado, tendr´ıamos: dados a, b y c de ax2 + bx + c = 0 encontrar los 2 valores de x que lo satisfacen es un problema mal planteado, en general. Si yo tratase de resolver el problema con el mecanismo particular para el caso de dos raices reales, me podr´ıa encontrar con situaciones err´oneas como que a = 0, b2 − 4ac < 0 ´ o a = b = 0, c 6= 0. 2 En los lenguajes declarativos esto no es as´ ı, al menos en teor´ıa, sino que lo que el programador hace es algo m´ as relajadamente, escribir, sin una necesidad tan estricta de orden, sus conocimienos, aserciones, sobre el problema y se deja al ordenador sacar las ‘conclusiones’. En programaci´ on, imperativa, es sin embargo esencial ‘ordenar’ al computador cada acci´ on, una debajo de la otra dici´ endole (como a un subnormal de CI 1) exactamente, qu´ e es lo que debe hacer. Por desgracia, los lenguajes imperativos son los m´ as eficientes y m´ as populares.


3.4

Las estructuras fundamentales de control de flujo

17

Para que los algoritmos tengan validez dentro de multiples situaciones es necesario que puedan tomar decisiones por s´ı mismos. En el ejemplo anterior tendr´ıamos Algoritmo para resolver CUALQUIER ecuaci´ on de segundo grado si a = b = 0 y c = 0 entonces hacer explicar que la ecuaci´ on es absurda; acabar este algoritmo ya aqu´ı; finsi si a = 0 entonces hacer soluci´ on x es -c/b acabar este algoritmo ya aqu´ı; finsi si b*b - 4*a*c < 0 entonces hacer soluci´ on x1 es (-b + i * Raiz(4*a*c-b*b)/(2*a); soluci´ on x2 es (-b - i * Raiz(4*a*c-b*b)/(2*a); sino soluci´ on x1 es (-b + Raiz(b*b-4*a*c)/(2*a); soluci´ on x2 es (-b - Raiz(b*b-4*a*c)/(2*a); finsi Este ejemplo se parece mucho a lo que es un programa de ordenador, tan s´olo que hemos empleado un lenguaje m´ as relajado que el que se emplea en la mayor´ıa de los lenguajes de ordenador. En el ejemplo anterior se toman decisiones. La toma de decisiones permite al algoritmo ser algo m´as ‘inteligente’ siendo capaz de adoptar una secuencia de acciones u otra seg´ un la situaci´on. Muchos problemas no se podr´ıa algoritmizar sino se hiciese uso de decisiones y esto es debido a que muchos problemas carecen de una soluci´on u ´nica, como le sucede al de las ra´ıces de la ecuaci´on de segundo grado, planteado como problema general. La decisi´ on es la instrucci´ on que va a hacer los algoritmos menos mec´anicos, alej´andolos del t´ıpico uso que se hace de una calculadora, donde las decisiones no las puede tomar la m´aquina, sino que debe tomarlas el usuario, el humano. De hecho casi se podr´ıa medir la ‘inteligencia’ de un algoritmo, que no es otra que una ‘instant´anea’ de la inteligencia del programador, como el n´ umero de decisiones en su programa. Por supuesto esto es s´olo una aproximaci´on superficial al tema. Evidentemente, la elecci´ on y el orden adecuado de las instrucciones tambi´en denotan mayor o menor ‘inteligencia’. Pero la variedad de respuestas de un algoritmo que admite multiples entradas da idea de un algoritmo ‘el´ astico’, ‘inteligente’. 3.4.3.

Iteraci´ on

Sin la iteraci´ on es imposible escribir ciertos algoritmos. Veamos un ejemplo: supongamos que queremos describir el proceso de sumar N n´ umeros distintos dados. Si sabemos cu´antos n´ umeros tenemos, esto es, cu´ anto vale N y los n´ umeros en s´ı, parece que una secuencia de sumas bastar´ıa toma el primero como resultado; suma el segundo al resultado; suma el tercero al resultado; ... suma el N-simo al resultado; Pero, supongamos que no sabemos cuanto vale N . ¿Qu´e hacer? No tendr´ıamos forma de describir este proceso sin recurrir a la idea de repetir acciones hasta que se d´e una cierta condici´on. Otro ejemplo, supongamos que tenemos que calcular el n´ umero e mediante un algoritmo num´erico que utilice la serie de Taylor: 1 1 1 e = e1 = 1 + + + + . . . 1! 2! 3!


3.5

Pseudolenguaje (v. C1.0.1)

18

Normalmente, como sabemos, el uso de series polin´omicas para aproximar una funci´on est´a condicionado a la rapidez de convergencia de la serie, esto a cu´antos t´erminos de tal serie son necesarios para llegar a un valor aceptable de la funci´on; y, esto, depende del valor de cada t´ermino, de manera que: hasta que no se tenga un t´ermino de menor valor que la precisi´ on buscada se deben seguir tomando t´erminos. Existen muchos procesos en los que se deben ejecutar acciones mientras no se deje de dar una situaci´ on Ejemplos de tales algoritmos hay muchos: “caminar hasta alcanzar la acera” (´o, “mientras no se alcance la acera”, como se prefiera decir); “sacar monedas mientras no se rebase o iguale la cantidad solicitada”; “acumular t´erminos mientras el valor absoluto del t´ermino sea superior a la precisi´on buscada”; etc. Los bucles, repeticiones o iteraciones son combinaci´on de un grupo de acciones que se pretende que se ejecuten, llamado el cuerpo del bucle y de una condici´ on que se deber´a cumplir (o dejar de cumplir), para que el cuerpo deje de ejecutarse. Gracias a esta combinaci´on se pueden describir much´ısimos procesos comunes. Los ordenadores son capaces de ejecutar bucles, esto es, repetir un juego de acciones hasta que se deje o se cumpla una condici´on predeterminada. La comprensi´ on del mecanismo de los bucles es fundamental para avanzar ante problemas b´asicos de programaci´ on, que son irresolubles sin ellos. Al principio, sin embargo, son algo ‘extra˜ nos’ y dif´ıciles de comprender para los principiantes.

3.5.

Pseudolenguaje (v. C1.0.1)

Para describir una serie de operaciones es necesario conocer el dispositivo que las ejecutar´a. Sin embargo, independientemente de las acciones finales, estamos viendo que se pueden tener sentencias seguidas; condicionar la ejecuci´on de grupos de sentencias y/o repetir la ejecuci´on de grupos de sentencias. Por otra parte en los dispositivos programables como los ordenadores, vamos a utilizar lugares de memoria sobre los que actuar, ley´endolos y escribiendo sobre ellos. Esta informaci´on, datos variables que estar´ an en la memoria del ordenador podr´a estar representando n´ umeros aritm´eticos, o c´ odigos de alg´ un otro tipo de informaci´on. Las posibilidades son ilimitadas. Para describir cada variable en un programa utilizaremos nombres inventados seg´ un el programa. Para controlar la forma de ejecuci´on de las acciones utilizaremos palabras reservadas de alg´ un lenguaje. Inicialmente es pues conveniente establecer alg´ un tipo de codificaci´on sencilla, con palabras f´acilmente comprensibles del lenguaje natural: este es el pseudolenguaje. Expresiones del lenguaje natural que indiquen la forma de las acciones y del control de flujo del programa. Para las variables igualmente se establecen normas para comprender si estamos trabajando con contenidos aritm´eticos, valores constantes, c´odigos de letras ASCII, etc. 3.5.1.

ALGORITMO // Comentarios o bien detr´ as de // o bien /* entre */

Algoritmo identificador declaraciones Inicio acciones Fin. 3.5.2.

DECLARACIONES

Pueden ser de constantes, tipos, variables y funciones. Cada tipo de declaraci´ on ir´ a en su zona, previamente declarada CONST, TIPOS o VAR.


3.5

Pseudolenguaje (v. C1.0.1)

19

Todas las variables, tipos, constantes y funciones, deben ser declaradas antes de su uso. Las constantes, tipos y variables se pueden declarar varias a la vez, separando en la lista los identificadores por comas. Se pone antes el nombre del tipo de datos y despu´es la(s) etiqueta(s) elegida(s) para nombrarla(s) (si son m´as de una, separadas por comas). Las variables pueden ser inicializadas en su declaraci´on (ya est´en definidas por separados o varias a la vez). Las constantes, como las “variables”, se declaran en la zona de CONSTantes pero siempre van inicializadas a su valor fijado. 3.5.3.

TIPOS

Simples predefinidos: Tipo Natural

S´ımbolo N

Entero

Z

Real L´ogico

R B

Letra

C

Operadores + - * DIV(/) MOD(%) + - * DIV(/) MOD(%) ABS + - * / Y(&&) O(||) NO(!) CHAR ORD(N)

Para los tipos cuyos valores tienen todos predecesor y sucesor (ordinales), que son N, Z, B y C, tambi´ en se tiene PRED y SUCC.

Otros tipos: Enumeraci´on (identificaci´ on) de valores: Enumerado {CYAN, MAGENTA, AMARILLO, NEGRO} Colores; Arrays (vectores, matrices, hileras): <TipoBase> <nombre>[indiInicio..indiFinal]; // ej.: int cuenta[1..5] = {10, 4, 5, 5, -4}; Cadenas de letras: char cadena[] = "Hola mundo"; char *cadena = "Hola \"mundo\; char cadena[0..100]; // debe terminarse en \0 Registro Registro <declaraci´ onVariable> {<declaraci´ onVariable>} [<Casos> /* sustituyendo acciones por declaraciones */] finRegistro Puntero <TipoApuntado> *PTI; // NODO *ENLACE, *LINK; // N *arrPunt[1..10]; // array de 10 punteros (raro) NOTA: en pseudolenguaje, el contenido de las estructuras se puede copiar, sin embargo no los arrays. Ejemplos:


3.5

20

Pseudolenguaje (v. C1.0.1)

N *puntero_a_natural; //tipo puntero N array_natural[1..20]; //array de 20 naturales enteros No hacer varias declaraciones en una l´ınea. Utilizar definiciones de tipos y constantes previas. Esto hace m´ as legible y controlable el programa. 3.5.4.

SUBALGORITMOS

ALGORITMO [<TipoQueDevuelve>] identificador( {FormaDePaso <declarVariable>}) <declaraciones> inicio acciones fin identificador Si no devuelve nada se pondr´ a como <TipoQueDevuelve> nulo(void). FormaDePaso puede ser: E, S, o ES que corresponden a entrada, salida y entrada-salida. (NO OLVIDAR PONERLO). 3.5.5.

ACCIONES

Asignaci´ on: a = <expresi´ on>; L´ogica: ==, <, >, !=, <=, >=;

&&, ||, !, Y, O, NO

Selecci´on: si <expresi´ onBooleana> entonces acciones {sinosi <expresi´ onBooleana> entonces acciones} [sino acciones] finsi Iteraci´on: mientras <expresi´ onBooleana> hacer acciones finmientras o bien repetir acciones hasta que <expresi´ onBooleana> Casos especiales con tipos ordinales: Para (for): para <asignaci´ on> hasta <expresi´ on> [paso <constante>] hacer acciones finpara


3.6

Diagramas de Control de Flujo

21

Filtro (Casos): caso <expresi´ on> sea <rango> : acciones {<rango> : acciones} [sino: acciones] fincaso 3.5.6.

(* <rango> puede ser una lista de constantes *)

Prioridad de operadores

1.

Los par´entesis ()

2.

Operador NO(!) (y los operadores + - unarios -3, !fin)

3.

Operadores multiplicativos: * / DIV MOD(%)

4.

Operadores aditivos: + -

5.

Operadores relacionales: < >= > >=

6.

Operadores de igualdad: == !=

7.

Operador AND&& ´ oY

8.

Operador OR(||) u O

3.5.7.

Acciones

Entre las acciones la m´ as importante es la de asignaci´ on: <identVariable> = <expresi´ on>; La asignaci´on es la operaci´ on m´ as importante de los lenguajes imperativos. En ella la parte derecha del operador =, podr´ a ser cualquier expresi´on (constante o variable) y ser´a evaluada y asignada a la variable que haya a la izquierda del operador =. Naturalmente a la izquierda s´olo puede haber el identificador de una variable adecuada. Como por ejemplo. Asignar, para 3.14 a la variable r: r= 3.14;, 3.5.8. r= y= i= s= y=

Ejemplos de modificaci´ on de variables 3.14; r; i + 1; /* incrementa en 1 el valor de la variable i */ s + n; /* aumenta en n el valor de s */ √ sin(3.14)*sqrt(4.0*z) + 1.0; /* y = sin(3,14) × 4z + 1 */

Ejercicio: ¿C´ omo intercabiar´ıa el contenido de dos variables? O sea, si la variable a y b tienen valores (100 y 200, respectivamente, por ejemplo), queremos realizar acciones para que al final a tenga el anterior contenido de b (200) y b el de a (100).

3.6.

Diagramas de Control de Flujo

Especialmente cuando los algoritmos tienen un flujo de control complicado o para evidenciar ciertas partes delicadas son muy utilizadas formas gr´aficas de representaci´on en las que cada estructura de control viene reflejada por un s´ımbolo. Hace dos d´ecadas se empleaban con m´as frecuencia que hoy y se utilizaban m´ as s´ımbolos, pero nosotros simplificaremos a los reflejados en la Figura 3.


3.7

22

Nociones sobre reconocimiento de lenguajes

Comienzo

Acción

acción de I/O

Decisión

Figura 3: Elementos m´ as comunes en los diagramas de flujo de datos. En muchos casos incluso se reducen al de Acci´ on y al de decisi´ on.

En ´el se ven, el s´ımbolo de Comienzo (Fig. 4), que indicar´a el comienzo o fin (es un terminador) de un proceso algor´ıtmico. L´ ogicamente el de comienzo tendr´a una flecha de salida y el de Fin una de entrada.

Figura 4: S´ımbolo de comienzo o fin de un proceso.

El s´ımbolo de Acci´ on (Fig. 5), que junto con el de decisi´on son los m´as usados para todo. Cuando la acci´ on se quiere detallar con m´as subacciones que la compongan, se indica mediante alg´ un s´ımbolo convenido, y se desglosa en otra parte.

Figura 5: S´ımbolo de acci´ on de alg´ un tipo. En principio una acci´ on no descomponible, excepto que se indique lo contrario.

El de operaciones de entrada y salida (Fig. 5) puede ser m´as espec´ıfico indicando si es hacia una impresora, desde un teclado, hacia un disco, etc., pero esto es una convenci´on amplia que se suele establecer en cada grupo de programadores por anticipado. Finalmente la bifurcaci´ on (Fig. 7), que es la m´as flexible, pudiendo bifurcarse hacia atr´as o hacia adelante hacia otros puntos del algoritmo. Pero en programaci´on estructurada s´olo ser´ıa admisible una sencilla bifurcaci´ on hacia atras para indicar un bucle o una decisi´on hacia adelante indicando alternativas, siempre sin cruzar otras l´ıneas de bifurcaci´ on. Ver la Figura 8.

3.7.

Nociones sobre reconocimiento de lenguajes

Supongamos que tenemos un lenguaje generado por la siguiente gram´atica: G = N, T, A, R N = s´ımbolos con <> T = s´ımbolos sin <> A =< sentencia >

yR


3.7

23

Nociones sobre reconocimiento de lenguajes

Figura 6: S´ımbolo para cualquier operaci´ on de entrada y salida. Se indica como una acci´ on especial dado que involucra un cambio en el control del programa.

Figura 7: S´ımbolo de bifurcaci´ on. En el caso de Fortran ten´ıa tres salidas pues las preguntas eran del tipo x < 0, x = 0 ´ o x > 0. A veces se ampl´ıa para m´ ultiples salidas cada una indicando una posible respuesta a la pregunta indicada.

(1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12)

<sentencia> ::= <identificador> <asignaci´ on><expresi´ on> <asignaci´ on> ::= = <expresi´ on> ::= <expresi´ on> <m´ as> <expresi´ on> <expresi´ on> ::= <expresi´ on> <por> <expresi´ on> <expresi´ on> ::= <n´ umero> <expresi´ on> ::= <identificador> <m´ as> ::= + <por> ::= * <n´ umero> ::= <d´ ıgito> {<d´ ıgito>} <d´ ıgito> ::= 0 | 1 | 2 | ...| 9 <letra> ::= a | b | c | ...| z <identificador> ::= <letra> {<letra> | <d´ ıgito>}

Las reglas de esta gram´ atica se pueden dividir en dos partes, obteniendo dos gram´aticas ‘complementarias’: los s´ımbolos terminales de la primera son los s´ımbolos no terminales de la segunda. Siendo la primera (1) (2) (3) (4) (5)

<sentencia> <expresi´ on> <expresi´ on> <expresi´ on> <expresi´ on>

::= ::= ::= ::= ::=

identificador asignaci´ on <expresi´ on> <expresi´ on> m´ as <expresi´ on> <expresi´ on> por <expresi´ on> n´ umero identificador

y la segunda (1) (2) (3) (4) (5) (6) (7)

<asignaci´ on> ::== <m´ as> ::= + <por> ::= * <n´ umero> ::= <d´ ıgito> {<d´ ıgito>} <d´ ıgito> ::= 0 | 1 | 2 | ...| 9 <letra> ::= a | b | c | ...| z <identificador> ::= <letra> {<letra> | <d´ ıgito>}

SI

?

NO

>0

x? =0

<0

x?

Situación?

A

B

C

D

E

Figura 8: Algunas variantes que indican bifurcaciones.

F


3.7

24

Nociones sobre reconocimiento de lenguajes

Supongamos ahora que en nuestro fichero fuente (programa escrito en nuestro lenguaje ejemplo) est´a escrita la siguiente frase: nuevo= viejo + razon * 23 Nuestro compilador deber´ a analizar esa frase (leerla y “entenderla”) y generar el c´odigo m´aquina u objeto correspondiente (s´ıntesis). En esquema, un compilador realiza una serie de fases como las representadas en la Figura en Programa fuente

A N Á L I S I S

S I N T E S I S

Análisis lexicográfico Análisis sintáctico

rutinas de errores

Análisis semántico gestión de tablas

generación de código intermedio

Tablas de símbolos otras tablas

Optimización de código Generación de código

Programa objeto

1 2 n

Enlazado o linkado

Ejecutable

Figura 9: Fases de un compilador

las que: An´ alisis lexicogr´ afico Utilizando la parte de la gram´atica que describe los s´ımbolos no t´erminales directamente en funci´ on de los terminales, genera una serie interconectada de s´ımbolos, denominados ‘tokens’, para la fase siguiente. Adem´as elimina comentarios, espacios en blanco, separadores, tabuladores, localiza s´ımbolos no en el alfabeto y dem´as errores lexicogr´aficos que se detecten. An´ alisis sint´ actico A partir de los tokens anteriores analiza la estructura de las frases y comprueba la gram´atica del lenguaje. An´ alisis sem´ antico Comprueba que una frase, correcta sint´acticamente, tiene sentido sem´antico. Por ejemplo, no se puede sumar un objeto de tipo ASCII a uno de tipo entero, aunque la expresi´ on de la suma, la frase, estuviese bien planteada. Generaci´ on de c´ odigo intermedio Se genera un c´odigo independiente de la m´aquina de destino. Este c´ odigo es a´ un de relativo alto nivel y f´acil de generar para el lenguaje de trabajo. Depende de la arquitectura del compilador. En nuestro ejemplo:


3.8

25

Ejercicios

temp1= 23 temp2= id3 * temp1 temp3= id2 + temp2 id1= temp3 Optimizaci´ on de c´ odigo Se eliminan redundancias de operaciones y, quiz´as de datos, seg´ un se programe el compilador. Se hacen optimizaciones en los c´alculos, bucles, etc. En nuestro caso: temp1= id3 * 23 id1= id2 + temp1 Generaci´ on de c´ odigo Se genera, a partir del c´odigo intermedio, prob´ablemente optimizado, el c´odigo final para la m´ aquina. En nuestro caso (pas´ andolo a ensamblador, cercano al lenguaje m´aquina): MOVE MUL MOVE ADD MOVE

id3, R1 23, R1 id2, R2 R1, R2 R1, id1

Enlazado o linkado Aunque ya no es una fase propiamente de la compilaci´on, es la fase final de muchos compiladores para dejar el c´odigo ejecutable. En ella se unen todos los ficheros objeto generados en diferentes etapas de compilaci´on. En este enlazado se calculan las direcciones de cada rutina que quedar´ a en cada diferente m´odulo.

3.8.

Ejercicios

. 1 ÂżQu´e lenguaje genera la gram´ atica siguiente? G = {T, N, P, I} T = a, b N =S I=S P = S ::= ab|aP b| . 2 Dado el lenguaje L = {0m 1|m â&#x2030;Ľ 0}, encontrar la gram´atica, en notaci´on BNF, que lo genera. . 3 Dado el lenguaje L = {an |n á 3}, encontrar la gram´atica, en notaci´on BNF, que lo genera.3 . 4 Dado el lenguaje L = {an bm |m > n}, encontrar la gram´atica, en notaci´on BNF, que lo genera. . 5 Proponer una gram´ atica en notaci´ on BNF que genere el siguiente lenguaje: L = {anâ&#x2C6;&#x2019;1 bn+1 }. . 6 Construye la gram´ atica que generar´Ĺa el lenguaje L = {an cb3n |n â&#x2030;Ľ 0}. . 7 DiseË&#x153; na una gram´ atica que genere un lenguaje formado por palabras que contengan u ´nicamente las letras a y b en cualquir orden, pero de forma que en cada palabra haya el mismo n´ umero de as que de bs. Por ejemplo, las palabras abab, aabb, babaab pertenecen al lenguaje, pero ababa no ya que en ella hay 3 apariciones de a y 2 de b. 3 El

s´Ĺmbolo a á b indica que b divide a a.


26

REFERENCIAS

. 8 Definir en BNF o mediante diagramas sint´acticos la gram´atica que genere las sentencias del siguiente lenguaje L = {an b2n c3n |n ≥ 0}. Las sentencias de este lenguaje son aquellas que est´an compuestas por una serie de letras a seguidas por el doble de letras b y terminadas por el triple de letras c; como por ejemplo abbccc, aabbbbcccccc, etc. . 9 Construir una gram´ atica capaz de generar un lenguaje en el que no existiesen las cadenas ‘abc’, pero s´ı cualesquiera otras de cualquier longitud. Por ejemplo, correcto: ‘xxaaacbbb’, incorrecto: ‘mnoabcquenoqueno’. 3.8.1.

Referencias de consulta

Para el tema de los tipos de lenguaje se ha seguido el cap´ıtulo primero de la excelente obra [PZ96] y tambi´en el primero de [AV97]. El cap´ıtulo 6 de [AV97] es una buena referencia para la teor´ıa de gram´aticas. El cap´ıtulo 2 de [Pit92] es totalmente aprovechable. As´ımismo el cap´ıtulo 3 de [Ben90].

Referencias [AV97] Doris Appleby and Julius J. VandeKopple. Programming Languages. Paradigm and Practice. McGraw-Hill, 2nd edition edition, 1997. [Ben90] J.P. Bennett. Introduction to Compiling Techniques. McGraw-Hill, 1990. [Cho56] N. Chomsky. Three models for the description of language. IRE Trans. Inf. Theory, 2(2):113–124, 1956. [Cho63] N. Chomsky. Handbook of Mathematical Psichology, chapter 2. Formal properties of grammars, pages 323–418. John Wiley, and Sons, New York, 1963. [Pit92]

Thomas Pittman. The Art of Compiler Design. Prentice-Hall International, Inc., 1992.

[Pos43] E. Post. Formal reductions of the general combinatorial decision problems. Am. J. Math., 65:197–215, 1943. [PZ96]

Terrence W. Pratt and Marvin V. Zelkowitz. Programming Languages. Design and Implementation. Prentice-Hall, 2nd. edition edition, 1996.

[Sam69] J. Sammet. Programming Languages: History and fundamentals. Prentice-Hall, 1969.

Juan Falgueras Dpto. Lenguajes y Ciencias de la Computaci´ on Universidad de M´ alaga Despacho 3.2.32


algoritmos  

Estructuras de control

Advertisement
Read more
Read more
Similar to
Popular now
Just for you