lpp_teoria

30
The Scheme-book Lenguajes y paradigmas de la programaci´ on Curso 2004/2005 Jos´ e P´ erez Mart´ ınez [email protected] Universidad de Alicante

Transcript of lpp_teoria

The Scheme-bookLenguajes y paradigmas de la programacion

Curso 2004/2005

Jose Perez Martınez

[email protected]

Universidad de Alicante

JOSE PEREZ MARTINEZ

The Scheme-book

UNIVERSIDAD DE ALICANTE

Tıtulo The Scheme-book

Autor: Jose Perez Martınez, [email protected]

Este documento ha sido fruto del esfuerzode todos su autor.

Si tiene cualquier notificacion querealizarle pongase en contacto con

el en su respectiva direccionelectronica.

Texto compuesto con TEX (http://www.tug.org) yLATEX2ε (http://www.latex-project.org).

Indice general

Indice general 3

Indice de figuras 5

1. Programacion Funcional 71.1. Lenguajes de programacion . . . . . . . . . . . . . . . . . . . . . . . . . . 71.2. Introduccion a Scheme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

1.2.1. Expresiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81.2.2. Evaluacion de una expresion . . . . . . . . . . . . . . . . . . . . . . 91.2.3. Definicion de nuevas funciones . . . . . . . . . . . . . . . . . . . . . 101.2.4. Quote . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101.2.5. Algunas funciones de Scheme . . . . . . . . . . . . . . . . . . . . . 111.2.6. Otros ejemplos de definicion de funciones . . . . . . . . . . . . . . . 111.2.7. Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

1.3. Programacion Funcional . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121.4. Modelo de sustitucion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

1.4.1. Orden de evaluacion normal vs. de aplicacion . . . . . . . . . . . . 15

2. Recursion 172.1. Recursion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172.2. Procesos recursivos e iterativos . . . . . . . . . . . . . . . . . . . . . . . . 20

2.2.1. Factorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222.2.2. Numeros de Fibonacci . . . . . . . . . . . . . . . . . . . . . . . . . 23

3. Procedimientos de orden superior 253.1. Funciones como datos de primera clase . . . . . . . . . . . . . . . . . . . . 25

3.1.1. Funciones como argumentos . . . . . . . . . . . . . . . . . . . . . . 253.1.2. Listas en Scheme . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273.1.3. Filtro sobre elementos de una lista . . . . . . . . . . . . . . . . . . 273.1.4. Funciones sin nombre . . . . . . . . . . . . . . . . . . . . . . . . . . 273.1.5. Tipos de datos de primera clase . . . . . . . . . . . . . . . . . . . . 273.1.6. Funciones que devuelven funciones . . . . . . . . . . . . . . . . . . 27

3.2. Let . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

3

4 INDICE GENERAL

4. Abstraccion de datos 29

5. Datos jerarquicos 31

6. Representacion de datos abstractos 33

7. Asignacion, estado y entornos 357.1. Modelo de evaluacion de entornos . . . . . . . . . . . . . . . . . . . . . . . 35

7.1.1. Conceptos previos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357.1.2. Reglas para evaluar una expresion en el entorno actual . . . . . . . 35

8. Tipos de datos mutables 37

9. Programacion Orientada a Objetos 39

10.Streams 41

A. Ejemplos de clase 43A.1. Clase 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43A.2. Clase 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46A.3. Clase 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46A.4. Clase 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46A.5. Clase 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46A.6. Clase 6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46A.7. Clase 7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46A.8. Clase 8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46A.9. Clase 9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46A.10.Clase 10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46A.11.Clase 11 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46A.12.Clase 12 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46A.13.Clase 13 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46A.14.Clase 14 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46A.15.Clase 15 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46A.16.Clase 16 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46A.17.Clase 17 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46A.18.Clase 18 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46A.19.Clase 19 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46A.20.Clase 20 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46A.21.Clase 21 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46A.22.Clase 22 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46A.23.Clase 23 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46A.24.Clase 24 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46A.25.Clase 25 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46

Indice de figuras

2.1. Calculo de fib(5) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

5

6 INDICE DE FIGURAS

Tema 1Programacion Funcional

1.1. Lenguajes de programacion

En esta asignatura vamos a profundizar en la idea de lenguaje de programacion. ¿Ex-isten caracterısticas comunes a todos los lenguajes de programacion? ¿Son caracterısticasindependientes del paradigma de programacion que se usa? Lo primero que debemos hacerpara poder reflexionar sobre estas cuestiones es concretar algo mas que entendemos por¡¡lenguaje de programacion¿¿.

Nuestra idea de ¡¡lenguaje de programacion¿¿ y de ¡¡programa¿¿ es la que planteanAbelson y Sussman:

“Vamos a estudiar la idea de un proceso computacional. Los procesos com-putacionales son seres abstractos que pueblan los computadores. Al ejecutarse,los procesos manipulan otras cosas abstractas llamadas datos. El desarrollode un proceso esta gobernado por un conjunto de reglas llamadas un pro-grama. [...] Los programas que usamos para conjurar procesos son como losencantamientos de los magos. Estan compuestos de expresiones simbolicas enarcanos y esotericos lenguajes de programacion que prescriben las tareasque queremos que realicen nuestros procesos. [...] El lenguaje de programacionsirve como un marco en el que organizamos nuestras ideas sobre procesos.”(SICP, pp. 1,4)

Todo lenguaje de programacion tiene tres mecanismos para conseguir esto

expresiones primitivas, que representan las entidades mas simples que maneja ellenguaje

mecanismos de composicion, con los que construimos elementos mas complicados apartir de los mas sencillos

mecanismos de abstraccion, con los que los objetos compuestos puede ser nombradosy manipulados como una unidad.

7

8 1.2. Introduccion a Scheme

1.2. Introduccion a Scheme

En la asignatura Lenguajes y Paradigmas de programacion (LPP) vamos a programaren scheme, que es un lenguaje interpretado. Esto significa que en lugar de escibrir unprograma y luego compilarlo, vamos a poder escribir una sencilla expresion y averiguarsu valor. El interprete de scheme realiza un bucle read-eval-print con el que lee laexpresion, la evalua e imprime su resultado.

1.2.1. Expresiones

Por ejemplo si ejecuatamos en el interprete:

> 22> (∗ 3 2)6> ( sq r t 4 )16> (+ 2 3 4)9

La sintaxis general de una expresion en scheme es:

(< f unc i on > <arg1 > . . . < argn >)

Composicion de funciones:

(+ (∗ 3 4 ) 5 )(− (+ 2 1 ) ( / 10 2 ) )

Definiciones y entornos

Una caracterıstica fundamental de cualquier lenguaje de programacion es la posibilidadde definir nuevos nombres que van ampliando el vocabulario que podemos usar paraconstruir programas. Cada nombre nuevo representa un aumento de la capacidad deexpresividad del lenguaje, en la lınea del dominio en el que queremos definir el programa.Para ser consistentes con el libro de la asignatura, el idioma que vamos a usuar para lamayorıa de los nombres que inventemos sera el ingles.

Por ejemplo, si estamos escribiendo un programa de geometrıa, es normal que defi-namos nombres como square, point, circle, etc . . .

¿A que le podemos dar nombre? A las entidades propias del lenguaje. En el caso deScheme a datos elementales (enteros, strings, . . .) y a funciones.

Si lo de ¡¡dar nombre a un entero¿¿ te puede parecer una forma muy retorcidade explicar un DEFINE de C, ya ni me contaras con lo de ¡¡dar nombre a unafuncion¿¿. ¿Por que esa forma de hablar? ¿Por que no decir directamente ¡¡definiruna funcion¿¿? Ya veras dentro de poco que realmente no es una forma de hablar...es la forma que tiene Scheme de funcionar.

1. Programacion Funcional 9

Para definir nuevas entidades Scheme usa la forma especial define:

> ( d e f i n e p i 3 . 1415 )p i> p i3 .1415> (+ p i 7 )10.1415> ( d e f i n e r a d i o 10)r a d i o> ( d e f i n e c i r c u n f e r e n c i a ( ∗ 2 p i r a d i o ) )c i r c u n f e r e n c i a> c i r c u n f e r e n c i a62.8318

La forma especial define asocia un valor con un identificador. Su sintaxis es:

( d e f i n e < i d e n t i f i c a d o r > <e xp r e s i o n >)

El interprete evalua la expresion y asocia al identificador el resultado de la evalu-acion. Podemos pensar que esta asociacion se guarda en una tabla o memoria que usael interprete y que se denomina entorno. Un entorno no es mas que una coleccion deparejas (<identificador>,<valor>). El modelo de computacion que veremos mas ade-lante, el modelo de sustitucion, no necesita el concepto de entorno. Pero cuando pasemosal paradigma procedural sera muy importante su utilizacion. Por ejemplo, despues deejecutar el ¡¡define¿¿ anterior, el entorno quedara como:

Identificador Valor

pi 3′1415

1.2.2. Evaluacion de una expresion

Para evaluar una expresion, Scheme sigue la siguiente regla:

Para evaluar (foo arg1 . . . argn)

1. Si foo es una forma especial: aplicar foo a los argumentos sin evaluar.Cada forma especial se aplica de una forma distinta.

2. Sino foo solo puede ser una funcion

a) Evaluar las subexpresiones: foo, arg1, . . . , argn

