viernes, 17 de febrero de 2017

1.5 Tipos de Datos

Los datos más básicos para cualquier problema de programación son los valores numéricos y booleanos, como criterio general hay que tener en cuenta todos los tipos numéricos existentes en Haskell, así como el tipo de los booleanos, son ejemplares de la clase de tipos Eq, Ord, Read y Show, en particular, las operaciones de comparación (==), (/=), (<), (>=) y (>) y se pueden utilizar para comparar valores de cualquier tipo numérico, así como valores booleanos. las funciones Max y min también se pueden aplicar a valores de todos estos tipos. 

Números enteros.

En Haskell existen dos tipos de números enteros: el tipo Int de los enteros de precisión limitada, y el tipo Interger de los enteros de precisión arbitraria.

Paradigma de programación

Un paradigma de programación es una propuesta tecnológica que es adoptada por una comunidad de programadores cuyo núcleo central es incuestionable en cuanto a que unívocamente trata de resolver uno o varios problemas claramente delimitados. La resolución de estos problemas debe suponer consecuentemente un avance significativo en al menos un parámetro que afecte a la ingeniería de software. Tiene una estrecha relación con la formalización de determinados lenguajes en su momento de definición. Un paradigma de programación está delimitado en el tiempo en cuanto a aceptación y uso ya que nuevos paradigmas aportan nuevas o mejores soluciones que la sustituyen parcial o totalmente.

Ejemplo: 
Probablemente el paradigma de programación que actualmente es el más usado a todos los niveles es la orientación a objeto. El núcleo central de este paradigma es la unión de datos y procesamiento en una entidad llamada "objeto", relacionable a su vez con otras entidades "objeto". 


Tipos de paradigmas de programación

Programación imperativa 

La programación imperativa, en contraposición a la programación declarativa es un paradigma de programación que describe la programación en términos del estado del programa y sentencias que cambian dicho estado. Los programas imperativos son un conjunto de instrucciones que le indican al computador cómo realizar una tarea.

Los lenguajes imperativos de alto nivel usan variables y sentencias más complejas, pero aún siguen el mismo paradigma. Las recetas y las listas de revisión de procesos, a pesar de no ser programas de computadora, son también conceptos familiares similares en estilo a la programación imperativa; cada paso es una instrucción, y el mundo físico guarda el estado (Zoom).  Los primeros lenguajes imperativos fueron los lenguajes de máquina de los computadores originales. En estos lenguajes, las instrucciones fueron muy simples, lo cual hizo la implementación de hardware fácil, pero obstruyendo la creación de programas complejos. Fortran, cuyo desarrollo fue iniciado en 1954 por John Backus en IBM, fue el primer gran lenguaje de programación en superar los obstáculos presentados por el código de máquina en la creación de programas complejos. 

Programación lógica

La programación lógica consiste en la aplicación del corpus de conocimiento sobre lógica para el diseño de lenguajes de programación; no debe confundirse con la disciplina de la lógica computacional, es un tipo de paradigmas de programación dentro del paradigma de programación declarativa. El resto de los sus paradigmas de programación dentro de la programación declarativa son: programación funcional, programación basada en restricciones, programas DSL (de dominio específico) e híbridos. La programación lógica gira en torno al concepto de predicado, o relación entre elementos.

Programación funcional

Es un paradigma de programación declarativa basado en la utilización de funciones aritméticas que no maneja datos mutables o de estado. Enfatiza la aplicación de funciones, en contraste con el estilo de programación imperativa, que enfatiza los cambios de estado. La programación funcional tiene sus raíces en el cálculo lambda, un sistema formal desarrollado en los 1930s para investigar la definición de función, la aplicación de las funciones y la recursión. Muchos lenguajes de programación funcionales pueden ser vistos como elaboraciones del cálculo lambda, La programación dirigida por eventos es la base de lo que llamamos interfaz de usuario, aunque puede emplearse para desarrollar interfaces entre componentes de Software como módulos del núcleo también.

Programación orientada a objetos

La programación orientada a objetos o POO (OOP según sus siglas en inglés) es un paradigma de programación que usa objetos y sus interacciones, para diseñar aplicaciones y programas informáticos. Está basado en varias técnicas, incluyendo herencia, abstracción, polimorfismo y encapsulamiento. Su uso se popularizó a principios de la década de los años 1990. En la actualidad, existe variedad de lenguajes de programación que soportan la orientación a objetos.

