Este documento presenta los conceptos fundamentales de la programación funcional con Scheme. En menos de 3 oraciones, resume que la programación funcional se basa en evaluar funciones matemáticas en lugar de ejecutar instrucciones, Scheme es un lenguaje funcional minimalista basado en Lisp, y presenta conceptos como funciones de orden superior, recursión, currying y listas.
2. Programación Funcional
• Un paradigma de programación, donde
toda computación es resultado de evaluar
funciones matemáticas
• Enfatiza la evaluación de expresiones, no la
ejecución de instrucciones
• Las funciones son objetos de primer nivel
OALP-2005 All Rights Reserved
3. Ventajas y Características
• No hay operador de asignación
• Una vez se fija el valor de una variable, no
se puede cambiar
• No hay efectos secundarios
• Transparencia referencial
• Ideal para programación concurrente
• No hay bucles
• Facilita formalización y verificación
matemática
OALP-2005 All Rights Reserved
4. Cálculo-λ
• Modelo matemático formal de
computación
• Ideado por Alonzo Church y Stephen
Kleene en la década de 1930
• Define las funciones computables
• Equivalente a la Máquina de Turing
• Es el lenguaje de programación universal
más pequeño
OALP-2005 All Rights Reserved
5. Cálculo-λ: Definición Formal
• Definimos un conjunto infinito enumerable
de identificadores {a, b, ..., y, z, x1, x2, ...}
• El conjunto de todas las expresiones
lambda puede ser descrito por:
1. <expr> ::= <identificador>
2. <expr> ::= (λ <identificador> . <expr>)
3. <expr> ::= (<expr> <expr>)
OALP-2005 All Rights Reserved
6. Cálculo-λ: Definición Formal
• La ligadura de ocurrencias de variables está definida por
las siguientes reglas:
1. En una expresión de la forma V, donde V es una
variable, esta V es la única ocurrencia libre
2. En una expresión de la forma λ V. E las ocurrencias
libres son las ocurrencias libres en E, exceptuando
aquellas que estén en V. En este caso se dice que las
ocurrencias de V en E están ligadas por el λ antes de V
3. En una expresión de la forma (E E') las ocurrencias
libres son las ocurrencias libres en E y E'
• Se definen las siguientes transformaciones sobre
expresiones lambda: conversión-α, reducción-β,
conversión-η
OALP-2005 All Rights Reserved
7. Evolución de LISP y Scheme
LISP
(1958)
Scheme
(1974)
Common LISP
(1984)
Common LISP
ANSI X3.226
(1994)
Scheme R6RS
(2006)
CLOS
OALP-2005 All Rights Reserved
8. Características de Scheme
• Estándar minimalista
• Variables con alcance léxico
• Recursión de cola es mandatoria
• Sistema de macros higiénicos
• Promueve un estilo de programación funcional
• Los programas son datos y los datos son
programas
• Continuaciones explícitas
• Tipos dinámicos
• Recolector de basura
• Compilación nativa o máquina virtual
OALP-2005 All Rights Reserved
9. Entorno de Programación
• Un “mundo” que siempre
está en el REPL
• Lectura: chequea la
sintaxis de una
expresión
• Evaluación: ejecuta la
expresión
• Impresión: muestra el
resultado de la
ejecución
• El REPL se ejecuta en el
contexto de un entorno
global; puede visualizarse
como un diccionario que
asocia nombres a valores
OALP-2005 All Rights Reserved
10. Tipos de Datos
Tipo Ejemplos
booleano #t #f
entero 1 -2 3 42
racional 1/4 36/8
real 3.1416 1.2e+4
complejo 0+i 2+3i
carácter #c #space #tab #newline
símbolo 'var 'xyz
cadena "¡Hola, Mundo!"
lista '(1 2 3 4 5)
vector #(1 2 3 4 5)
estructura (define-struct persona (nombre cc dir tel))
OALP-2005 All Rights Reserved
11. Expresiones-S
• Son una convención para representar datos o
expresiones en un programa
• Se construyen usando notación prefija (polaca,
funcional) y paréntesis
• Pueden estar arbitrariamente anidadas
• Están conformadas por átomos y pares (celdas) cons
• Cuando una expresión-s denota la aplicación de un
procedimiento, se evalúa en orden aplicativo:
1. Se evalúan todas las sub-expresiones
2. Se aplica el procedimiento resultado de evaluar la
sub-expresión que está más a la izquierda (el
operador) a los argumentos, que son los valores
de las demás sub-expresiones (los operandos)
OALP-2005 All Rights Reserved
13. Convenciones
Convención Ejemplos
nulo '()
comentarios ;
cualquier valor distinto de #f #t
es verdadero (if (and 0 '()) "mal uso")
nombres en minúsculas (make-vector 5)
separados por guiones (make-hash-table 'equal)
conversiones (number->string 10)
predicados (null? '(1 2 3))
operaciones destructivas (define x 0)
(asignación) (set! x 1)
comparaciones (char>=? #z #a)
OALP-2005 All Rights Reserved
14. Formas Especiales
(and bool-exp1 ;; también case
... (cond ((bool-exp1) eval-exp1)
bool-expn) ...
((bool-expn) eval-expn)
(or bool-exp1 (else eval-exp))
...
bool-expn)
;; también let* y letrec
;; también when (let ((var1 exp1)
;; y unless ...
(if (bool-exp) (varn expn))
then-exp use-var1-...-varn)
else-exp)
OALP-2005 All Rights Reserved
15. Formas Especiales: Ejemplos
(and #t #f (/ 1 0)) (let ((x 2)
(y 3))
(or #f #t (/ 1 0)) (* x y))
(if (= 1 1) (let* ((x 2)
"then-exp" (y (+ x 1)))
(/ 1 0)) (* x y))
(define a 1) (letrec ((y x)
(cond ((= a 1) "uno") (x 5))
((= a 2) "dos") x)
(else "else"))
OALP-2005 All Rights Reserved
16. Definiciones
• Scheme usa la siguiente construcción para asociarle un
nombre a un valor:
(define <nombre-nuevo> <valor-inicial>)
• Las parejas nombre/valor son almacenadas y buscadas
en el diccionario que constituye el entorno global de
ejecución
• Es posible asociar un nombre a: valores constantes, el
resultado de evaluar otras expresiones,
procedimientos, etc.
• Cuando se define un nuevo procedimiento, éste tendrá
su propio entorno local de ejecución
• Las definiciones se pueden anidar (estructura de
bloque)
OALP-2005 All Rights Reserved
17. Definiciones: Ejemplos
(define x 7)
(define y (* x x))
(define suma +)
(define (pitagoras x y)
(sqrt (+ (sqr x) (sqr y))))
(define (es-par n)
(define (par?)
(if (= (modulo n 2) 0) #t #f))
(if (par?)
(print "es par")
(print "es impar")))
OALP-2005 All Rights Reserved
18. Procesos Recursivos Lineales
(define (factorial n)
(if (= n 1)
1
(* n (factorial (- n 1)))))
OALP-2005 All Rights Reserved
19. Procesos Recursivos Arbóreos
(define (fib n)
(cond ((zero? n) 0)
((= n 1) 1)
(else (+ (fib (- n 1))
(fib (- n 2))))))
OALP-2005 All Rights Reserved
21. Expresiones Lambda
• Similares a sus contrapartes en el cálculo-λ, son
funciones anónimas de la forma:
(lambda (<parámetros-formales>) <cuerpo>)
• Sin embargo, las expresiones lambda de Scheme
son formas especiales que pueden recibir más de
un parámetro
• Al evaluarlas, retornan un procedimiento
• El entorno en el que la expresión lambda fue
evaluada es recordado como parte del
procedimiento retornado
• La definición de una función y la asignación de un
nombre a ésta no siempre ocurren de manera
simultánea
OALP-2005 All Rights Reserved
22. Expresiones Lambda: Ejemplos
(lambda (x) (* x x))
((lambda (x) (* x x)) 8)
(define cuadrado
(lambda (x) (* x x)))
(cuadrado 8)
(define (cuadrado x)
(* x x))
(cuadrado 8)
(let ((x 1))
(define f (lambda (y) (+ x y)))
(let ((x 2))
(f 3)))
OALP-2005 All Rights Reserved
23. Currying
• Técnica que permite transformar una función que
recibe múltiples argumentos en una secuencia de
funciones que reciben un solo argumento
• Nombrada en honor al lógico Haskell Curry
• La clave está en recibir el primer argumento,
retornar una función que recibe el siguiente
argumento y así sucesivamente
• En ocasiones puede ser útil disponer de las
funciones “incompletas” que se obtienen cuando
no se proporcionan todos los argumentos
OALP-2005 All Rights Reserved
24. Currying: Ejemplos
(define (pitagoras? a b c)
(= (sqr a) (+ (sqr b) (sqr c))))
(pitagoras? 5 3 4)
(define pitagoras?
(lambda (a)
(lambda (b)
(lambda (c)
(= (sqr a) (+ (sqr b) (sqr c)))))))
(((pitagoras? 5) 3) 4)
(define por-n
(lambda (factor)
(lambda (numero)
(* factor numero))))
(define por-2 (por-n 2))
(por-2 192)
(define por-23 (por-n 23))
(por-23 86)
OALP-2005 All Rights Reserved
25. Combinador-Y
• Conocido como un “combinador de punto fijo” en términos
matemáticos; es una función que computa puntos fijos de
otras funciones
• Un punto fijo es un valor que permanece “fijo” después de
aplicar una función. Formalmente: x es un punto fijo de una
función f si f(x) = x
• En algunas formalizaciones matemáticas, como el cálculo-λ,
todas las funciones tienen un punto fijo
• Haskell Curry descubrió la función Y del cálculo-λ, que
computa un punto fijo para cualquier función que se le
proporcione
• Formalmente: Y = λf.(λx.(f (x x)) λx.(f (x x))) . Cumple con la
propiedad: ∀f f(Y(f)) = Y(f)
• Utilidad práctica: ¡permite definir funciones recursivas
anónimas!
OALP-2005 All Rights Reserved
26. Combinador-Y: Ejemplos
(define (Y X)
((lambda (proc)
(proc proc))
(lambda (proc)
(X (lambda (arg)
((proc proc) arg))))))
(define factorial
(Y (lambda (fact)
(lambda (n)
(if (= n 1)
1
(* n (fact (- n 1))))))))
(factorial 5)
OALP-2005 All Rights Reserved
27. Listas
• Recordar: LISP significa List Processor
• Las listas son la estructura de datos más
importante en Scheme, pero no son
fundamentales
• En cambio, los pares (celdas) cons si son
fundamentales
• Los procedimientos primitivos usados para
manipular pares son:
• cons : recibe dos argumentos y retorna un par
• car : retorna el primer componente de un par
• cdr : retorna el segundo componente de un par
OALP-2005 All Rights Reserved
28. Listas
• Las listas se construyen a partir de sucesivas
aplicaciones de cons, donde el primer elemento es
un valor y el segundo un par o nulo
(cons 1 (cons 2 (cons 3 (cons 4 (cons 5 '())))))
• Alternativamente, también se pueden crear listas
usando las siguientes formas especiales:
(list 1 2 3 4 5)
'(1 2 3 4 5)
OALP-2005 All Rights Reserved
30. Funciones de Orden Superior
• Reciben una función como argumento o retornan
una función como resultado
• Un concepto originado en las matemáticas. Por
ejemplo, las derivadas e integrales son funciones de
orden superior
• Ejemplos de uso:
• Una función que requiere otra función como
parámetro es una plantilla, donde la función
recibida modifica su comportamiento
• Si las funciones son cerraduras, pueden emplearse
para definir estructuras de control
• Útiles para construir iteradores y enumeradores
sobre estructuras de datos
OALP-2005 All Rights Reserved
31. Funciones de Orden Superior: Ejemplos
(define (f x) (sqr x))
(define (g x) (+ x 1))
(define h1 (compose f g))
(define h2 (compose g f))
(h1 5)
(h2 5)
(map + '(1 2 3) '(4 5 6))
(filter (lambda (x) (> x 5)) '(0 2 4 6 8 10))
(mergesort '(-1 7 10 0 2 2 -3)
(lambda (x y) (>= x y)))
OALP-2005 All Rights Reserved
32. Evaluación Perezosa
• Una expresión no se evalúa de inmediato, la evaluación se
pospone hasta el momento en que sea necesaria
• Scheme permite usar evaluación estricta o perezosa, pero
esta última debe programarse de manera explícita:
• (delay <expresión>) retorna una promesa, que puede
ser evaluada más adelante
• (force <promesa>) evalúa una promesa y retorna el
valor resultante
• El valor de una promesa es recordado, de tal forma que si
force es llamado nuevamente, se retorna el valor
previamente computado
• Útil para trabajar con estructuras de datos infinitas
• Permite “pegar” programas de una forma completamente
distinta
OALP-2005 All Rights Reserved
33. Evaluación Perezosa: Ejemplos
(let ((promesa (delay (+ 1 2))))
(list (force promesa) (force promesa)))
(define (next n)
(cons n (delay (next (+ n 1)))))
(define a-stream (next 0))
(define head car)
(define (tail stream)
(force (cdr stream)))
(head (tail (tail a-stream)))
OALP-2005 All Rights Reserved
34. Bibliografía
1. Theo D’Hondt y Wolfgang De Meuter. “Scheme in 2 days”.
Vrije Universiteit Brussel
2. Harold Abelson, Gerald Sussman y Julie Sussman.
“Structure and Interpretation of Computer Programs”.
MIT Press
3. John Hughes. “Why Functional Programming Matters”.
Institutionen för Datavetenskap, Chalmers Tekniska
Högskola
4. Combinador-Y.
http://www.ece.uc.edu/~franco/C511/html/Scheme/ycomb.ht
ml
5. Teach Yourself Scheme in Fixnum Days.
http://www.ccs.neu.edu/home/dorai/t-y-scheme/t-y-
scheme.html
6. Wikipedia. http://en.wikipedia.org/wiki/Main_Page
OALP-2005 All Rights Reserved