b) Aplicar el procedimiento resultante de evaluar foo a los resultadosde evaluar arg1, . . . , argn

Por ejemplo, para evaluar (+ 2 3), Scheme evalua el 3 (resulta el numero 3), despuesel 2 (resulta el numero 2) y despues el + (resulta el procedimiento ¡¡suma¿¿, que elinterprete representa como "#[...]"). Y por ultimo, aplica el procedimiento suma a 3 y2, resultando 5.

10 1.2. Introduccion a Scheme

Puede parecer raro eso de que se evalue el +. ¿Que significa eso? Pues que en Schemelos procedimientos son objetos primitivos del mismo nivel que otros objetos como enteroso caracteres. Ya veremos en el tema 3 que en Scheme es posible pasar un procedimientocomo parametro o incluso hacer que una funcion devuelva un procedimiento.

Los parentesis significan algo. En Scheme no puedes usar parentesis a menos que vayasa llamar a una funcion o a una forma especial. Por ejemplo, si tecleas:

(+ ( squa r e 2 ) ( 3 ) )

No obtendras 7. Scheme devolvera un error porque 3 no es una funcion.

1.2.3. Definicion de nuevas funciones

Y tambien se usa define para definir una funcion, con la siguiente sintaxis (que ya veremosque es azucar sintactico):