Programación con restricciones

Es un paradigma de la programación en informática, donde las relaciones entre las variables son expresadas en términos de restricciones (ecuaciones). Actualmente es usada como una tecnología de software para la descripción y resolución de problemas combinatorios particularmente difíciles, especialmente en las áreas de planificación y programación de tareas (calendarización). La programación con restricciones se relaciona mucho con la programación lógica y con la investigación operativa. De hecho, cualquier programa lógico puede ser traducido en un programa con restricciones y viceversa. Muchas veces los programas lógicos son traducidos a programas con restricciones debido a que la solución es más eficiente que su contraparte.  La diferencia entre ambos radica principalmente en sus estilos y enfoques en el modelado del mundo. Para ciertos problemas es más natural (y por ende más simple) escribirlos como programas lógicos, mientras que en otros es más natural escribirlos como programas con restricciones. 

Programación orientada a componentes

La programación orientada a componentes (que también es llamada basada en componentes) es una rama de la ingeniería del software, con énfasis en la descomposición de sistemas ya conformados en componentes funcionales o lógicos con interfaces bien definidas usadas para la comunicación entre componentes. Se considera que el nivel de abstracción de los componentes es más alto que el de los objetos y por lo tanto no comparten un estado y se comunican intercambiando mensajes que contienen datos.

Fokker, J. (1996). Programaci´on Funcional. Madrid: Universidad Carlos III de Madrid.
maldonado, m. (12 de febrero de 2014). programación logica y funcional.

1.4 Disciplina de Tipos

En los lenguajes de programación con disciplina de tipos, cada tipo representa una colección de valores (datos) similares. Una función cuyo tipo sea A1 -> ... An -> espera n parámetros con tipos A1, ... An y devuelve un resultado de tipo R. El conocer los tipos de las funciones ayuda a documentar los programas y a evitar errores en tiempo de ejecución.
Disciplina estática de tipos: Los programas bien tipados se pueden reconocer en tiempo de compilación, un programa bien tipado se puede utilizar sin efectuar comprobaciones de tipo en tiempo de ejecución. Estando garantizado que no se producirán errores de tipo durante el cómputo.

Clases de errores

 Al escribir una función se pueden cometer errores. El intérprete puede avisar de algunos errores. Si la definición de una función no es correcta, el intérprete da un mensaje en el momento de analizar la definición. Por ejemplo, la siguiente definición contiene un error:

esCero x = x=0
El segundo = tenía que ser el operador == (= significa ‘se define como’ y == significa ‘es igual a’). Al analizar esta definición el intérprete dice:
Reading script file "nuevo":
Parsing...............
ERROR "nuevo" (line 18): Syntax error in input (unexpected ‘=’)

El intérprete encuentra los errores de sintaxis en el primer paso del análisis: el análisis sintáctico (en inglés: parsing). Otros errores pueden ser un paréntesis izquierdo sin su paréntesis derecho correspondiente, o el uso de palabras reservadas (como where) en lugares en los que no se permiten. Además de los errores de sintaxis, existen otros errores que el intérprete puede encontrar. Una posibilidad es usar una función que no está definida. Por ejemplo, con:

fac  x  =  producto  [1..x]

el int´erprete dice:
Reading script file "nuevo":
 Parsing..............
 Dependency analysis....................................
ERROR "nuevo" (line 19): Undefined variable "producto"

Estos errores se encuentran durante el segundo paso: el análisis de dependencia (dependency analysis). El siguiente obstáculo en el análisis es la comprobación de los tipos (type checking). Por ejemplo, funciones que operan sobre números no pueden ser usadas con valores booleanos y tampoco con listas. Con la expresión 1+True en la definición de una función el intérprete dice:

Reading script file "nuevo":
Parsing...............
Dependency analysis...
Type checking......

ERROR "nuevo" (line 22): Type error in application
*** expression            : 1 + True
*** term                       : 1
*** type                        : Int
*** does not match : Bool

La subexpresón (term) 1 tiene el tipo Int (entero). No se puede sumar tal valor a True, que es de tipo Bool.
Otros errores de tipado se producen si se aplica la función length a algo diferente de una lista, como en length 3:

ERROR: Type error in application
*** expression : length 3
*** term : 1
*** type : Int
*** does not match : [a]

Sólamente si no existen errores de tipado en el programa, el intérprete ejecuta el cuarto paso (generación de código).

