Apuntes Scheme

download Apuntes Scheme

of 31

Transcript of Apuntes Scheme

  • PROGRAMACIN FUNCIONAL: SCHEME

    II PROGRAMACIN EN SCHEME .................................................................................................1 II.1 SINTAXIS BSICA ..........................................................................................................................1

    SMBOLOS: TOMOS Y NMEROS.............................................................................................1 INTERPRETACIN y NMEROS........................................................................................................... 1 DEFINICIONES Y LITERALES............................................................................................................... 1 EXPRESIONES ARITMTICAS.............................................................................................................. 1 LISTAS ...................................................................................................................................................... 2 Anidamiento ............................................................................................................................................... 2 Composicin............................................................................................................................................... 2 Evaluacin de listas (funciones como listas) .............................................................................................. 2 Funciones adicionales de Scheme para estructuras..................................................................................... 3

    DEFINICIN DE FUNCIONES.....................................................................................................3 Sentencias condicionales ............................................................................................................................ 4 Forma condicional genrica: COND .......................................................................................................... 4 OPERADORES LGICOS........................................................................................................................ 5

    Ejemplo de programa: ............................................................................................................................ 5 II.2 ABSTRACCIN DE DATOS Y NMEROS ...........................................................................................6

    Nmeros..........................................................................................................................................6 Representacin y precisin. ........................................................................................................................ 6 Propiedades................................................................................................................................................. 7 Operaciones ................................................................................................................................................ 7 Entrada/Salida............................................................................................................................................. 8

    Definicin recursiva de funciones aritmticas................................................................................8 Aritmtica exacta por abstraccin de datos....................................................................................9 NUMEROS RACIONALES .............................................................................................................9

    II.3 FUNCIONES DE ORDEN SUPERIOR Y EVALUACIN PARCIAL.........................................................13 Definicin generalizada de funciones en Scheme - Funciones LAMBDA ....................................13 Funciones de orden superior ........................................................................................................14

    Funciones de orden superior predefinidas ................................................................................................ 15 Definiciones Locales - Let y Letrec...............................................................................................17 Currying en Scheme......................................................................................................................18

    Conclusiones............................................................................................................................................. 20 II.4 COMPUTACIN CON LISTAS INFINITAS: STREAMS ........................................................................21

    Estrategias de evaluacin: Perezosa vs Impaciente................................................................................... 21 Streams .........................................................................................................................................22

    Funciones de orden superior sobre streams .............................................................................................. 24 Implementacin de Streams...................................................................................................................... 25 Streams Infinitos....................................................................................................................................... 26 Streams Infinitos definidos implcitamente .............................................................................................. 26

    II.5 EJEMPLOS DE PROGRAMACIN SIMBLICA: DERIVACIN SIMBLICA Y MATCHING. ....................27 Derivacin simblica ................................................................................................................................ 27 Matching................................................................................................................................................... 30

  • 1

    II PROGRAMACIN EN Scheme En este apartado vamos a ver como se pueden escribir programas funcionales en Scheme empleando

    los conceptos de programacin funcional.

    II.1 Sintaxis bsica En primer lugar veremos como se representan los datos (las S-expresiones) para luego pasar a la

    definicin de funciones.

    SMBOLOS: TOMOS Y NMEROS En Scheme los tomos se denominan smbolos.

    Estos se forman por caracteres distintos de: ( ) []{}; , " ' ` # \ Adems + - . no pueden aparecer al principio de un smbolo.

    Ejemplos de smbolos vlidos: abcd r cdr p2q4 errores? uno-dos *ahora&

    INTERPRETACIN y NMEROS Los nmeros en Scheme se consideran una categora aparte. As para evaluar un nmero basta con

    introducir a la entrada: (el nmero entre corchetes es el prompt de Scheme) [1] 7 7 [2] Esto se debe a que el intrprete de Scheme sigue un ciclo entrada-evaluacin-salida que evala cada

    entrada del usuario. De forma que cada entrada puede considerarse un programa.

    DEFINICIONES Y LITERALES A la hora de evaluar una cadena "siete" es necesario asignarle una definicin previa. Las definiciones se realizan en Scheme a travs de la expresin define:

    (define identificador valor) (NO LO EMPLEAREMOS CON ESTE USO) Pero esta es una caracterstica de programacin procedural y no la emplearemos. Sin embargo si lo

    que queremos escribir la cadena como un smbolo constante (es decir que no se evale) debemos emplear la funcin quote:

    (quote siete) 'siete As podremos introducir: [2] 'siete siete [3]

    EXPRESIONES ARITMTICAS Las operaciones aritmticas se consideran un tipo ms de funcin y se expresan en notacin prefija:

    (+ 1 2) 1 + 2 (* 3 (+ 4

    5)) 3*(4

    +5)

  • 2

    as desde Scheme: [3] (* 3 (+ 4 5)) 27 [4]

    LISTAS En Scheme una lista se denota por una coleccin de elementos encerrados entre parntesis y separados

    por espacios en blanco. en concreto la lista vaca se representa por ( ). Scheme proporciona la funcin cons como constructora de listas, permitiendo aadir un elemento de

    cada vez. Su sintaxis es la siguiente:

    (cons primerelem ListaResto) Ejemplos: [4] (cons 1 '( ) ) ( 1 ) [5] (cons 2 '( 1 ) ) ( 2 1 ) [6] (cons 'tres '( 2 1 ) ) ( tres 2 1 ) [7] (cons '( 2 1) '( tres 2 1) ) ( ( 2 1) tres 2 1) [8]

    Anidamiento La ltima lista presenta a su vez la lista ( 2 1 ) anidada. Los elementos de una lista son aquellos que no

    aparecen anidados dentro de otra, as:

    ( ( a b ( c d ) ) e ( f g ) h ) tiene 4 elementos : ( a b ( c d ) ), e, ( f g ) y h.

    Composicin La generacin de una lista completa se puede conseguir a travs de cons junto con la composicin

    funcional. Ejemplo: Para generar ( 2 1 ) podemos hacer (cons 2 (cons 1 '( ) ) )

    Evaluacin de listas (funciones como listas) Al introducir y usar la lista vaca es necesario emplear '( ) esto se debe a que Scheme al encontrarse

    con una lista interpreta siempre el primer elemento como una funcin. As: [8] ( 1 2 ) Error smbolo 1 no definido [9] El apstrofe indica que todos los elementos dentro de la lista se deben interpretar como literales. [9] '( ( 2 1 ) tres 2 1 ) ( ( 2 1 ) tres 2 1) [10]

  • 3

    NOTA: En el ejemplo anterior tres no necesita apstrofe al pertenecer a la lista que a su vez est bajo el efecto del apstrofe externo.

    Otro ejemplo: [10] (cons '( a b ) '( c ( d e ) ) ) ( ( a b ) c ( d e ) )

    Funciones adicionales de Scheme para estructuras

    ( cXXXXr lista ) ,, donde cada "X" puede ser : a (car), d (cdr) no aparecer. Ejemplo: (cadadr '( 1 ( ( 2 3 ) 4 ) ) ) (car (cdr (car (cdr '(1((2 3)4))

    ))) devolvera 4

    Aparte de: atom?, null?, list? y pair?, Scheme distingue dos tipos de tomos:

    Numricos: (number? n) devuelve #t si n es un nmero. Simblicos: (symbol? n) devuelve #t si n es un smbolo. Ejemplos: (number? -4.6)

    #t (symbol? -4.6)

    #f (number? '3) #t (symbol? '3) #f (number? 'doce)

    #f (symbol? 'doce)

    #t (number? #t) #f (symbol? #t) #f

    (boolean? n) devuelve #t si n es un valor lgico. Ejemplos:

    (boolean? #t) #t (boolean? (number? 'a)) #t (boolean? (car '(1 2))) #f

    El nico tipo de dato que nos queda por identificar son las funciones, en Scheme procedures,

    (procedure? n) devuelve #t si n es una funcin. (procedure? cons)

    #t (procedure? +) #t

    (procedure? 'cons) #f

    (procedure? 100) #f

    DEFINICIN DE FUNCIONES En Scheme se pueden asociar valores a nombres mediante la funcin define. As, por ejemplo,

    escribiendo: [11] (define pi 3.141592) pi [12]

  • 4

    A partir de ah podemos referir ese valor como pi. [12] pi 3.141592 [13] La evaluacin de la funcin define tambin nos permite asociar un nombre de funcin con la

    correspondiente definicin en el entorno. Tras la evaluacin el intrprete responde escribiendo el nombre de la funcin:

    [13] (define (square X) (* X X)) square [14] (square 2) 4 [15] (square (+ 3 1)) 16 [16] La forma general de la definicin de una funcin es la siguiente: (define ( ) ) Donde es un smbolo asociado con la definicin de la funcin en el retorno. Los

    son los nombres utilizados dentro del cuerpo de la funcin para referirse a los correspondientes argumentos de la misma. El es una expresin que produce el valor de la aplicacin de la funcin cuando los parmetros formales son reemplazados por los argumentos actuales en la evaluacin de dicha funcin.

    Sentencias condicionales En Scheme la expresin condicional SI-ENTONCES-SINO se realiza mediante la forma especial if. (if )

    Al evaluarse una expresin if, el intrprete primero evala el de la funcin. Si se evala a CIERTO, el intrprete evala y devuelve el valor de , en caso contrario el intrprete evala y devuelve el valor de .

    Ejemplos: [16] (define (maxAoB a b) (if (> a b) 'a 'b) ) maxAoB [17] (maxAoB 3 2) a [18]

    Forma condicional genrica: COND Existe tambin una forma condicional genrica equivalente a mltiples expresiones condicionales

    SI-ENTONCES-SINO anidadas, como una estructura "case", sta es la forma especial cond. (cond ( ) ( ) .................................................. ( )) Los argumentos de la construccin cond son pares de expresiones de la forma

    ( ) llamadas clusulas. Cuando el intrprete evala la estructura primero evala los predicados de las clusulas en el orden dado hasta encontrar una clusula en la que el predicado

  • 5

    se evala a CIERTO, entonces evala y retorna el valor de dicha clusula, i.e., .

    Si todos los predicados de las clusulas se evaluan a FALSO la estructura cond no retorna ningn

    valor. El consecuente de una clusula puede ser vaco, en cuyo caso, si su predicado es el primero que se

    hace cierto el valor devuelto por cond es el resultado de la evaluacin de dicho predicado. Tambin se puede indicar, a travs del smbolo else cual es el valor a retornar por defecto. Para ello

    debemos aadir una clusula final al cond de la forma: (else ), devolvindose el resultado de evaluar .

    OPERADORES LGICOS Los predicados de las formas o estructuras if y cond pueden ser cualquier expresin que se evale a

    CIERTO o a FALSO como, por ejemplo, expresiones de comparacin (, =, , >=,

  • 6

    II.2 Abstraccin de datos y nmeros Una de las caractersticas esenciales de la Programacin Funcional (PF) es la capacidad de manejar

    tipos abstractos de datos. Para ello nos vamos a centrar en el clculo con nmeros. Vamos a ver como la abstraccin de datos

    nos va a permitir combatir el problema de la inexactitud en la representacin y en las operaciones con nmeros .

    Nmeros Hasta ahora hemos visto como Scheme identificaba dos tipos de tomos: los smbolos y los nmeros.

    Sin embargo dentro de los nmeros Scheme nos ofrece la siguiente pila de subtipos donde cada nivel es un subconjunto del inmediatamente superior:

    Number (Nmero)

    Complex (Complejo - zi)

    Real (Real - xi)

    Rational (Racional - qi)

    Integer (Entero - ni)

    Por ejemplo, el tomo 3 es un entero. Por tanto, tambin es un racional (3/1), un real (3.0) y un

    complejo (3+0i), y obviamente es el nmero representado por 3. Para identificar estos tipos Scheme nos ofrece los siguientes predicados:

    (number? obj) (complex? obj) (real? obj) (rational? obj) (integer obj)

    Representacin y precisin. Cada nmero (objeto) en Scheme puede tener ms de una representacin externa, as en el ejemplo

    anterior, 3, 3.0, 3/1 y 3+0i son todas representaciones externas del mismo nmero entero, el tres. Las operaciones sobre nmeros se realizan sobre objetos abstractos, tratando, en la medida de lo posible, abstraerse de su representacin.

    Para representar nmeros racionales e imaginarios, Scheme utiliza la siguiente sintaxis: / +i Ejemplos: 5/4, -4/3, 3+4i 4i Aunque nosotros trabajemos con datos inicialmente exactos, hay operaciones que necesariamente

    generan nmeros inexactos. Esta imprecisin se arrastrar en todas las operaciones donde se utilicen dichos valores. Este hecho debe poder detectarse para identificar que nmeros son exactos y cuales no. Para ello, todo nmero en Scheme es exacto o inexacto, esta informacin es mantenida automticamente por Scheme, y para consultarla nos ofrece dos predicados:

    (exact? z) (inexact? z)

  • 7

    El criterio que sigue Scheme para identificar la exactitud es el siguiente: dada una constante numrica se considera inexacta si contiene un punto decimal, presenta un exponente, o uno de sus dgitos es desconocido, si no es inexacto entonces es exacto.

    Es posible obtener la versin exacta de un nmero inexacto por aproximacin, y de manera similar podemos obtener la versin inexacta ms cercana a un nmero exacto, esta labor la realizan las siguientes funciones:

    (exact->inexact z) (inexact->exact z) Por ejemplo: (exact->inexact 1/3) 0.3333333333333333 (inexact->exact 0.333333333333) 6004799503154657/18014398509481984

    Propiedades Disponemos de un conjunto de predicados que nos permiten extraer unas serie de propiedades de un

    nmero, propiedades referidas a su signo, paridad o si es nulo: (positive? x) (negative? x) (odd? n) (even? n) (zero? z)

    Operaciones Predefinidas en Scheme encontramos mltiples operaciones sobre nmeros, entre ellas tenemos las

    siguientes: (+ z1 ...) suma de una serie de nmeros (- z1 z2 ...) resta de una serie de nmeros (- z) negacin (* z1 ...) producto de una serie de nmeros (/ z1 ...) divisin de una serie de nmeros (/ z) inverso (= z1 ...) igualdad (< x1 ...) montono creciente (> x1 ...) montono decreciente (= x1 ...) montono no creciente (max x1 ...) mximo de una serie de nmeros (min x1 ...) mnimo de una serie de nmeros (abs x) valor absoluto (floor x) mayor entero menor o igual que x (ceiling x) menor entero mayor o igual que (truncate x) parte entera (round x) redondeo (gcd n1 ...) mximo comn divisor (lcm n1 ...) mnimo comn mltiplo (exp z) funcin exponencial

  • 8

    (log z) logaritmo en base 10 (sin z) seno en radianes (cos z) coseno en radianes (tan z) tangente en radianes (asin z) arcoseno en radianes (acos z) arcocoseno en radianes (atan z) arcotangente en radianes (expt z1 z2) potencia (sqrt z) raz cuadrada (quotient n1 n2) n1 div n2 (remainder n1 n2) resto de div, signo del numerador (modulo n1 n2) resto de div, signo del denominador Las operaciones con ... en sus argumentos se ejecutan sobre un nmero arbitrario de argumentos,

    como mnimo los especificados, ejecutando las operaciones con asociatividad a izquierdas.

    Entrada/Salida La entrada y salida de nmeros se consigue por medio de dos funciones que nos permiten recoger una

    cadena de texto (string) y convertirla en un nmero, y viceversa: (string->number z) (string->number z radix) (number->string z) (number->string z radix) Estas funciones llevan incluidas como segundo argumento la base a utilizar (2, 8, 10 o 16), que por

    defecto ser 10. Si no es posible realizar la conversin por no representar un nmero vlido, la funcin devuelve #f.

    Definicin recursiva de funciones aritmticas A la hora de aplicar la recursin sobre nmeros fijaremos la base y la recurrencia de forma similar a

    como hacemos con las listas. Veamos un ejemplo. La funcin suma-armonicos calcula la suma de los primeros n trminos de una serie de armnicos, es

    decir:

    n1...

    31

    211 ++++

    La base en este caso se refiere al valor n=0 en el que la suma es nula. Base: si n=0 entonces suma-armonicos(0)=0 Recurrencia: conocemos suma-armonicos(n-1),, entonces

    suma-armonicos(n) = (1/n) + suma-armonicos(n-1) suma-armonicos(n) ::=

    si n=0 entonces 0

    sino 1/n + suma-armonicos(n-1) finsi (define(suma-armonicos n) (if (zero? n) 0 (+ (/1 n) (suma-armonicos (- n 1)))))

  • 9

    Vamos a definir ahora la funcin indice que dada una lista y un ndice n, nos devuelve el ensimo elemento, empezando en cero.

    Ejemplos de indice(lista,n): indice( (a b c d e f), 3) d indice( (a b c), 3) Error: indice fuera de rango Definicin de indice: Base: indice(lista,0) es car(lista) Recurrencia: conocemos indice(cdr(lista), n-1) e indice(lista, n)=indice(cdr(lista),n-1) indice(lista,n)::= si list?(lista)

    entonces si length(lista)>n entonces _indice(lista,n) sino error finsi

    sino error finsi _indice(lista,n)::= si n=0 entonces car(lista) sino _indice(cdr(lista),n-1) finsi Ahora podemos implementarla en Scheme como: (define (indice lista n) (if (list? lista) (_indice lista n) (error "argumento lista no es una lista"))) Nota: La funcin error interrumpe el proceso de evaluacin, muestra sus argumentos y devuelve el

    prompt al usuario. (define (_indice lista n) (cond ((null? lista) (error "indice fuera de rango")) ((zero? n) (car lista)) (else (_indice (cdr lista) (- n 1)))))

    Aritmtica exacta por abstraccin de datos A la hora de trabajar con operaciones aritmticas nos encontramos con resultados inexactos, el caso

    ms simple lo encontramos al evaluar un cociente de enteros. En este caso el resultado no tiene porque ser exacto y as al dividir (1 / 3) obtenemos el nmero inexacto 0.333333... que es distinto de 1/3.

    Sin embargo es posible realizar operaciones aritmticas con nmeros racionales y obtener resultados

    exactos. Con este objetivo nos aprovecharemos de la abstraccin de datos. Para ejemplificar la potencia de la abstraccin a la hora de mantener la exactitud en la representacin y manipulacin de los nmeros, vamos a tomar los nmeros racionales, tipo de dato que el estndar de Scheme ya incorpora. Por ejemplo:

    (/ 1 3) 1/3 (+ 1/3 2/6) 2/3 (/ 2/3) 3/2

    NUMEROS RACIONALES

    Un nmero racional ba

    est formado por 2 enteros: a su numerador y b su denominador que debe ser

    distinto de 0.

  • 10

    Por el momento no nos preocuparemos de la representacin de un nmero racional. Abstrayndonos de su representacin vamos a definir la funciones que servirn de interfaz para el

    acceso y construccin de racionales. SELECTORES Nos basaremos en la idea de que un racional est compuesto por un numerador y un denominador y a

    los que tenemos acceso a travs de 2 funciones: (rnum rac) numerador (rden rac) denominador As si rac es un racional, su numerador ser (rnum rac) y su denominador (rden rac). Son las

    funciones selectoras para nmeros racionales al igual que car y cdr aplicadas a listas. CONSTRUCTOR De la misma forma que con cons en listas, necesitamos un constructor para los racionales, ste ser

    crea-rac que construye un racional a partir de dos enteros, siendo el denominador no nulo. De esta forma, un racional rac verifica:

    (crea-rac (rnum rac) (rden rac)) rac Por ejemplo:

    (crea-rac 3 4) 43

    A partir de los selectores y el constructor podemos empezar a construir nuestras funciones aritmticas

    independientemente de la representacin final. La primera funcin va a ser rzero? que nos indicar si un racional es nulo o no (su denominador no

    importa, su numerador ha de ser cero). (define (rzero? rac) (zero? (rnum rac))) OPERACIONES ARITMETICAS Ahora combinaremos dos racionales para definir las operaciones aritmticas bsicas (+ - * /)

    obteniendo resultados exactos. SUMA

    La suma de dos fracciones ba

    y dc

    es otra fraccin cuyo numerador es a*d + b*c y su denominador

    es b*d. As si x e y son dos racionales definimos r+, (define (r+ X Y) (crea-rac (+ (* (rnum X) (rden Y)) (* (rnum Y) (rden X))) (* (rden X) (rden Y)))) PRODUCTO De la misma forma el producto lo definimos como, denominador b*d y numerador a*c. La funcin r*

    ser: (define (r* X Y) (crea-rac (* (rnum X) (rnum Y)) (* (rden X) (rden Y)))) RESTA/DIFERENCIA

  • 11

    De forma similar la diferencia ser: r- (define (r- X Y) (crea-rac (- (* (rnum X) (rden Y)) (* (rnum Y) (rden X))) (* (rden X) (rden Y)))) INVERSA Para definir el cociente, definimos previamente el inverso de un racional no nulo como rinver: (define (rinver X) (if (rzero? X) (error "rinver no puede invertir" X) (crea-rac (rden X) (rnum X)))) COCIENTE A partir de ella definimos el cociente como el producto de X por la inversa de Y, as r/ es: (define (r/ X Y) (r* X (rinver Y))) IGUALDAD

    Otra funcin interesante es la igualdad entre racionales, nos basamos en que dcbabc

    ba .. == ,

    as r= : (define (r= X Y) (= (* (rnum X) (rden Y)) (* (rnum Y) (rden X)))) SIGNO De igual forma podemos definir rpositive? que devuelve cierto en caso de que numerador y

    denominador tengan el mismo signo. Nos apoyamos en positive? y negative? de Scheme. (define (rpositive? rac) (OR (AND (positive? (rnum rac)) (positive? (rden rac))) (AND (negative? (rnum rac)) (negative? (rden rac))))) MAYOR Y MENOR El predicado r> y r< se basan en que la diferencia entre 2 racionales (r-) sea positiva o no (rpositive?)

    basta en r> intercambiar X e Y para obtener r< : (define (r> X Y) (rpositive? (r- X Y))) (define (r< X Y) (r> Y X)) De igual forma pueden construirse muchas otras operaciones. Funciones como argumento: MAXIMO Y MINIMO Para acabar vamos a definir la funcion rmax que recibe 2 racionales y devuelve el 1 en caso de que

    sea mayor que el segundo, en otro caso devuelve el 2. Para su definicin nos basamos en como se define bsica de max para los reales: (define (max X Y) (if (> X Y) X Y)) Para rmax basta cambiar el operador ">": (define (rmax X Y) (if (r> X Y) X Y)) Para rmin cambiamos de nuevo el operador: (define (rmin X Y) (if (r< X Y) X Y))

  • 12

    Como vemos existe una gran semejanza entre estas funciones. Cuando ocurre esto en programacin

    funcional, se opta por una funcin comn donde el operador a aplicar se le pasa como argumento, es decir, una Funcin de Orden Superior.

    Con este objetivo definimos una funcin valor-extremo que recibe tres argumentos: (valor-extremo Operador X Y) X o Y lo que formalmente se expresara por: valor-extremo::AA(AABooleano)A Donde Operador es la funcin a aplicar a los argumentos X e Y. Esta funcin devuelve X si el

    resultado de evaluar (oper X Y) es #t e Y en caso contrario. As la definicin de valor-extremo es: (define (valor-extremo oper X Y) (if (oper X Y) X Y)) De esta forma rmax y rmin se definirn como: (define (rmax X Y) (valor-extremo r> X Y)) (define (rmin X Y) (valor-extremo r< X Y)) Como vemos la versatilidad de la PF nos permite trabaja con objetos de distinto tipo con slo

    especificar el operador adecuado. POTENCIA DE LA ABSTRACCIN A partir de estas funciones es posible definir programas complejos que trabajen con aritmtica exacta

    sobre racionales. De esta forma, siempre que proporcionemos, el constructor y los selectores podremos generar un paquete de funciones sobre racionales. Es ms, ya que hasta ahora hemos manejado los datos como abstractos, esto nos permitir cambiar su representacin interna con slo modificar los selectores y el constructor: crea-rac, rnum y rden.

    REPRESENTACIN nicamente nos resta dar una representacin concreta a los datos. Una posibilidad sera una lista

    (a b) siendo a el numerador y b el denominador, donde b debe ser distinto de cero. (define (rnum rac) (car rac)) (define (rden rac) (cadr rac)) (define (crea-rac a b) (if (zero? b) (error "denominador 0 en crea-rac") (cons a (list b)))) Podramos cambiar la representacin a un par (a.b) y slo cambiaramos estas 3 funciones y el resto

    permanecera inalterado. rnum (car rac) rden (cdr rac) crea-rac (cons a b) Finalmente si queremos una representacin nica, podemos trabajar con la siguiente representacin

    cannica: (define (crea-rac a b) (if (zero? b) (error "denominador 0 en crea-rac") (list (quotient a (mcd a b)) (quotient b (mcd a b)))))

  • 13

    II.3 Funciones de orden superior y Evaluacin Parcial En este apartado vamos a ver dos de las caractersticas que permiten considerar a las funciones en PF

    como "ciudadanos de primera clase". Lo que se refleja en el hecho de que las funciones puedan ser tratadas como cualquier otro objeto del lenguaje. Pudiendo actuar como argumentos y como resultado.

    Definicin generalizada de funciones en Scheme - Funciones LAMBDA Scheme proporciona un mtodo ms elegante que el ya visto para la definicin de funciones. Este se

    basa en el Clculo- introducido por el lgico Alonzo Church (1932-33). En realidad los lenguajes funcionales son una versin "suavizada" de este clculo. Vemoslo con un ejemplo:

    (cons 19 '( )) (19) Tratamos ahora de escribir una funcin que recibe un argumento y devuelve una lista con dicho

    argumento mediante una expresin lambda. (lambda (item) (cons item '( ))) Si ahora aplicamos la expresin a 19: ((lambda (item) (cons item '( ))) 19) (19) Para evitar tener que reescribir la funcin cada vez que la deseamos aplicar, le damos un nombre: (define haz-lista-de-uno (lambda (item) (cons item '( )))) A partir de ah puede ser aplicada como otra funcin ms: (haz-lista-de-uno 19) (19) La forma general de una expresin- es la siguiente: (lambda (par1 ) ) funcin y la definicin con nombre sera: (define ) funcin Ejemplo: > (define (make-suma num) (lambda (X) (+ X num))) make-suma > (make-suma 4) (lambda(x) (+ x 4)) > ((make-suma 4) 7) 11 > Las funciones lambda son definiciones funcionales annimas, que no sern de especial utilidad para la

    definicin de funciones locales a una funcin (como veremos ms adelante) y como argumento o resultado de otra funcin.

  • 14

    Estas expresiones disponen de una segunda sintaxis que nos permite trabajar con un nmero arbitrario de argumentos:

    (lambda ) En este caso la expresin lambda recibir un nmero arbitrario de argumentos, en el momento de ser

    invocada la funcin todos sus argumentos se introducen en una lista que es referenciada como en el cuerpo de la funcin .

    (lambda ( ... . ) ) En esta ocasin la expresin lambda recibe al menos n argumentos ligados a los , y el resto de

    argumentos se reciben agrupados en una lista como

    Funciones de orden superior A partir de la definicin de este tipo de funcin se expondrn varios ejemplos de su uso y las

    funciones de orden superior (FOS) predefinidas en Scheme. Definicin: Una funcin se dice de orden superior si alguno de sus argumentos es una funcin o si

    devuelve una funcin o una estructura conteniendo una funcin. Las funciones de orden superior nos permiten un mayor nivel de abstraccin, y su utilizacin puede

    justificarse mediante los ejemplos siguientes: Ejemplos: Definir 3 programas funcionales que retornen los valores de:

    1. =

    =b

    annbaf ),(

    2. =

    =b

    annbaf 3),(

    3. = +=b

    an sananbaf

    )34)(34(1),(

    Su definicin es: f(a,b) ::= si a>b

    entonces 0 sino a+f(a+1,b) fsi g(a,b) ::= si a>b entonces 0 sino a*a*a+g(a+1,b) fsi h(a,b) ::= si a>b entonces 0 sino 1/(a*(a+2)) + h(a+4,b) fsi

  • 15

    Las tres funciones son muy similares, pudindose definir una funcin que es la abstraccin de todas ellas:

    sum(term, a, next, b) ::= si a>b entonces 0 sino term(a)+sum(term, next(a), next, b) fsi En cuyo caso la definicin de la funcin f(a,b) se realizara de la forma siguiente: term(X) ::= X next(X) ::= X+1 f(a,b) ::= sum(term, a, next, b) Emplearemos las expresiones lambda para definir con una sla funcin f(a,b): (define (f a b) (sum (lambda (X) X) a (lambda (X) (+ X 1)) b))

    Funciones de orden superior predefinidas APPLY Supongamos que queremos calcular el mximo de dos nmeros en una lista ls, no podemos llamar a

    (max ls) ya que ls no es del tipo correcto ya que cada argumento debe ser un nmero, as: >(max '(2 4)) error > Solucion A: Construir un programa recursivo que calcule max de una lista ls. Solucin B: Scheme nos ofrece la funcin apply, que permite aplicar una funcin de k argumentos a

    una lista de k elementos. El resultado es el mismo de pasar los elemento de la lista como los k argumentos. Su sintaxis es la siguiente:

    (apply ) Donde debe tener tantos argumentos como elementos tenga .

    Devolviendo el resultado de evaluar: ( ) Ejemplo: > (apply max '(2 4)) ; equivalente a (max 2 4) 4 > (apply + '(4 11 23)) ; equivalente a (+ 4 11 23) 38 > MAP La funcin map requiere como primer argumento el nombre de una funcin y a continuacin tantas

    listas como argumentos necesita esta ltima. El valor retornado es la lista de resultados de aplicar la funcin dada a los elementos correspondientes en las listas.

    Todas las listas han de tener la misma longitud. La sintaxis general: (map ...)

  • 16

    Ejemplos: > (map car '((a b) (c d) (e f))) (a c e) > (map + '(1 2) '(4 5 6)) (5 7) > Sumar 2 a los elementos de una lista > (map (lambda(x) (+ z 2)) '(1 2 3 4)) (3 4 5 6) > Comprobar que el tomo a pertenece a las listas elementos de una lista > (map (lambda(x) (member? 'a x)) '((a b c) (b c d) (c d a))) (#t #f #t) > FOR-EACH Similar a la funcin anterior, en el sentido de establecer un proceso secuencial (el indicado por la

    funcin) sobre los elementos correspondientes de las listas, pero en este caso no retorna resultado alguno. >(for-each display '((a b) (c d) (e f))) (a b) (c d) (e f) > Ejemplo de uso de funciones de orden superior: Traspuesta de una matriz: (tras '((1 2) (3 4) (5 6))) ((1 3 5) (2 4 6)) sera : (define (tras Mat) (apply map List Mat))

  • 17

    Definiciones Locales - Let y Letrec El lenguaje Scheme es interpretado y permite la definicin de los elementos del lenguaje a distintos

    niveles.

    General Environment

    User Environment

    Function Environment

    El mbito de las definiciones tiene tres niveles anidados. Entorno General, en l vienen las

    definiciones del propio intrprete incorporando las funciones y constantes predefinidas en el lenguaje. Entorno de Usuario, constituido por el Entorno General y las funciones definidas por el usuario. Entorno de Funcion, dentro de una funcin es posible establecer dos tipos de definiciones internas: argumentos y definiciones locales.

    Este apartado presenta las formas especiales Let y Letrec que permiten establecer definiciones locales

    en la definicin de la propia funcin y que permiten simplificar la lectura y definicin de la propia funcin.

    LET Nos permite asociar una definicin a un conjunto de smbolos, limitando su mbito a dicha expresin.

    Su sintaxis es la siguiente:

    (let ( (id1 val1) (id2 val2) (idn valn) ) body) Cada una de estas definiciones slo tienen una vigencia en la expresin body que constituye el cuerpo

    de la funcin. Ejemplo: Suma de dos definiciones internas (a y b) en una expresin: [32] (let ( (a 2) (b 3)) (+ a b) ) 5 [33]

    Definicin de la distancia eucldea: 22 )21()21( YYXX + , la expresin body aparece en negrita.

    (define (distancia X Y) (let ( (dif2 (lambda (X1 X2) (expt (- X1 X2) 2)))) (sqrt (+ (dif2 (car X) (car Y) ) (dif2 (cadr X) (cadr Y)))))) [33] (distancia '(2 3) '(3 6)) 2 [34] LETREC Es similar a la anterior salvo que en este caso se permite utilizar las propias definiciones de forma

    recursiva, los id's pueden aparecer en las expresiones val's.

  • 18

    Ejemplo: Definicin de la funcin factorial. (define (fact n) (letrec ((aux (lambda (n) (if (zero? n) 1 (aux (- n 1))))) (aux n)))) > (fact 5) 120 >

    Currying en Scheme Se define el "currying" como la capacidad de definir una funcin f que acta sobre n argumentos de

    tipo A1 A2An de forma distinta a la clsica: f:A1xA2xxAn B Sino como una funcin que va consumiendo entre 1 y n argumentos. Para ello se define como:

    f:A1A2A3An B La funcin se aplica al primer argumento y devuelve una funcin que tomar un nuevo argumento y

    devolver otra funcin. En caso de que haya n argumentos al final disponemos de un valor constante; si hay menos nos devuelve una funcin que puede ser aplicada al resto de argumentos restantes.

    Este tipo de funciones se denominan "funciones parametrizables". De esta forma conseguimos simular

    una funcin de n argumentos a travs de una funcin de orden superior de slo un argumento y que retorna una funcin.

    El empleo de este concepto en Scheme se alcanza a travs del empleo de funciones lambda, siendo

    ste el objeto devuelto con cada nuevo argumento. Veamos su utilizacin en un sencillo ejemplo: La funcin + toma 2 argumentos (nmeros) y devuelve su suma. Podemos definir un procedimiento

    (funcin) add5 que suma 5 a su argumento: (define add5 (lambda (n) (+ 5 n))) o (define (add5 n) (+ 5 n)) Fcilmente podemos definir una fucnin que le sume cualquier nmero en lugar de 5. Para ello

    emplearemos la ventaja de que una funcin pueda devolver como valor de retorno otra funcin. As definimos una funcin curried+ que tiene como nico argumento m y devuelve una funcin lambda, i.e. sin nombre, que tiene un slo argumento n y retorna la suma de m y n:

    (define (curried+ m) (lambda(n) (+ m n))) As (curried+ 5) devuelve una funcin definida como:

    (lambda (n) (+ 5 n)) ; m queda ligado a 5 Para sumar ahora 5 y 7, llamaramos a: ((curried+ 5) 7) 12 Es ms, ahora podemos definir add5 como: (define add5 (curried+ 5)) En este caso curried+ es donde se ha aplicado el concepto de currying pasando de una funcin con 2

    argumentos x e y, a reescribirla como una funcin con un argumento x y retorna otra funcin de un argumento, lo que se denomina currying o (segn autores) currificacin.

  • 19

    Ejemplo con Member: Supongamos que queremos aplicar la funcin member? sobre varias listas

    consultando por el mismo elemento. Con este objetivo definimos la funcin member-c que recibe un argumento item y devuelve una funcin helper con un argumento que debe ser una lista:

    (define (member-c item) (letrec ((helper (lambda (ls) (if (null? ls) #f (or (equal? (car ls) item) (helper (cdr ls))))))) helper)) Ahora podemos definir un member? en funcin de member-c (define (member? item lista) ((member-c item) lista)) Ejemplo con map: La funcin map tiene al menos 2 parmetros: una funcin fuc y una lista ls. (map add1 '(1 2 3 4)) (2 3 4 5) Su definicin: (define (map fuc ls) (if (null? Ls) '( ) (cons (fuc (car ls)) (map fuc (cdr ls))))) Esto puede ser reescrito a travs de la funcin apply-to-all que toma un argumento fuc y devuelve una

    funcin con un argumento ls que es una lista. Podemos definirla como: (define (apply-to-all fuc) (letrec ((helper (lambda(ls) (if (null? Ls) '( ) (cons (fuc (car ls)) (helper (cdr ls))))))) helper))) Ejemplo: > ((apply-to-all add1) '(1 2 3 4)) (2 3 4 5) > A partir de ella definimos map como: (define map (lambda (proc ls) ((apply-to-all proc) ls)))

  • 20

    Ejemplo de currying: funcin Swapper Se va a definir una funcin que recibe una lista Ls y dos elementos que deben ser intercambiados en

    ella x e y. Su definicin es la siguiente: (define swapper (lambda (x y Ls) (cond ((null? Ls) '( )) ((equal? (car Ls) x)(cons y (swapper x y (cdr Ls)))) ((equal? (car Ls) y)(cons x (swapper x y (cdr Ls)))) (else (cons (car ls) (swapper x y (cdr ls))))))) [36] (swapper 0 1 '(0 1 2 0 1 2)) (1 0 2 1 0 2) [37] Currificacin de swapper, swapper-m recibe dos argumentos x e y y retorna una funcin lambda con

    un argumento Ls de tipo lista. (define swapper-m (lambda (x y) (letrec ((helper (lambda(Ls) (cond (null? Ls) '( )) ((equal? (car Ls) x) (cons y (helper (cdr Ls)))) ((equal? (car Ls) y) (cons y (helper (cdr Ls))))) (else (cons (car Ls) (helper (cdr Ls)))))))) helper))) Por ejemplo para crear una funcin que intercambie 0 por 1's en una lista bastara con: [37] ((swapper-m 0 1) '(0 1 2 0 1 2)) (1 0 2 1 0 2) [38] Podemos adems darle nombre int01: [38] (define int01 (swapper-m 0 1)) int01 [39]

    Conclusiones Hemos introducido el concepto de currying.

    Permite redefinir una funcin con n = m+k parmetros como una funcin de m parmetros que retorna una funcin de k parmetros.

    De forma general, podemos redefinir una funcin de n argumentos como n funciones de 1 argumento.

  • 21

    II.4 Computacin con listas infinitas: streams En este apartado veremos como resolver problemas en Scheme sobre listas de longitud infinita. Para

    ello se introducir el concepto de stream como una secuencia infinita de elementos, y los operadores necesarios para su programacin, lo que implicar modificar tambin la estrategia de evaluacin de Scheme

    Estrategias de evaluacin: Perezosa vs Impaciente Una computadora para evaluar una expresin aplica un proceso de reduccin, o simplificacin, hasta

    alcanzar una expresin no reducible que es el resultado de la evaluacin. Esta expresin final decimos que est en forma cannica o en forma normal.

    Cada lenguaje de programacin presenta una determinada estrategia de reduccin de expresiones, que establece el orden en que deben ser reducidas las expresiones para alcanzar la forma normal. En cada paso se aplica una regla predefinida o de usuario que reescribe o reduce expresin seleccionada de acuerdo con la estrategia de reduccin. En cualquier caso el resultado de la evaluacin debe ser el mismo.

    Vamos a ver dos de las estrategias principales de evaluacin:

    Estrategia Impaciente o Ansiosa (Eager): En este caso la expresin a reducir es siempre la ms interna. As para evaluar la llamada a una funcin hay que evaluar primeramente sus argumentos. Esta estrategia es la que siguen los lenguajes imperativos como C, Pascal o Fortran, y algunos Funcionales como Scheme o Lisp.

    Estrategia Perezosa (Lazy): A la hora de reducir una expresin se selecciona siempre la ms externa.

    Lo que permite evaluar una funcin sin tener necesariamente que evaluar sus argumentos. Esta estrategia es la utilizada en lenguajes funcionales puros como Haskell. La ventaja de esta estrategia es que permite trabajar con estructuras infinitas, evita reducir argumentos (expresiones) que no son necesarios para la evaluacin. Todo ello confluye en el hecho de que por lo general necesita un nmero igual o menor de pasos que la evaluacin ansiosa.

    Veamos en que medida afecta al nmero de pasos necesarios para realizar la reduccin de una expresin. En primer lugar vamos a ver dos posibles secuencias de reduccin, utilizando las estrategias anteriores, al evaluar el cuadrado de una suma. Una posible secuencia con evaluacin impaciente es

    (cuadrado (+ 3 4)) = ; definicin de +

    (cuadrado 7) = ; definicin de cuadrado

    (* 7 7) = ; definicin de *

    49

    Otra posible secuencia de reduccin con evaluacin perezosa es

    (cuadrado (+ 3 4)) = ; definicin de cuadrado

    (* (+ 3 4) (+ 3 4)) = ; definicin de +

    (* 7 (+ 3 4)) = ; definicin de +

    (* 7 7) = ; definicin de *

    49

    He aqu un segundo ejemplo sobre una funcin (fst X Y) que devuelve el valor de su primer argumento. La primera secuencia con evaluacin impaciente es

  • 22

    (fst (cuadrado 4) (cuadrado 2)) = ; definicin de cuadrado (fst (* 4 4) (cuadrado 2)) = ; definicin de * (fst 16 (cuadrado 2)) = ; definicin de cuadrado (fst 16 (* 2 2)) = ; definicin de * (fst 16 4) = ; defincin de fst

    16 Utilizando la estrategia perezosa la secuencia es (fst (cuadrado 4) (cuadrado 2)) = ; definicin de fst (cuadrado 4) = ; definicin de cuadrado (* 4 4) = ; definicin de *

    16 Como vemos en los ejemplos, independientemente del orden, si ambos acaban, obtienen el mismo resultado. En el primer ejemplo la estrategia impaciente necesita un menor nmero de pasos. Lo que consigue al poder reutilizar el valor de un argumento ya reducido, que en el caso de la estrategia perezosa tiene que evaluarse cada vez que se utiliza. Por el contrario, en el segundo ejemplo es la estrategia perezosa la que necesita un menor nmero de pasos, ya que evita la reduccin del segundo argumento que no llega a ser necesario en la evaluacin. La evaluacin perezosa, frente a la impaciente, presenta dos propiedades deseables: (i) termina siempre que cualquier otro orden de reduccin termine, y (ii) no requiere ms (sino posiblemente menos) pasos que la evaluacin impaciente. Veremos como modificar la estrategia de evaluacin de Scheme, de forma que las estructuras de longitud infinita puedan ser procesadas por evaluacin perezosa.

    Streams Un stream es una secuencia posiblemente infinita de elementos. Existe una gran similitud con las listas de Scheme en su estructura, pero verdaderamente difieren en como se evalan lo que permite que un stream pueda tener longitud infinita. Como consecuencia, la resolucin de problemas sobre streams tiene un enfoque propio, muy semejante al procesamiento de seales electrnicas, tal y como podemos ver en la siguiente figura:

    Generadorde seal

    Filtro /Modulador

    Filtro /Modulador

    Moduladorde salida. . . Salida

    Como se puede ver la resolucin consiste en la definicin de una secuencia de operadores que se aplican secuencialmente a la seal de entrada en cada instante de tiempo. En Scheme la analoga sera la aplicacin secuencial de una serie de funciones a cada elemento del stream, que ahora puede ser procesado sin esperar por el resto de elementos.

    La filosofa de trabajo con streams consistir en identificar la secuencia de operaciones op1 op2 ... opn que debemos aplicar sobre los elementos de la secuencia para obtener el resultado. Para ello implementamos cada operador como una funcin sobre streams, y su aplicacin se logra por medio de la composicin funcional:

    (opn . . . (op2 (op1 Stream-Entrada) ) . . .) Salida

    De forma similar a las listas, para implementar estas operaciones necesitamos definir los operadores que nos permiten construir y examinar streams:

  • 23

    (head s) cabeza del stream s (tail s) cola del stream s (cons-stream a b) construye un stream con a y b, verificando que: (cons-stream (head s) (tail s)) s the-empty-stream se define como el stream vaco (empty-stream? s) cierto si s es el stream nulo Ejemplo: Pretendemos definir una funcin que calcule la suma de los cuadrados de las hojas impares de un rbol binario de nmeros. Una posible implementacin en Scheme sera la siguiente funcin: ;; suma de los cuadrados de las hojas impares de un arbol binario ;; arbol: ( ) ;; arbol(degenerado): (define (suma-cuadrados-impares arbol) (if (nodo-hoja? arbol) (if (odd? arbol) (square arbol) 0) (+ (suma-cuadrados-impares (rama-izq arbol)) (suma-cuadrados-impares (rama-der arbol))))) Para implementarla sobre streams identificamos la secuencia de operaciones como ENUMERA:hojas del rbol

    FILTRO:odd?

    MAP:square

    ACUMULA:+, 0

    La definicin de estas funciones sobre streams es la siguiente: (define (enumera-hojas arbol) (if (nodo-hoja? arbol) (cons-stream arbol the-empty-stream) (append-streams (enumera-hojas (rama-izq arbol)) (enumera-hojas (rama-der arbol))))) ;; CONCATENACION de streams (define (append-streams s1 s2) (if (empty-stream? s1) s2 (cons-stream (head s1) (append-streams (tail s1) s2)))) ;; FILTRA IMPARES de un stream (define (filter-odd s) (cond ((empty-stream? s) the-empty-stream) ((odd? (head s)) (cons-stream (head s) (filter-odd (tail s)))) (else (filter-odd (tail s))))) ;;; MAP square a lo elemento de un stream

  • 24

    (define (map-square s) (if (empty-stream? s) the-empty-stream (cons-stream (square (head s)) (map-square (tail s))))) ;;; ACUMULA la suma de los elementos del stream (define (accumulate-+ s) (if (empty-stream? s) 0 (+ (head s) (accumulate-+ (tail s))))) Finalmente podemos componer estas funciones para definir de la funcin original en trminos de streams: (define (suma-cuadrados-impares arbol) (accumulate-+ (map-square (filter-odds (enumera-hojas arbol)))))

    Funciones de orden superior sobre streams Las funciones sobre streams, como accumulate-+, map-square o filter-odds pueden generalizarse a funciones de orden superior que nos faciliten la resolucin de problemas similares sobre streams: ;;; elimina de s los elementos X que no cumplen (oper X) (define (filter-s oper s) (cond ((empty-stream? s) the-empty-stream) ((oper (head s)) (cons-stream (head s) (filter-s oper (tail s)))) (else (filter-s oper (tail s))))) ;;; MAP oper sobre los elementos de un stream (define (map-s oper s) (if (empty-stream? s) the-empty-stream (cons-stream (oper(head s)) (map-s oper (tail s))))) ;;; ACUMULA la suma de los elementos del stream ;;; similar a foldl sobre listas (define (accumulate-s oper val-ini s) (if (empty-stream? s) val-ini (oper (head s) (accumulate-s oper val-ini (tail s)))))

    As el ejemplo anterior se reduce a la siguiente definicin:

    (define (suma-cuadrados-impares arbol) (accumulate-s + 0 (map-s square (filter-s odd?

  • 25

    (enumera-hojas arbol)))))

    Implementacin de Streams Para definir la implementacin de los operadores sobre streams vamos a fijar la verdadera potencia de los streams sobre un ejemplo concreto.

    Consideremos el problema de localizar el segundo nmero primo entre 104 y 106, una posible solucin

    sera la siguiente:

    (head (tail (filter-s prime? (enumerate-interval 10000 1000000)))) Este enfoque con listas sera muy ineficiente (ya que tendra que generar en primer lugar una listas con todos los nmeros del intervalo, para a continuacin filtrar todos los nmeros que no son primos para quedarse al final con el segundo elemento de la lista resultante). Sin embargo con streams esta definicin es eficiente. La justificacin es que en un stream no tienen porque estar definidos de forma explcita todos sus elementos, sino que se pospone la definicin de los elementos que no sean necesarios. Este hecho se refleja en la implementacin de los operadores bsicos sobre streams que se basan en las dos siguientes funciones de Scheme que modifican el orden de evaluacin de las expresiones: (delay ) pospone la evaluacin de el resultado de eval se denomina una promesa (force ) fuerza la evaluacin de la promesa una vez evaluada una promesa, se registra su valor y no se recalcula De acuerdo con estas dos funciones, podemos ahora definir ahora: (define (head s) (car s)) (define (tail s) (force (cdr s))) (define (cons-stream ) (cons (delay )) (define the-empty-stream '()) (define (empty-stream? s) (null? s)) El constructor cons-stream es una estructura especial de Scheme (una macro en el caso de DrScheme) ya que si la definiramos como una funcin su invocacin evaluara el argumento antes de tratar de posponer su evaluacin con la funcin delay, con lo que no conseguiramos el efecto deseado. Volviendo al problema anterior, la funcin enumerate-interval quedara definida como : (define (enumerate-interval n1 n2) (if (> n2 n1) the-empty-stream (cons-stream n1 (enumerate-interval (+ n1 1) n2))))

    Con lo que la evaluacin de la expresin

    (enumerate-interval 10000 1000000) (10000 . )

    As devuelve un par donde la cabeza est definida y el resto (la cola) es una promesa cuya evaluacin se pospone hasta que se necesiten sus valores.

  • 26

    Para localizar los dos primeros primos (10007 y 10009) en el intervalo slo es necesario generar los primeros 10 elementos del intervalo si utilizamos streams.

    Streams Infinitos Siempre podemos crear una funcin que integre todos los pasos a la vez, y as en el ejemplo anterior que vaya comprobando si es primo cada nmero y si es el que buscamos lo devuelva. Pero los streams presentan una ventaja adicional y es que son capaces de definir y trabajar con secuencias infinitas. As por ejemplo podramos definir los nmeros Naturales por medio de la siguiente funcin

    (define (enteros-desde n) (cons-stream n (enteros-desde (+ n 1))) (define naturales (enteros-desde 1)) El objeto naturales es una secuencia infinita de elementos, con esta implementacin un stream infinito que ahora podemos manipular con las funciones head y tail. As podemos ahora definir los nmeros primos como: (filter-s prime? (tail naturales)) De nuevo si necesitamos en un programa los primeros nmeros primos, basta con utilizar esta ltima invocacin. Al representarlos como un stream no slo conseguimos que se calculen nicamente los nmeros primos que se necesiten, sino que adems si se vuelven a necesitar no habr que recalcularlos (recordemos gracias a que tail se implementa sobre la funcin force).

    Streams Infinitos definidos implcitamente De nuevo podemos basarnos en la analoga con el procesamiento de seales elctricas. En ellas, tal y como muestra la siguiente figura, podemos tener una salida que no slo depende de la entrada actual sino tambin de la salida inmediatamente anterior por medio de una retroalimentacin (o feedback). En Scheme podemos lograr un efecto similar con los streams. Vamos a definir la secuencia infinita de los nmeros de Fibonacci como: (define (fibgen a b) (cons-stream a (fibgen b (+ a b)))) (define fibs (fibgen 0 1)) Fibs es un par cuyo primer elemento es 0 y su cdr es una promesa que se evala a (fibgen 1 1), que una vez evaluada producir un par cuyo car ser 1 y su cdr una promesa que se evaluar a (fibgen 1 2) y as sucesivamente. Vamos aprovecharnos de la evaluacin pospuesta para definir streams de forma implcita. Por ejemplo, podemos definir el stream unos definido por una secuencia infinita de unos como: (define unos (cons-stream 1 unos)) Como vemos, el stream es un par donde el car es un 1 y el cdr es una promesa que se evala de nuevo al stream unos, comenzando con otro 1 y su cola de nuevo es unos, y as sucesivamente.

    Ahora vamos a definir la funcion add-streams que nos genera el stream suma de otros dos streams de longitud variable: (define (add-streams s1 s2) (cond ((empty-stream? s1) s2) ((empty-stream? s2) s1) (else (cons-stream (+ (head s1) (head s2)) (add-streams (tail s1) (tail s2))))))

  • 27

    Ahora podemos definir el conjunto de los enteros como: (define enteros (cons-stream 1 (add-streams unos enteros))) Esta definicin es correcta ya que los enteros son un par donde el primer elemento es el 1 y el resto se obtiene de sumar unos y el propio enteros. En la suma se utiliza siempre un elemento de enteros ya calculado en el paso anterior con lo que se accede directamente a l sin tener siquiera que ser vuelto a calcular.

    II.5 Ejemplos de programacin simblica: derivacin simblica y matching. En este apartado veremos dos ejemplos donde se explota la capacidad para la computacin simblica del lenguaje Scheme.

    Derivacin simblica Escribir un programa que permita aplicar las reglas de derivacin para la suma y el producto de una expresin que se proporcionar en forma prefija. Deber indicarse, adems, quin es la variable con respecto a la que se deriva.

    1) Identificacin de los tipos bsicos de datos que vamos a manejar:

    Operandos

    constantes numricas 3, 4, 6

    variables X, Y, Z

    Operadores suma

    (+ sum1 sum2) producto

    (* mul1 mul2)

    constant?(x) ::= number?(x) variable?(x) ::= symbol?(x) same-variable?(v1,v2) ::= variable?(v1) variable?(v2) eq?(v1,v2)

    2) Selectores, constructores y funciones de test de las expresiones (sumas y productos) empleados.

    Constructores

    make-sum(a1,a2) ::= list('+,a1,a2) make-product(a1,a2) ::= list('*,a1,a2)

    Selectores addend(s) ::= cadr(s) augend(s) ::= caddr(s) multiplier(p) ::= cadr(p) multiplicand(p) ::= caddr(p)

    Test

  • 28

    sum?(x) ::= si atom?(x) entonces eq?(car(x),`+) sino falso fsi

    product?(x) ::= si atom?(x) entonces eq?(car(x),'*) sino falso fsi

  • 29

    3) Funcin de derivacin de expresiones simblicas

    deriv(exp,var):=

    si constant?(exp) entonces 0 sino si variable?(exp) entonces si same-variable?(exp,var) entonces 1 sino 0 fsi sino si sum?(exp) entonces make-sum( deriv(addend(exp), var), deriv(augend(exp), var)) sino si product?(exp) entonces make-sum( make-product( multiplier(exp),deriv(multiplicand(exp), var)) make-product( deriv(multiplier(exp), var), multiplicand(exp))) sino error fsi fsi fsi fsi

    Ejemplo: Dada la expresin: x + 3 *(x + y + 2), la pasamos a notacin prefija para poder derivarla. deriv( '(+ X (* 3 (+ X (+ Y 2)))), 'X) (+ 1 (+ (* 3 (+ 1 (+ 0 0)) (* 0 (+ X (+ Y 2)))))) Vamos a tratar de simplificar las expresiones resultantes, para ello modificaremos los constructores de la suma y el producto para que analicen sus componentes antes de construir una nueva expresin. Si conocemos la regla de evaluacin de la nueva expresin se aplica directamente y el resultado es lo que devuelve el constructor. Vemoslo con el constructor de la suma.

    4) Versin mejorada de make-sum

    Vamos a tratar de realizar la simplificacin de la suma a la vez que se construye. Los casos analizados son la suma de constantes y la suma con el elemento neutro. make-sum2(a1,a2)::= si number?(a1) number?(a2) entonces a1 + a2 sino si number?(a1) cero?(a1) entonces a2 sino si number?(a2) cero?(a2) entonces a1 sino list('+,a1,a2) fsi fsi fsi

  • 30

    Ejemplo: (deriv '(+ x 3) 'x) 1 (deriv '(+ x x) 'x) 2

    Matching Un ejemplo tpico de programacin simblica es el matching, el cual permite determinar si una lista se corresponde o no con un patrn dado. El patrn puede contener, adems de smbolos concretos, metacaracteres . Los metacaracteres ms habituales son * y ?. El primero se corresponde con cualquier secuencia de s-expresiones (includa la secuencia vaca) en la lista, mientras que el segundo debe corresponderse exactamente con una s-expresin en la lista.

    Ejemplos:

    Patrn Lista Matching (a b * c ? d) (a b (c d) e h c f d) si (a b * c) (a b c) si (a b ? c) (a b c) no

    Las funciones de matching pueden ser ms complejas, incorporando otro tipo de patrones, como, por ejemplo, funciones. En este caso se podran preguntar cuestiones del tipo: si una s-expresin de la lista es un nmero, o un smbolo. (define (nulo? patron) (cond ((null? patron)) (else (and (eq? (car patron) '*) (nulo? (cdr patron)))))) (define (match patron lista) (cond ((null? patron) (null? lista)) ;; fin de ambas ((null? lista) (nulo? patron)) ;; fin de lista, y patron? ((eq? (car patron) '?) ;; metacaracter ? (match (cdr patron) (cdr lista))) ((eq? (car patron) '*) ;; metacaracter * (or (match (cdr patron) (cdr lista)) (match patron (cdr lista)))) ((eq? (car patron) (car lista)) ;; coinciden elementos (match (cdr patron) (cdr lista))) (else #f))) Normalmente, adems de saber si se produce o no matching, es interesante saber cmo se produce el mismo. En este sentido una variante de la funcin match, bastante habitual, consiste en incorporar en el patrn pares de valores metacaracter-variable, de modo que de producirse el matching la funcin retorna una lista en la que cada variable le corresponde la parte de la lista con la que se produjo dicho matching.

    Ejemplos:

    Patrn Lista Matching (a b (* x) c (? y) d) (a b (c d) e h c f d) ((x ((c d) e h )) (y f))

    (a b (* x) c) (a b c) (x ()) (a b (? x) c) (a b c) no