( d e f i n e (< nombre−funcion > <param>)<cuerpo >)

Ejemplos:

( d e f i n e ( squa r e x )(∗ x x ) ) ; ; o d e f i n i c i n de una o f u n c i n

( squa r e 5 ) ; ; l l amada a l a o f u n c i n( squa r e (+ 2 3 ) ) ; ; ocompos i c i n con f u n c i o n e s d e f i n i d a s

Terminologıa: el parametro formal es el nombre del argumento (x); la expresionargumental real es la expresion utilizada en la llamada ((+ 2 3)); el valor real del argu-mento es el valor del argumento en la llamada (5). El nombre del argumento viene de ladefinicion de la funcion; el valor del argumento viene de la llamada.

1.2.4. Quote

La forma especial quote devuelve el identificador que se le pasa como argumento sinevaluar, como el mismo identificador:

> ( quote ho l a )ho l a> ’ ho l aho l a

La tilde (’) es una abreviatura de quote. Las dos expresiones anteriores son identicas.A quote se le puede pasar como argumento un identificador o una expresion:

> ( quote (+ 1 2 ) )(+ 1 2)

1. Programacion Funcional 11

1.2.5. Algunas funciones de Scheme

Hasta ahora hemos usado unicamente funciones numericas. Vamos a ver algunos ejem-plos de otras funciones de Scheme.

Una caracterıstica fundamental de Scheme es que es muy sencillo extender el lenguaje,definiendo nuevas primitivas. La version estandar, limpia, de Scheme esta definida en alR5RS. Pero en clase vamos a usar desde el principio algunas funciones que no estandefinidas en esta version estandar, como first o word. El interprete STk que hemospuesto en la web, en su version de Windows, ya incorpora estas nuevas primitivas. Si usasotro interprete de Scheme deberas cargar el fichero simply.scm que se encuentra en lapagina de ¡¡Apuntes¿¿ de la web de la asignatura :

(load "/PATH_ABSOLUTO/simply.scm")

Algunos ejemplos:

( f i r s t 274)( b u t f i r s t 274)( f i r s t ’ h e l l o )( f i r s t ” h e l l o ” )( f i r s t ( b f ’ h e l l o ) )(+ ( f i r s t 2 3 ) ( l a s t 45 ) )( empty ? ( b f ’ h ) )(and #f # f #t )( or #f # f #t )

Algunas consideraciones:

Aunque para Scheme un identificador (’hello) no es exactamente igualque un string ("hello"), muchas funciones de las anteriores las conside-ran iguales

Muchas funciones son polimorficas: pueden usarse con distintos tipos deobjetos.

1.2.6. Otros ejemplos de definicion de funciones

Vamos a definir algunas funciones mas

( d e f i n e ( p l u r a l wd)( word wd ’ s ) )

Este sencillo plural funciona correctamente (en ingles) para la mayorıa de las pal-abras (book, elephant, computer) pero no para las palabras que terminan en y. Vamos amejorarlo:

( d e f i n e ( p l u r a l wd)( i f ( equal ? ( l a s t wd ) ’ y )

( word ( b l wd ) ’ i e s )( word wd ’ s ) ) )

12 1.3. Programacion Funcional

if es una forma especial que solo evalua una de las alternativas.(Ejemplo) Pig Latin: Mueve consonantes iniciales al final de la palabra y anade "ay";

SCHEME se convierte en EMESCHAY.

( d e f i n e ( p i g l wd)( i f ( pl−done ? wd)

( word wd ’ ay )( p i g l ( word ( b f wd ) ( f i r s t wd ) ) ) ) )

( d e f i n e ( pl−done ? wd)( vowel ? ( f i r s t wd ) ) )

( d e f i n e ( vowel ? l e t t e r )(member? l e t t e r ’ ( a e i o u ) ) )

Pigl introduce recursion — una funcion que se llama a ella misma —. Veremos mas sobreesto en las proximas semanas.

(Otro ejemplo): ¿Sabes jugar a Buzz? Comienzas a contar y si tu numero es divisiblepor 7 o tiene el dıgito 7 tienes que decir "buzz" en lugar del numero:

( d e f i n e ( buzz n )( cond ( ( equal ? ( r ema inde r n 7 ) 0 ) ’ buzz )

( (member? 7 n ) ’ buzz )( e l s e n ) ) )

Esto introduce la forma especial cond para multiples opciones. La forma especial condes la gran excepcion a la regla sobre el significado de los parentesis; las clausulas no sonllamadas.

1.2.7. Resumen

El define es el modo de abstraccion mas sencillo en Scheme, ya has visto que permiteutilizar simples nombres para referirse a resultados de funciones compuestas, como es elcaso de la circunferencia.

En general, los objetos pueden tener estructuras muy complejas y serıa muy tedioso situviesemos que recordar y repetir sus detalles cada vez que queremos utilizarlos. Ademas,los programas complejos se generan construyendo, paso a paso, objetos cada vez mascomplejos.

Asociar valores con sımbolos y despues reutilizarlos, significa que debe existir alguntipo de memoria que guarde las parejas nombre-objeto. Ese tipo de memoria se denominaentorno (y mas concretamente, entorno global). En temas posteriores veremos que unaejecucion puede involucrar diferentes entornos.

1.3. Programacion Funcional

El primer paradigma de programacion que vamos a ver es el paradigma de la Pro-gramacion Funcional. En este paradigma, las funciones no tienen ¡¡memoria¿¿ y no dejan

1. Programacion Funcional 13

efectos colaterales, y ademas cada funcion siempre devuelve el mismo resultado para unmismo parametro. Por ejemplo,

( squa r e 2 )4( squa r e 2 )4

Si le pasamos a square el argumento 2 siempre devolvera 4. Parece algo obvio, sinembargo en el siguiente ejemplo veras una funcion que no forma parte de la ProgramacionFuncional:

( random 10)4( random 10)7

La funcion random es un procedimiento que toma un argumento y aleatoriamentedevuelve un entero menor que el pasado como argumento. Para un mismo argumentodevuelve diferentes resultados. Eso no es una funcion.

Otro ejemplo donde se viola este principio de la Programacion Funcional. Como to-davıa no hemos visto como se hacen asignaciones en Scheme, lo vamos a escribir en C:

s t a t i c i n t iCount = 0 ;i n t getCount ( i n t i I n c r emen t ){

iCount = iCount + i I n c r emen t ;return iCount ;

}

count = getCount (10) ; // d evu e l v e 10count = getCount (10) ; // d evu e l v e 20

Fıjate que si llamamos a getCount dos veces se obtienen resultados distintos. Ademashay un efecto colateral despues de llamar a getCount: el valor de la variable iCount varıa.

Resumiendo, el Paradigma de la Programacion Funcional tiene las siguientes carac-terısticas:

Una funcion puede tener cualquier numero de argumentos, incluyendo cero, perosiempre debe devolver un unico valor. (Supongamos que necesitamos dos. Podrıamoscombinarlos en uno, por ejemplo, una frase). No es una funcion a no ser que siempreobtengas la misma respuesta para los mismos argumentos

La definicion de funcion proporciona un parametro formal (un nombre) y la llamadaa la funcion proporciona un argumento real (un valor). Estos encajan como en unrompecabezas. No escribas una ¡¡funcion¿¿ que solo funciona para un valor particularde argumento.

Las funciones no deben tener ¡¡memoria¿¿ ni producir efectos colaterales. Despuesde una llamada a una funcion, el sistema tiene que permanecer inalterable.

14 1.4. Modelo de sustitucion

En lugar de secuencias de eventos, tenemos composicion de funciones, como f(g(x))de las clases de matematicas del instituto. Las representaremos graficamente conmaquinas y diagramas de funciones.

1.4. Modelo de sustitucion

Vamos a ver el primer modelo computacional. Es aplicable al paradigma funcional ysirve para formalizar como se comportan los programas escritos en este paradigma. Setrata del modelo de sustitucion o de reescritura.

La regla que define el modelo de sustitucion para evaluar una expresion es muy sencilla:

Para aplicar un procedimiento compuesto a unos argumentos, se debe evaluarel cuerpo del procedimiento con cada parametro formal reemplazado por elcorrespondiente argumento.

Para ilustrar esta regla, vamos a definir unas funciones:

( d e f i n e ( doub le x ) (+ x x ) )( d e f i n e ( squa r e y ) ( ∗ y y ) )( d e f i n e ( f z ) (+ squa r e ( doub l e z ) ) 1 ) )

Y ahora vamos a ver que ocurre cuando ejecutamos (f (+ 2 1)) utilizando el modelode sustitucion:

1. Evaluamos la expresion: f es un procedimiento; que es (+ 2 1)? → (f 3))

2. Ahora tomamos el cuerpo de f y sustituimos 3 por z: (+ square (double 3)) 1)

3. Ahora evaluamos la expresion: + es +, 1 es 1. ¿Que es (square (double 3))?square es un procedimiento; y ¿que es (double 3)? double es un procedimiento y 3es 3. Ası que, una vez hemos evaluado todo, podemos empezar a aplicar los proced-imientos. Empezamos por double sustituyendo el cuerpo de la funcion: (+ (square

(+ 3 3)) 1)

4. Ahora, ¿que es (+ 3 3)? → (+ (square 6) 1)?

5. Ahora se sustituye el cuerpo de square: (+ (* 6 6) 1)

6. ¿Que es (* 6 6)? → (+ 36 1)?

7. ¿Y (+ 36 1)? → 37

1. Programacion Funcional 15

1.4.1. Orden de evaluacion normal vs. de aplicacion

El ejemplo del modelo de sustitucion que hemos visto se ha hecho utilizando el ordende aplicacion, donde se evalua primero todos los procedimientos y argumentos antes deejecutar la llamada a la funcion.

Otra forma de evaluar expresiones es utilizando el orden normal : expandir completa-mente los procedimientos hasta que todo esta expresado mediante operaciones primitivasy autoevaluaciones, y entonces evaluar la expresion.

El mismo problema de antes: (f (+ 2 1))

1. Expandimos f, dejando (+ 2 1) sin evaluar: (+ (square (double (+ 2 1))) 1)

2. Ahora expandimos square: (+ (* (double (+ 2 1)) (double (+ 2 1))) 1)

3. Y ahora expandimos double: (+ (* (+ (+ 2 1) (+ 2 1)) (+ 2 1) (+ 2 1)))