El intérprete da todos los mensajes de error en el momento de analizar una función. En algunos otros lenguajes se comprueba el tipado en el momento en que se llama a una función. En este caso nunca se está seguro si existen errores de tipado o no. Si el intérprete de Gofer no da mensajes de error, se puede estar seguro de que no existen errores de tipado.

Pero, si el intérprete de Gofer no da mensajes de error, es posible todavía encontrar errores en el programa. Si se pone (por ejemplo) en la definición de la función suma el signo menos en vez del signo más, el programa no funciona bien, pero el intérprete no lo sabe. Estos errores, ‘errores lógicos’, son los más difíciles de encontrar.

Expresiones de tipo

El tipo de una expresión se puede determinar con el comando del intérprete: type seguido de la expresión de la que se quiere conocer el tipo. Por ejemplo:

? :type 3+4
3 + 4 :: Int

El símbolo: significa: ‘es del tipo’. La expresión no se evalúa con el comando: type, solamente se determina el tipo.
Existen cuatro tipos estándar:
• Int: el tipo de los enteros;
• Float: el tipo de los números de punto flotante (floating-point);
• Bool: el tipo de los valores booleanos: True y False; pág. 49
• Char: el tipo de los caracteres (se tratará en el párrafo 3.2.2).

También las funciones tienen un tipo. El tipo de una función está determinado por el tipo del parámetro y el tipo del resultado. Por ejemplo, el tipo de la función sum es:

? :type sum
sum :: [Int] -> Int
La función sum opera sobre listas de enteros y como resultado devuelve un solo entero. El símbolo -> en el tipo de la función representa una flecha (→). Otros ejemplos de tipos de funciones son:

sqrt :: Float -> Float
even :: Int -> Bool
sums :: [Int] -> [Int]

Se suele decir: “even tiene el tipo de int a bool”.

Polimorfismo

Para algunas funciones sobre listas no interesa el tipo de los elementos de la lista. La función estándar length puede determinar el tamaño de una lista de enteros, pero también de una lista de valores booleanos, y –por qué no– de una lista de funciones. El tipo de la función length se define como sigue:

length :: [a] -> Int

Este tipo indica que esta función se aplica sobre una lista, pero que el tipo de los elementos de esta lista no importa. Este tipo se indica por medio de una variable de tipo, en el ejemplo se utiliza la letra a. Variables de tipo se escriben con una letra minúscula, al contrario que los tipos fijos como Int y Bool. La función head, que devuelve el primer elemento de una lista, tiene el tipo:

head :: [a] -> a


Esta función se aplica también a listas, sin que importe cual sea el tipo de los elementos. Pero su resultado es del tipo de los elementos de la lista (es el primer elemento de la lista). Por tanto, se usa para el tipo del resultado la misma variable que para el tipo de los elementos de la lista. Un tipo que contiene variables de tipo, se llama tipo polimórfico. Las funciones con un tipo polimórfico se llaman funciones polimórficas. El fenómeno en si se llama polimorfismo. Las funciones polimórficas, como length y head, tienen en común que usan solamente la estructura de una lista. Una función no polimórfica como sum, usa también propiedades de los elementos de la lista (como la posibilidad de sumarlos, etc.).

Fokker, J. (1996). Programacion Lógica y funcional. Madrid: Copyright.


1.3 Definición de funciones


En las funciones que hemos visto, los parámetros son números, valores booleano solistas. Pero un parámetro de una función puede también ser una función. Un ejemplo es la función map. Tiene dos parámetros: una función y una lista. La función map aplica la función parámetro a todos los elementos de la lista. Por ejemplo: 

? map fac [1,2,3,4,5]
 [1, 2, 6, 24, 120]
? map sqrt [1.0,2.0,3.0,4.0]
 [1.0, 1.41421, 1.73205, 2.0]
? map even [1...8]
 [False, True, False, True, False, True, False, True]

Existen muchas funciones en Gofer que tienen funciones como parámetro. En el capítulo 2 se describen pág. 23 más funciones de este tipo.

Definición por combinación

La manera más fácil de definir funciones es por combinación de otras funciones:
fac n = product [1...n]
 impar x = not (even x)
 cuadrado x = x*x

suma de cuadrados lista = sum (map cuadrado lista)
Las funciones pueden tener más de un parámetro:
comb n k = fac n / (fac k * fac (n-k))
formula ABC a b c = [ (-b+sqrt(b*b-4.0*a*c)) / (2.0*a), (-b-sqrt(b*b-4.0*a*c)) / (2.0*a) ]

Las funciones sin parámetros se llaman normalmente constantes:
pi = 3.1415926535
e = exp 1.0

Toda definición de función tiene por tanto la siguiente forma:

• el nombre de la función
• los nombres de los parámetros (si existen)

• el símbolo = 
• una expresión, que puede contener los parámetros, las funciones estándar y otras funciones definidas.

Una función que tiene un valor booleano como resultado, tiene a la derecha del símbolo = una expresión con un valor booleano:

negativo x = x < 0
 positivo x = x > 0
 es Cero x = x == 0

Note la diferencia en el anterior ejemplo entre = y ==. El símbolo = separa la parte izquierda de la parte derecha de la dentición. El símbolo == es un operador, como < y >.

En la definición de la función formula ABC se encuentran las expresiones sqrt (b*b-4.0*a*c) y (2.0*a) dos veces. Esto da mucho trabajo para teclear, pero calcular estas expresiones exige también demasiado tiempo: se calcula dos veces la misma expresión. Para evitar eso, se puede dar (en Gofer) un nombre a (sub)expresiones. La definición mejorada sería formula ABC’:

Formula ABC’ a b c = [ (-b+d)/n
                                   , (-b-d)/n
                                   ]
                                   where d = sqrt (b*b-4.0*a*c)  
                                                           n = 2.0*a

Con la opción de estadística del interprete (el comando: set +s) se puede ver la diferencia muy bien:

? formula ABC’ 1.0 1.0 0.0
 [0.0, -1.0]
(18 reductions, 66 cells)
? formula ABC 1.0 1.0 0.0
 [0.0, -1.0]
(24 reductions, 84 cells)

La palabra where no es el nombre de una función. Es una de las palabras reservadas que están en la lista de la sección 1.3.2. Detrás de ‘where’ hay algunas definiciones. En el caso del ejemplo, las definiciones de las constantes d y n. Estas constantes se pueden usar en la expresión anterior a la parte de where. Fuera de este lugar no se pueden usar: son definiciones locales. Parece tal vez extraño que se llamen a d y n ‘constantes’, porque el valor puede ser diferente para cada vez que se llama a la función formula ABC’. Pero durante la computación de una llamada a formula ABC’, con valores para a, b y c, aquellos valores son constantes.

Definición por distinción de casos

Algunas veces es necesario distinguir diferentes casos en la definición de una función. Un ejemplo es la función abs: con un parámetro negativo la definición es diferente que con un parámetro positivo. Esto se anota en Gofer de la siguiente manera:

abs x | x<0 = -x
         | x>=0 = x

También, se pueden distinguir más de dos casos. Por ejemplo, en la función signum:

signum x | x>0 = 1
                | x==0 = 0
                | x<0 = -1

Las definiciones de los diferentes casos son precedidas por expresiones booleanas, que se llaman guardas. Si se llama a una función definida de esta forma, se tratan las guardas una a una, hasta que se encuentre una guarda con el valor True. Entonces, se evalúa la expresión a la derecha del símbolo =. En ocasiones la última guarda es True o la constante otherwise. Por tanto, la descripción de la forma de la definición de una función es más amplia que la explicada en la sección anterior. Una descripción más completa de la definición de una función es:

 • el nombre de la función;
 • los nombres de los parámetros (si existen);
 • el símbolo = y una expresión, o una o más expresiones guardadas;
• si se desea, la palabra where seguida de definiciones locales.

En cada expresión guardada debe existir el símbolo |, una expresión booleana, el símbolo = yunaexpresi´on2. Esta descripción de funciones todavía no está completa...

Definición por análisis de patrones

Se llaman parámetros formales a los parámetros de una función en la definición de la misma, por ejemplo x e y en

f x y = x * y

En una llamada a la función se dan parámetros actuales a la función. Por ejemplo, en la llamada

f 17 (1+g 6)