1)

4. Por ultimo, expandimos todo los operadores primitivos y autoevaluamos los valores.Se hace uno por uno

(+ (∗ (+ 3 (+ 2 1 ) ) (+ (+ 2 1 ) (+ 2 1 ) ) ) 1 )(+ (∗ (+ 3 3 ) (+ (+ 2 1 ) (+ 2 1 ) ) ) 1 )(+ (∗ (+ 3 3 ) (+ 3 (+ 2 1 ) ) ) 1 )(+ (∗ (+ 3 3 ) (+ 3 3 ) ) ) 1 )(+ (∗ 6 (+ 3 3 ) ) 1 )(+ (∗ 6 6 ) 1 )(+ 36 1)37

Hemos visto las dos formas de evaluacion: con el orden aplicativo, evaluamos comple-tamente todas las subexpresiones antes de aplicar el procedimiento principal. Mientrasque en el orden normal, primero expandimos completamente los procedimientos hastaque todo esta expresado mediante operaciones primitivas y autoevaluaciones. Entonces,en uno y en otro, podemos encontrar la solucion de la expresion.

En programacion funcional, el orden normal y el orden aplicativo siempre devolveranel mismo resultado.

Vamos a definir una funcion que deberıa devolver siempre 0:

( d e f i n e ( z e ro x ) (− x x ) ) ; Es ta f un c i on s i empre d e b e r i a d e v o l v e r 0

Ahora vamos a evaluar (zero (random 10)) con orden aplicativo y con orden normal.

16 1.4. Modelo de sustitucion

Orden aplicativo

( z e ro ( random 10 ) )( random 10) ==> 5

( z e ro 5) −−−−>(− 5 5) ==> 00 ; E l orden de a p l i c a c i o n d evu e l v e 0

Orden normal

( z e ro ( random 10 ) )( z e ro ( random 10)) −−−−>(− ( random 1 0 ) ( random 10 ) )

( random 10) ==> 4( random 10) ==> 8

(− 4 8) ==> −4−4 ; E l orden normal no

La regla es que si estas haciendo programacion funcional, obtienes las mismas re-spuestas sea cual sea el orden de evaluacion. ¿Por que no sucede ası en el caso de (zero

(random 10))? Porque no es una funcion. ¿Por que?Eficiencia: intenta computar

( squa r e ( squa r e (+ 2 3 ) ) )

en orden normal y de evaluacion. El orden de aplicacion es mas eficiente proque solosuma 2 y 3 una vez, no cuatro. (Pero mas adelante, veremos que esto no es una reglageneral, a veces el orden normal es mas eficiente).

Tema 2Recursion

2.1. Recursion

En este tema vamos a hablar de la recursion. ¿Como disenar un algoritmo recursivo?El consejo mas importante es el que aparece en el siguiente recuadro:

CONFIA EN LA RECURSION

Esto es, para resolver un problema de forma recursiva debes:

CONFIAR EN LA RECURSION para resolver una version mas simple del problema

Obtener la solucion al problema completo a partir de la solucion de la version massimple

Por ejemplo, veamos el problema de saber si un caracter (por ejemplo la a) esta enuna cadena:

1. Quitamos el primer caracter de la cadena.

2. Si el caracter es la "a" devolvemos true.

3. Sino llamamos a la recursion con el resto de la cadena (un problema mas sencillo,porque es una cadena mas corta) y devolvemos el resultado devuelto por la recursion.

¿Cuando para el algoritmo? Cuando el problema es lo mas simple posible y ya no sepuede simplificar mas: este es el caso base. Debemos entonces devolver un valor concreto,la recursion ya ha terminado. En el caso del ejemplo; caso base: cadena vacıa; devolvemosfalse. Esto en Scheme es:

( d e f i n e ( e l emento ? x pa l a b r a )( cond

( ( empty ? pa l a b r a ) # f )( ( equal ? x ( f i r s t pa l a b r a )) # t )( e l s e ( e l emento ? x ( b f p a l a b r a ) ) ) ) )

17

18 2.1. Recursion

Otro ejemplo. Veamos el problema de contar el numero de veces que un caracter (porejemplo la “a”) aparece en una cadena:

1. Quitamos el primer caracter de la cadena.

2. Llamamos a la recursion con el resto de la cadena (un problema mas sencillo, porquees una cadena mas corta). Esta llamada nos dice que en el resto de la cadena hay nletras “a”.

3. Si el primer caracter que hemos quitado es una “a” devolvemos n+1, sino devolvemosn.

4. Caso base: cadena vacıa. En ese caso devolvemos 0.

Esto en Scheme queda como:

( d e f i n e ( v ece s l wd)( i f ( empty ? wd)

0( i f ( equal ? ( f i r s t wd ) l )

(1+ ( vece s l ( b f wd ) ) )( v ece s l ( b f wd ) ) ) ) )

Otro problema: Pig Latin. Pig Latin es una modificacion del ingles que usan losninos para hablar de forma divertida. Se trata de modificar una cadena moviendo todaslas consonantes (hasta la primera vocal) de su comienzo a su final y anadiendo el sufijo“ay”. Por ejemplo, en castellano:

Ingles Pig Latinchaval avalchaycurso ursocay

problema oblemapray

Aquı la solucion recursiva es algo mas complicada. La primera idea serıa hacerlo comolos ejemplos anteriores:

1. Si la palabra a modificar empieza por consonante: quitar la consonante, aplicar PigLatin al resto de cadena.

2. Confiamos en que la recursion nos devuelva el resto de cadena traducida a Pig

Latin y entonces anadimos la consonante al final.

3. Caso base: si la cadena empieza por vocal devolver la cadena con el sufijo “ay”anadido.

¿Funciona bien esta solucion? Veamos un ejemplo. Apliquemos Pig Latin a la palabra"problema". Nos deberıa devolver oblemapray. Sin embargo, el algoritmo anterior harıalo siguiente:

2. Recursion 19

1. La palabra "problema" comienza por consonante. La quitamos y aplicamos Pig

Latin a roblema.

2. Confiamos en la recursion y devuelve la palabrama traducida: oblemaray. Movemosla "p" al final: oblemarayp. NO ES CORRECTO.

¿Como habrıa que plantear la recursion? Si nos fijamos en el ejemplo, nos daremoscuenta de que la "p" deberıa estar antes del sufijo ay. Esto se consigue llamando a larecursiondespues de haber movido la consonante al final de la cadena:

1. La palabra "problema" empieza por consonante, la quitamos y la colocamos al finalde la palabra. Nos queda roblemap.

2. Confiamos en la recursion y aplicamos Pig Latin a esta palabra. Devolverıa oblemapay.!!Correcto¡¡

La solucion en Scheme:

( d e f i n e ( p i g l wd)( i f ( v o ca l ? ( f i r s t wd) )

( word wd ’ ay )( p i g l ( word ( b f wd ) ( f i r s t wd ) ) ) ) )

¿Donde esta aquı la regla general? ¿Estamos aplicando la recursion a un caso massencillo? Sı, ya que la palabra roblemap tiene la primera vocal mas a la izquierda queproblema.

No todas las recursiones siguen este patron. En los ejemplos anteriores en el cuerpo dela funcion se realiza una unica llamada recursiva. No siempre es ası. Por ejemplo, veamosel calculo de la serie de Fibonacci. Recordemos que la secuencia de numeros de Fibonaccise define con la siguiente expresion:

fib(n) =

{1, si n ∈ {0, 1}fib(n − 1) + fib(n − 2), si n > 1

De esta forma, la secuencia de numeros de Fibonacci es: 1, 1, 2, 3, 5, 8, 13, 21, . . . Lafuncion recursiva que calcula el numero n-esimo de la serie es la siguiente:

( d e f i n e ( f i b n )( cond ( (= n 0 ) 0 )

((= n 1 ) 1 )( e l s e (+ ( f i b (− n 1 ) )

( f i b (− n 2 ) ) ) ) ) )

La diferencia con las funciones recursivas vistas hasta ahora es que en el cuerpo de lafuncion se realizan dos llamadas recursivas. Esto genera un proceso recursivo en forma dearbol, como se comprueba en la siguiente figura, extraıda del Abelson & Sussman.

20 2.2. Procesos recursivos e iterativos

Figura 2.1: Calculo de fib(5)

El ultimo ejemplo de funcion recursiva es el triangulo de Pascal. Cada elemento deltriangulo de Pascal es la suma de los dos numeros que hay sobre el:

11 1

1 2 11 3 3 1

1 4 6 4 11 5 10 10 5 1

1 6 15 20 15 6 11 7 21 35 35 21 7 1

...

La funcion Scheme que calcula el numero de Pascal que se encuentra en una determi-nada fila y columna es la siguiente:

( d e f i n e ( p a s c a l row co l )( cond ( (= co l 0 ) 1 )

((= co l row ) 1 )( e l s e (+ ( p a s c a l (−1+ row ) (−1+ co l ) )

( p a s c a l (−1+ row ) c o l ) ) ) ) )

El proceso que genera esta funcion es un proceso que tambien tiene forma de arbol,ya que se hacen dos llamadas recursivas dentro del cuerpo de la funcion.

2.2. Procesos recursivos e iterativos

Vamos a hablar ahora de la diferencia entre un proceso recursivo y un proceso iterativo.Para diferenciar entre ambos tenemos que ocuparnos de describir los procesos que generanlos procedimientos de Scheme. Veamos el siguiente ejemplo:

2. Recursion 21

( d e f i n e ( count s en t )( i f ( empty ? s en t )

0(1+ ( count ( b f s en t ) ) ) ) )

Esta funcion cuenta el numero de palabras en un frase. Es una funcion recursiva,porque se llama a si misma en su cuerpo. Veamos el proceso que genera la funcion. Paraeso, veamos una traza:

( count ’ a bcde f )(1+ ( count ’ b cde f ) )(1+ (1+ ( count ’ c d e f ) ) )(1+ (1+ (1+ ( count ’ d e f ) ) ) )(1+ (1+ (1+ (1+ ( count ’ e f ) ) ) ) )(1+ (1+ (1+ (1+ (1+ ( count ’ f ) ) ) ) ) )(1+ (1+ (1+ (1+ (1+ (1+ ( count ”” ) ) ) ) ) ) )(1+ (1+ (1+ (1+ (1+ (1+ 0))))))(1+ (1+ (1+ (1+ (1+ 1)))))(1+ (1+ (1+ (1+ 2))))(1+ (1+ (1+ 3)))(1+ (1+ 4))(1+ 5)6

El proceso requiere un tiempo O(n), siendo n el numero de caracteres de la cadena.Tambien requiere un espacio O(n), sin contar el espacio para la frase en si misma, porqueScheme tiene que mantener el estado de n computaciones pendientes durante el proce-samiento. Estas computaciones pendientes obligan a guardar el estado del proceso en elmomento en que se hace la llamada recursiva. Este estado se guarda en la llamada pilade recursion, y se recupera cuando se obtiene el valor de la llamada recursiva.

El esquema muestra la evolucion del proceso generado por la funcion count. Es unejemplo de un proceso recursivo lineal. Cuando hemos llegado a la mitad del esquema ycalculamos (count ""), todavıa no hemos finalizado completamente el problema. Ten-emos que recordar anadir 1 al resultado seıs veces. Cada una de esas tareas recordadasrequiere un espacio en memoria hasta que se termina el proceso.

A continuacion presentamos un programa algo mas complicado que hace lo mismo dedistinta forma.

( d e f i n e ( count s en t )( d e f i n e ( i t e r wds r e s u l t )

( i f ( empty ? wds )r e s u l t( i t e r ( b f wds ) (1+ r e s u l t ) ) ) )

( i t e r s en t 0 ) )

Esta vez, no dejamos ninguna computacion en espera, no tenemos que recordar tareasno completas; cuando alcanzamos el caso base de la recursion tenemos la respuesta al

22 2.2. Procesos recursivos e iterativos

problema completo.

( count ’ a bcde f )( i t e r ’ abcde f 0 )( i t e r ’ b cde f 1 )( i t e r ’ c d e f 2 )( i t e r ’ d e f 3 )( i t e r ’ e f 4 )( i t e r ’ f 5 )( i t e r ”” 6 )6

Este es un ejemplo de un proceso iterativo. Cuando un proceso tiene esta estructura,Scheme no necesita memoria extra para recordar todas las cosas pendientes de terminara lo largo de la computacion del proceso. En otros lenguajes de programacion existenbucles (for, while) para definir procesos iterativos. En un lenguaje funcional no existenbucles; todo lo tenemos que hacer con llamadas recursivas. Pero en este caso es un tipode recursion especial, que no deja llamadas en espera. Esta recursion tambien recibe elnombre de tail recursion o recursion por la cola.

Ambos algoritmos son equivalentes; incluso es posible construir la funcion con recursionpor la cola automaticamente a partir de la funcion recursiva (no vamos a ver como se hace;lo dejamos para otras asignaturas).

Otros ejemplos de procesos recursivos y procesos iterativos son los siguientes.

2.2.1. Factorial

; ; f a c t o r i a l p roce so r e c u r s i v o

( d e f i n e ( f a c t o r i a l n )( i f (= n 1 )

1(∗ n ( f a c t o r i a l (− n 1 ) ) ) ) )

; ; f a c t o r i a l p roce so i n t e r a t i v o

( d e f i n e ( f a c t o r i a l n )( f a c t− i t e r 1 1 n ) )

( d e f i n e ( f a c t− i t e r p roduct coun t e r max−count )( i f ( > counte r max−count )

product( f a c t− i t e r ( ∗ coun t e r p roduct )

(+ counte r 1 )max−count ) ) )

2. Recursion 23

2.2.2. Numeros de Fibonacci

; ; p roce so r e c u r s i v o

( d e f i n e ( f i b n )( cond ( (= n 0 ) 0 )

((= n 1 ) 1 )( e l s e (+ ( f i b (− n 1 ) )

( f i b (− n 2 ) ) ) ) ) )

; ; p r oce so i t e r a t i v o

( d e f i n e ( f i b n )( f i b− i t e r 1 0 n ) )

( d e f i n e ( f i b− i t e r a b count )( i f (= count 0 )

b( f i b− i t e r (+ a b ) a (− count 1 ) ) ) )

24 2.2. Procesos recursivos e iterativos

Tema 3Procedimientos de orden superior

3.1. Funciones como datos de primera clase

La gran idea de esta semana es considerar a las funciones como datos. Es una idearadical porque cambia completamente la vision mas habitual de los lenguajes de progra-macion procedurales, donde una funcion es un proceso y hay una clara distincion entredatos y programa.

La metafora habitual de un proceso es una receta. En esa metafora, la receta te dicelo que hacer, pero la receta no es un ingrediente. Pero esta semana comprobaremos comouna funcion es un objeto con la misma entidad que un numero o una cadena de texto.

Compara la idea con la derivada en calculo. Es una funcion cuyo dominio y rango sonfunciones, no numeros. La funcion derivada trata a funciones ordinarias como a cosas, nocomo a procesos.

En la primera semana hemos visto un primer elemento para construir abstracciones:definir procedimientos. Un procedimiento recoge un conjunto de operaciones sobre unosargumentos y les da un nombre. Este nombre nos permite tratar al procedimiento comouna unidad y usarlo para construir a su vez otros procedimientos.

Una caracterıstica esencial de Scheme y el resto de lenguajes funcionales es que con-sidera a las funciones como datos de primera clase. Al igual que un entero o un caracter,una funcion puede pasarse como argumento, asignarse a una variable o devolverse comoresultado de otra funcion. Esto es tan distinto al resto de lenguajes de programacion quemerece ser visto un poco mas despacio. Vamos a ver varios ejemplos y estrategias que nosvan a servir para ir entendiendo este importante concepto.

3.1.1. Funciones como argumentos

El uso de argumentos sirve para generalizar un patron determinado. Por ejemplo,consideremos las siguientes funciones

25

26 3.1. Funciones como datos de primera clase

( d e f i n e p i 3 .141592654)( d e f i n e ( squa re−area r ) ( ∗ r r ) )( d e f i n e ( c i r c l e− a r e a r ) ( ∗ p i r r ) )( d e f i n e ( sphere−area r ) ( ∗ 4 p i r r ) )( d e f i n e ( hexagon−area r ) ( ∗ ( sq r t 3 ) 1 . 5 r r ) )

En cada uno de estos procedimientos, estamos obteniendo el area de alguna figurageometrica multiplicando por una constante el cuadrado de una dimension lineal (radioo lado). Todas las funciones tienen un unico argumento, la dimension lineal. ¿Es posiblegeneralizar aun mas este patron? Si nos fijamos bien, nos daremos cuenta de que todaslas funciones usan la siguiente regla: elevo al cuadrado el radio (o lado) y lo multiplicopor una constante que depende de la forma de la figura.

De esta manera podemos construir una solucion mas general, definiendo una nuevafuncion area que toma dos argumentos: el radio (o lado) de la forma y el tipo de forma,representado por un numero constante que se multiplica por el cuadrado del radio (o lado)para devolver el area.

( d e f i n e squa r e 1 )( d e f i n e c i r c l e p i )( d e f i n e sphe r e ( ∗ 4 p i ) )( d e f i n e hexagon ( ∗ ( sq r t 3 ) 1 . 5 ) )

( d e f i n e ( a r ea shape r ) ( ∗ shape r r ) )

Hemos definido nombres para las formas; cada nombre representa un numero constanteque se multiplica por el cuadrado del radio. Estamos generalizando un patron medianteel uso de un numero variable en lugar de un numero constante.

Pero tambien podemos genealizar un patron en el que lo que varıa es una funcion.Veamos los dos ejemplos siguientes:

( d e f i n e ( sumsquare a b )( i f ( > a b )

0(+ (∗ a a ) ( sumsquare (1+ a ) b ) ) ) )

( d e f i n e ( sumcube a b )( i f ( > a b )

0(+ (∗ a a a ) ( sumcube (1+ a ) b ) ) ) )

Cada una de estas funciones calcula la suma de una serie desde un numero inicial (a)hasta un numero final (b). Por ejemplo (sumsquare 5 8) calcula 52 + 62 + 72 + 82. Elproceso de calcular cada termino indvidual, de anadir los terminos, y de saber cuandoparar es el mismo estemos anadiendo cuadrados de numeros o cubos de numeros. La unicadiferencia esta en decidir que funcion aplicar a cada termino. Podemos generalizar estepatron haciendo que la funcion sea un argumento adicional, de la misma forma que elargumento number en la funcion area:

3. Procedimientos de orden superior 27

( d e f i n e ( sum fn a b )( i f ( > a b )

0(+ ( fn a ) ( sum fn (1+ a ) b ) ) ) )

3.1.2. Listas en Scheme

3.1.3. Filtro sobre elementos de una lista

3.1.4. Funciones sin nombre

3.1.5. Tipos de datos de primera clase

3.1.6. Funciones que devuelven funciones

3.2. Let

Let es una forma especial de Scheme que permite llamar a una expresion (el cuerpodel let) despues de haber declarado unos valores para unas variables que se usan en estaexpresion. Esta declaracion de valores es local a la ejecucion del let. Una vez que laexpresion del let se evalua, no se conservan esos valores. Su sintaxis es:

( l e t ( ( va r1 exp1 ). . .( va rn expn ) )

)

Para evaluar el let, las expresiones exp1 . . . expn se evaluan y sus valores se asocian alas variables var1 . . . varn. La expresion se evalua con estos valores asignados. Ejemplos:

> ( l e t ( ( x 1 )( y 2 ) )

(+ x y ) )3

> ( d e f i n e x 5 )> ( l e t ( ( x (+ 2 1 ) )

( y (+ x 3 ) )(+ x y ) )

11

Dos precauciones:

1. Let no proporciona una sentencia de asignacion tal y como existen en otros lengua-jes. La asociacion entre los nombres y los valores solo se mantienen mientras quecomputamos el cuerpo del let.

28 3.2. Let

2. Si tienes mas de una pareja nombre-valor, como en este ultimo ejemplo, los calculosno se realizan secuencialmente. Las ultimas asociaciones no dependen de las previas.

Vamos a aclarar esto ultimo explicando como se define let en terminos de lambda:

( l e t ( ( va r1 exp1 ). . .( va rn expn ) )

)

se t r aduce a

( ( lambda ( va r1 . . . va rn ) ) exp1 . . . expn )

Esto es, se construye una funcion sin nombre con los mismos argumentos del let y sellama a esa funcion con el resultado de evaluar las expresiones asociadas a cada argumento.Por ejemplo:

( l e t ( ( x 1 )( y 4 ) )

(∗ x y ) )

se t r aduce a

( ( lambda ( x y )(∗ x y ) ) 1 4 )