17 es el parámetro actual que corresponde a x, (1+g 6) es el parámetro actual que corresponde a y. Los parámetros actuales se sustituyen en los lugares de los parámetros formales cuando se llama a una función. El resultado del anterior ejemplo es por tanto 17*(1+g 6). Por tanto, los parámetros actuales son expresiones. Los parámetros formales eran hasta ahora solamente nombres. En la mayoría de los lenguajes de programación, el parámetro formal tiene que ser un nombre. Existen otras posibilidades en Gofer: un parámetro formal puede ser también un patrón. Un ejemplo de la definición de una función en que se usa un patrón como parámetro formal es:

f [1,x,y] = x+y

Esta función es correcta solamente si el parámetro es una lista de exactamente tres elementos, con el primer elemento 1. La función suma el segundo y el tercer elemento. La función entonces, no está definida para listas más pequeñas o más grandes, o para listas que tienen otro elemento que 1 como primer elemento. (Es normal que una función no esté definida para todos los parámetros actuales que son posibles. Por ejemplo, la función sqrt no está definida para parámetros negativos y el operador / no está definido con 0 como segundo parámetro.) Se pueden definir funciones con diferentes patrones como parámetro formal:

suma [] = 0
suma [x] = x
suma [x,y] = x+y
suma [x,y,z] = x+y+z

Esta función se puede aplicar a listas con cero, uno, dos o tres elementos (en la siguiente sección se define esta función para listas con cualquier tamaño). En todos los casos la función suma los elementos. Llamando a la función, el intérprete ve si el parámetro cumple con una de las definiciones. La llamada suma [3,4] cumple con la tercera definición. 3 corresponde al parámetro x en la definición y 4 a y. Las siguientes construcciones se pueden utilizar como patrón:

• Números (por ejemplo 3);
• Las constantes True y False;
• Nombres (por ejemplo, x);
• Listas cuyos elementos también son patrones (por ejemplo [1,x,y]);
• El operador: con patrones a la izquierda y a la derecha (por ejemplo a:b);
• El operador + con un patrón a la izquierda y un entero a la derecha (por ejemplo n+1);
• El operador * con un patrón a la derecha y un entero a la izquierda (por ejemplo 2*x).

Con la ayuda de los patrones se pueden definir varias funciones importantes. Por ejemplo, el operador && del preludio:

False && False = False
False && True = False
True && False = False
 True && True = True

Con el operador: se pueden construir listas. La expresión x:y significa: coloca el elemento x delante de la lista y. Al incluir el operador: se separa el primer elemento del resto de la lista. Con esto podemos escribir dos funciones estándar que son bastante útiles:

head (x:y) = x
tail (x:y) = y

La función head (cabeza) devuelve el primer elemento de una lista; la función tail (cola) devuelve todo excepto el primer elemento. Usos de estas funciones en expresiones son, por ejemplo:

? head [3,4,5]
3
? tail [3,4,5]
 [4, 5]

Estas funciones se pueden aplicar a casi todas las listas, solamente están indefinidas para la lista vacía (la lista sin elementos). En los patrones en que ocurren los operadores + o *, las variables tienen que ser de enteros. Por ejemplo, se puede definir una función par que devuelve True si el número es par:

par (2*n) = True
par (2*n+1) = False

Con la llamada par 5, solamente se cumple el segundo patrón (en este caso n representa 2). No se cumple el primer patrón puesto que en ese caso n tendría que ser 2.5, que no es un entero.

Definición por recursión o inducción

En la definición de una función se pueden usar las funciones estándar y las funciones definidas por el usuario. Pero también se puede usar la propia función que se define en su definición. A tal definición se la llama definición recursiva. La siguiente función es una función recursiva:

f x = f x

El nombre de la función que se define (f), está en la expresión que define la función, a la derecha del símbolo =. Sin embargo, esta definición no tiene sentido. Para calcular el valor de f 3, se tiene que calcular el valor de f 3 primero y para eso, se tiene que calcular el valor de f 3, etc... Las funciones recursivas tienen sentido bajo las siguientes dos condiciones:

• el parámetro de la llamada recursiva es más simple que el parámetro de la función que se quiere definir (por ejemplo, numéricamente menor o una lista más corta);

• existe una definición no recursiva para un caso base. Una definición recursiva de la función factorial es:
fac n | n==0 = 1

 | n>0 = n * fac (n-1)

Aquí el caso base es n==0; en este caso se puede calcular el resultado directamente (sin recursión). En el caso n>0 existe una llamada recursiva, es decir fac (n-1). El parámetro de esta llamada (n-1) es menor que n. Otra manera de distinguir estos dos casos (caso base y caso recursivo) es usando patrones:

fac 0 = 1
fac (n+1) = (n+1) * fac n

También en este caso el parámetro de la llamada recursiva (n) es menor que el parámetro de la función que se quiere definir (n+1). El uso de patrones tiene mucha relación con la tradición matemática del usar inducción. Por ejemplo, la definición matemática de la función potencia puede ser usada casi directamente como función en Gofer:

x ^ 0 = 1
x ^ (n+1) = x * x^n

Una definición recursiva en que se usan patrones para distinguir diferentes casos (en lugar de expresiones booleanas) se llama también definición inductiva. Las funciones sobre listas también pueden ser recursivas. Una lista es ‘menor’ que otra si tiene menos elementos. La función suma que hemos visto en la sección anterior, puede ser definida de diferentes maneras. Una definición recursiva con expresiones booleanas es:

suma lista | lista==[] = 0
                 | otherwise = head lista + suma (tail lista)

Pero también es posible obtener una versión inductiva (usando patrones):

suma [] = 0
suma (cabeza: cola) = cabeza + suma cola

En la mayoría de los casos, es más clara una definición con patrones, porque las diferentes partes en el patrón pueden conseguir un nombre directamente (como cabeza y cola en la función suma). En la versión recursiva de suma se necesitan las funciones estándar head y tail para distinguir las diferentes partes en lista. En estas funciones también se usan patrones. La función estándar length, que calcula el número de elementos de una lista, puede ser definida inductivamente:

length [] = 0
 length (cabeza: cola) = 1 + length cola

En este caso, el valor del primer elemento de la lista no importa, solamente importa que exista un primer elemento. En patrones se permite usar el símbolo ‘_’ en este tipo de casos en vez de un nombre:

length [] = 0
length (_:cola) = 1 + length cola

Indentacion y comentarios

En la mayoría de los lugares de un programa, se pueden poner más blancos para hacer el programa más claro. En los anteriores ejemplos, hemos añadido más espacio, para ordenar las definiciones de funciones. Está claro que no se pueden poner blancos en un nombre de una función o en un número. len gth es algo diferente de length, y 1 7 es algo diferente de 17. También se puede añadir una nueva línea para hacer el resultado claro. Hemos hecho esto en la definición de formula ABC, porque sin ello la línea sería muy larga. Pero, a diferencia de otros lenguajes de programación, una nueva línea tiene un significado. Véanse por ejemplo las siguientes construcciones:

where                           where
a = f x y                        a = f x
b = g z                         b = g z y

Entre estas dos construcciones existe bastante diferencia. En una serie de definiciones, Gofer usa la siguiente manera para decidir que es parte de que:

• una línea inventada exactamente tanto como la línea anterior se considera una nueva definición.
• una línea inventada más que la anterior, es parte de la línea anterior.
 • una línea inventada menos que la anterior, no es parte de las definiciones anteriores.

El último caso es importante si la construcción de where está en otra definición de where. Por ejemplo, en
f x y = g (x+w)
           where g u = u + v
                            where v = u * u
                                            w = 2 + y

w es una declaración local de f, y no de g. La definición de w está a la izquierda de la definición de v; por tanto, no forma parte de la construcción del where de g. Deja tanto espacio libre como la definición de g, por tanto, es parte de la construcción del where de f. Si dejara menos espacio libre, no sería parte de tal construcción. En este caso, Gofer daría un mensaje de error. Tal vez estas reglas parezcan un poco complicadas, pero no es dificil si se usa la siguiente regla global:

definiciones equivalentes están igualmente inventadas

Por tanto, todas las definiciones de funciones tienen que empezar en la misma columna (por ejemplo, todas en la posición cero).

Comentarios En cualquier lugar donde se puedan poner blancos, se puede poner un comentario. El intérprete ignora todo comentario. ´Este solamente es u´til para los lectores del programa. Existen dos tipos de comentarios en Gofer:

• con los símbolos -- empieza un comentario, que termina al finalizar la línea.
• con los símbolos {- empieza el comentario, que termina con los símbolos -}.

Una excepción a la primera regla es el caso que -- sea parte de un operador, por ejemplo, en <-->. La combinación -- fue reservada (en el párrafo 1.3.2).

Los comentarios con {- y -} se pueden anidar. Cada comentario termina con el correspondiente símbolo de terminación. Por ejemplo, en

{- {- hola -} f x = 3 -}

no se define una función f, todo es comentario.


Fokker, J. (1996). Programación Funcional (primera edicion septiembre 1992 ed.). (H. R. J., Ed., & p. M. simón, Trad.) madrid: Universidad Carlos III de Madrid.

1.2 Evaluación de Expresiones


La computadora evalúa las expresiones de a un operador a la vez. Si en una expresión hay dos multiplicaciones y una suma (por ejemplo 4*5 + 2*3) no puede realizar las dos multiplicaciones a la vez, por más que los resultados sean independientes entre si y necesita seguir un orden.


Para evaluar expresiones con varios elementos se suele utilizar una estructura tipo pila, en la que se colocan los números y las operaciones se realizan sobre los últimos números que se ubicaron, reemplazando a estos por el resultado obtenido de la operación. Por ejemplo, para realizar 4+5+1, podemos ir agregando los números en orden a la pila (Tabla 3).


Posteriormente y con los elementos en la pila realizamos las dos operaciones de suma, como se puede observar en la Tabla 4:


Cada operación se realizó sobre los dos números superiores de la pila y reemplazó esos dos elementos por el resultado de la operación. Se hizo decrecer la pila por medio de las operaciones hasta llegar a un solo valor, que es el resultado de toda la expresión.
Una forma de escribir esta serie de operaciones sobre una pila, es con la notación Polaca Inversa, que es la notación que usan algunas calculadoras científicas, sobre todo las de la marca Hewlett – Packard. En ese sentido, la expresión anterior en notación polaca inversa es:

4 5 1 + +

Cada número que aparece se ingresa en la pila y las operaciones trabajan sobre los dos números superiores de la pila. Esta notación es práctica para indicar operaciones que queremos realizar en un orden determinado. Por ejemplo (B+1) * A *(C+2), se escribe en esta notación:

B 1 + A * C 2 + *

Es una forma más directa, pero expresa exactamente lo mismo que la expresión matemática con paréntesis. De esta manera, por medio de una pila la computadora realiza las operaciones, pero por supuesto, las expresiones no se escriben así en Basic, sino que usan la notación común. Es necesario llevar las expresiones a esta forma y es el trabajo del compilador hacerlo.

Por lo tanto, la evaluación de una expresión consiste en obtener el resultado de la misma mediante la aplicación sucesiva de los operadores que la forman. Para ello, se debe utilizar el orden de prioridad de la sección anterior y la semántica de cada operador implicado.

Para mostrar un ejemplo más de evaluación consideremos la siguiente expresión en Pascal:


Donde X, Y y Z son variables de tipo integer cuyos valores antes de efectuar la evaluación son respectivamente 5, -2 y 10. El lector puede comprobar que la expresión anterior esta bien formulada, es decir, cada operador actúa sobre operandos de tipos compatibles con el mismo.

El proceso de evaluación de una expresión como la anterior se realizará según el siguiente algoritmo:

1   1.   Aplicar secuencialmente y según el orden de prioridad definido en la sección anterior los operadores que aparezcan en la expresión. Cada uno de estos operadores se ejecutará como sigue:
2.      
     2.  Aplicar las indirecciones sobre todas las variables que aparezcan en la subexpresión que afecta al operador. En otras palabras, reemplazar cada variable por su valor actual. A modo de ejemplo considere la siguiente expresión:


1.    3.   Realizar todas las llamadas de función implicadas y reemplazar dichas llamadas por los valores devueltos. En el caso de que existan llamadas anidadas, como por ejemplo sqr(ord(´A´)), se realizaran primero las llamadas más interiores.


La siguiente secuencia de ejecución muestra la evaluación paso a paso de la expresión anterior. Obsérvese el orden y forma en que se ejecutan los operadores de dicha expresión. 


En este ejemplo, se ha subrayado la subexpresión que está siendo evaluada después de haber realizado los paso1 y 2 para cada operador. Cada subexpresión es entonces sustituida por su valor en el siguiente paso. Es importante resaltar el hecho de que no se ha especificado el orden en que se aplican los pasos 1 y 2 a los operandos. Este orden depende del compilador y por tanto las expresiones se deben escribir sin suponer ningún orden correcto.



Marcos Leguizamón. (2012). Lenguajes Basic en las Home Computers. REVISTA DE TECNOLOGÍA E INFORMÁTICA HISTÓRICA, Volumen 2 , p. 56-75.