Está en la página 1de 16

17/09/2018

PROGRAMACION LOGICA Y FUNCIONAL


Investigación Unidad 2: Características de la programación funcional.

7° Semestre Grupo S-701


Instituto Tecnológico Superior de Panuco
Ingeniería en Sistemas Computacionales
Alumno: Roque Oswaldo Cazares García.
Docente: Ing. Gil Santana Esparza.
UNIDAD 2: CARACTERISTICAS DE LA PROGRAMACION
FUNCIONAL
Índice

Introducción……………………………………………………………………………………………………………………….3

Programación imperativa y sus inconvenientes………………………………………………………………….3

El Lisp y la programación funcional…………………………………………………………………………………….5

El cálculo Lambda………………………………………………………………………………………………………………6

Características de la programación funcional…………………………………………………………………….8

Conclusión………………………………………………………………………………………………………………………..14

Mapa Conceptual……………………………………………………………………………………………………………..15

Bibliografía……………………………………………………………………………………………………………………….16

2
Introducción

Para introducirnos en el tema de la programación funcional vale la pena investigar


acerca de los orígenes de la programación funcional y pueden rastrearse al
matemático Alonzo Church, que trabajaba en la Universidad de Princeton, y, al igual
que otros matemáticos de allí, estaba interesado en la matemática abstracta,
particularmente en el poder computacional de ciertas máquinas abstractas.

Las preguntas que se hacía eran, por ejemplo: si dispusiésemos de máquinas de un


ilimitado poder de cómputo, ¿qué tipos de problemas se podrían solucionar?, o ¿se
pueden resolver todos los problemas?
Para contestar este tipo de preguntas, Church desarrolló un lenguaje abstracto,
denominado Cálculo Lambda, que el cual sólo realizaba evaluación de expresiones
usando funciones como mecanismo de cómputo. Este lenguaje abstracto no tenía en
cuenta limitaciones concretas de implementación de ningún tipo.

Al mismo tiempo que Church, otro matemático, Alan Turing, desarrolló una máquina
abstracta para intentar resolver el mismo tiempo de problemas planteados por Church.
Después se demostró que ambos enfoques son equivalentes.

PROGRAMACIÓN IMPERATIVA Y SUS INCONVENIENTES.

Los lenguajes imperativos como FORTRAN, COBOL, Pascal, Smalltalk y Ada se


basan en la sentencia (frase descriptiva que es transformada en una o más
instrucciones de máquina), que constituye la unidad de trabajo de programas escritos
en estos lenguajes. El efecto de cada sentencia se combina con el efecto de otras
para generar el resultado esperado de un programa. En estos lenguajes, la noción
fundamental de memoria y nombre son representados por el concepto de una variable.
Una variable en un lenguaje de programación imperativo es esencialmente una celda
de memoria nominada en la cual los valores son almacenados. Como su nombre lo
indica, una variable puede tomar valores distintos durante su existencia en el
programa. Muy unida a la arquitectura de la memoria está la noción de que todo
cómputo debe ser almacenado, es decir, asignado a una celda de memoria. Esto
responde a la presentación de la asignación en estos lenguajes. Por otro lado, un
programa en un lenguaje imperativo, usualmente viene acompañado de una
secuencia de pasos que se repiten mediante estructuras iterativas. La esencia de los

3
lenguajes imperativos es la repetición y el cómputo paso a paso de valores a bajo nivel,
y la asignación de estos valores a posiciones de memoria. Frecuentemente, este no
es el nivel de detalle que nosotros queremos manejar a la hora de programar una
aplicación de gran tamaño. De ahí, que muchos lenguajes de programación tratan de
esconder u ocultar los detalles de bajo nivel asociados a la máquina. Un buen ejemplo
de esto es el manejo de expresiones tales como:

max := if (x>y) then x else y;

Las expresiones suelen ser usadas porque son simples y jerárquicas. Pueden ser
combinadas para construir expresiones complejas. Por ejemplo, en ALGOL 68 el
último valor computado en un bloque es el valor del bloque. Sin embargo, en general
en este lenguaje, las expresiones también están sujetas a nominaciones, repeticiones
y asignaciones.

Los procedimientos permiten construir componentes independientes, pero los


parámetros con paso por referencia y variables globales interfieren con el concepto a
alto nivel por revelar el concepto de direccionamiento de cada celda de memoria.

Quizá el problema más serio en lenguajes imperativos es el verificar la correctitud de


programas. Esta dificultad es causada debido a que esta correctitud en general
depende del contenido de cada celda de memoria. El estado del cómputo es
determinado por el contenido de celdas de memoria. Para entender los ciclos tenemos
que ejecutarlos mentalmente. Para observar el progreso del cómputo a través del
tiempo, tenemos que fotografiar la memoria en cada paso del programa (en cada
instrucción). Esto es bastante tedioso sobre todo cuando el programa es extenso, o
usa gran cantidad de datos. Sin embargo, las reglas de alcance reducen el número de
variables que se pueden ver en un momento determinado.

Los problemas con los lenguajes imperativos se pueden sumarizar en términos de la


transparencia referencial. Se dice que un sistema es referencialmente transparente si
el significado del todo puede ser determinado únicamente por el significado de sus
partes. Las expresiones matemáticas son referencialmente transparentes. Por
ejemplo, en la expresión matemática f(x)+g(x), nosotros podemos sustituir otra función
f' por f, siempre y cuando nosotros sabemos que produce el mismo valor que f. Si la
misma expresión está en Pascal (o en algún lenguaje imperativo convencional), no se
puede asegurar esta propiedad. Además, si f y g tienen parámetros por referencia, o
modifican alguna variable global, no se puede asegurar que f(x)+g(x)=g(x)+f(x), ni que

4
f(x)+f(x) = 2*f(x). Esto se debe a que el resultado de la expresión depende de la historia
de cómputo de cada subexpresión.

La asignación, parámetros por referencia y variables globales son las principales


razones por lo que los lenguajes imperativos no son referencialmente transparentes.
La carencia de la transparencia referencial hace a los programas difíciles de leer,
modificar y de comprobar su correctitud. Los lenguajes imperativos también se tienen
presente los problemas de alias (cuando dos nombres constituyen el mismo objeto en
la misma unidad de programa) y efectos laterales (cuando se modifican variables no
locales). (Carmona, Rhadamés. (1996). Programacion Funcional: Lisp.)

“Básicamente los inconvenientes de usar la programación imperativa radican


en poder comprobar si el programa es correcto o no ya que depende del
contenido de cada una de las celdas de memoria y además de cuando se
modifican variables no locales etc.”

El Lisp y la Programación Funcional

El Lisp es el lenguaje más importante del paradigma funcional. De él nacen una


enorme variedad de dialectos, de los que Scheme es uno de los más extendidos en
ámbitos académicos en la actualidad.

El origen del Lisp se remonta al año 1956, en el que John McCarthy estaba buscando
una solución para programar el computador IBM 704, en los primeros proyectos de
inteligencia artificial. A finales de 1958 McCarthy, ya profesor de Ingeniería Electrónica
y Marvin Minsky, profesor de matemáticas, ambos en el MIT, comenzaron el MIT
Artificial Intelligence Project e iniciaron la implementación del Lisp.

Entre los elementos que inspiraron el Lisp se encuentra el formalismo de


programación funcional llamado cálculo lambda.

Uno de los factores que contribuyeron más al éxito del Lisp es su carácter pragmático.
No se trata de un lenguaje puramente funcional ni declarativo, sino que es posible
realizar sentencias imperativas en las que se modifican el valor de posiciones de
memoria a las que hacen referencia a variables.

(https://rua.ua.es/dspace/bitstream/10045/4032/1/tema02.pdf)

5
“El Lisp se origina por la necesidad de Jhon McCarthy para programar el
computador IBM 704 que era de los primeros proyectos de inteligencia artificial
por el año de 1958, ya hace bastantes años se empezaba a requerir la
inteligencia artificial y el calculo lambda fue uno de los elementos que inspiro
dicho proyecto.”

El cálculo lambda

La principal base teórica del paradigma funcional es el cálculo lambda (lambda


calculus en inglés) desarrollado en la década de los 30 por Alonzo Church como
modelo de computación con el mismo poder computacional que una Máquina de
Turing. El cálculo lambda proporciona una notación muy simple (pero bastante críptica
al mismo tiempo) de definición de funciones matemáticas. Sólo son necesarias tres
reglas sintácticas para definir las expresiones del cálculo lambda:

(https://rua.ua.es/dspace/bitstream/10045/4032/1/tema02.pdf)

La abstracción sirve para construir nuevas funciones matemáticas en base a


expresiones previamente definidas. La aplicación, como su nombre indica, denota la
aplicación de una función. Veamos algunos ejemplos simplificados (necesitaríamos
algo más de tiempo para explicar la versión real). Una función es una construcción
matemática que acepta una entrada y produce una salida. Supongamos que tenemos
una función "pintado-amarillo" que produce las siguientes salidas a partir de las
correspondientes entradas:

(https://rua.ua.es/dspace/bitstream/10045/4032/1/tema02.pdf)

Podemos usar el cálculo lambda para describir esta función:

lambda x.pintado-amarillo x

Esto es lo que se denomina una expresión-lambda.

Si queremos aplicar la función a un argumento, aplicamos la siguiente sintaxis:

6
(lambda x.pintado-amarillo x)cuadrado -> pintado-amarillo cuadrado

El resultado de aplicar una expresión-lambda también puede ser una función, como
en el siguiente ejemplo en el definimos un "constructor de funciones coloreadoras":

lambda y.lambda x.pintado-y x

Podemos usar esto para crear una función que pinta de color verde:

(lambda y.lambda x.pintado-y x)verde -> lambda x.pintado-verde x

Las funciones también pueden ser argumentos de otras funciones, como esta función
"aplicar-a-mickey-mouse":

lambda f.(f)mickey-mouse

Podemos entonces llamar a esta función con la función "pintado-amarillo" para pintar
a mickey-mouse de amarillo:

(lambda f.(f)mickey-mouse)lambda x.pintado-amarillo x

-> (lambda x.pintado-amarillo x)mickey-mouse

-> pintado-amarillo mickey-mouse

La evaluación de las expresiones-lambda se basa en dos reglas de sustitución de


argumentos formales por los argumentos reales, denominadas reducción-alfa y
reducción-beta. No vamos a verlo en detalle, sólo comentar la regla de reducción-beta:
La aplicación de una expresión-lambda a un argumento se consigue reemplazando
en el cuerpo de la expresión-lambda su variable ligada por el argumento:

(lambda x.P)Q -> [Q/x]P

donde [Q/x]P significa la substitución de x por Q en cualquier ocurrencia libre de x en


P.

(https://rua.ua.es/dspace/bitstream/10045/4032/1/tema02.pdf)

(http://en.wikipedia.org/wiki/Lambda_calculus)

“El autor nos comenta acerca de la implementación del cálculo lamba en la


programación funcional que fue desarrollada en la década de los 30 por Alonzo
Curch y que tenía el mismo poder computacional que una máquina de Turing y
que son necesarias 3 reglas para definir expresiones, cada una de estas reglas
sirven para diferentes cosas aplicadas a una función.”

7
Características de la programación funcional

Entre las características de la programación funcional destacamos:

 Programación declarativa
 Definición y evaluación de funciones
 Uso de la recursión
 Funciones como datos primitivos
Definición y evaluación de funciones

Cualquier lenguaje funcional permite la definición de nuevas funciones como forma de


abstracción, y la evaluación de expresiones como forma de computación.

Ya hemos visto cómo definir y evaluar funciones en Scheme:

(define (cuadrado x) (* x x)) (+ (cuadrado 3) (cuadrado 2))

Estamos definiendo una función con un argumento formal (x) que tiene como cuerpo
la expresión (* x x) y le estamos dando el nombre de cuadrado. Después evaluamos
una expresión en la que llamamos a la función recién definida y a la función primitiva
'+'.

(https://rua.ua.es/dspace/bitstream/10045/4032/1/tema02.pdf)

“Vemos como hacemos definiciones y como evaluamos las funciones, por


ejemplo, en clase hicimos ejercicios en Lisp muy similares al ejemplo usado por
el autor en Scheme, donde aprendimos sobre este tema”.

Uso de la recursión

Otro elemento común a todos los lenguajes funcionales es el uso de la recursión para
expresar funciones que en otros lenguajes se expresan con iteraciones. Muchas veces
es más sencillo y natural utilizar una recursión en la definición de una función.

Factorial

primer ejemplo es la típica función factorial que calcula el factorial de un número.


Matemáticamente, el factorial de un número se puede expresar con la siguiente
formulación:

8
Esta expresión tiene una traducción directa en Scheme:

(define (factorial x)

(if (= x 0)

(* x (factorial (- x 1)))))

>(factorial 8) 40320

>(factorial 30) 265252859812191058636308480000000

(https://rua.ua.es/dspace/bitstream/10045/4032/1/tema02.pdf)

“En este otro punto se habla sobre la recursión en la programación funcional y


es común en todos los lenguajes funcionales, el autor en este ejemplo utiliza
Scheme, más sin embargo nosotros en clase utilizamos Lisp e incluso hicimos
un ejemplo de sacar factorial”

(defun factorial(x)

if (= x 0) 1 (* x(factorial (- x 1)))

9
Sumatorio

Otro ejemplo de función recursiva es el sumatorio desde un número inicial hasta un


límite. Matemáticamente se expresa con la siguiente fórmula:

En Scheme:

(define (sumatorio min max)

(if (> min max)

(+ min (sumatorio (+ 1 min) max))))

> (sumatorio 3 12)

75

(https://rua.ua.es/dspace/bitstream/10045/4032/1/tema02.pdf)

“La sumatoria es otra forma de recursividad y el autor nos muestra un ejemplo


en Scheme de como se hace en la programacion funcional”

Funciones como tipo de datos primitivo

Otra característica fundamental de los lenguajes funcionales es que las funciones son
consideradas un tipo de dato primitivo.

Un tipo de dato primitivo es aquel que:

 Puede ser el valor de una variable (en terminología de programación funcional:


nombrado por un identificador)
 Un argumento de una función
 El valor que devuelve una función
 Componente de una estructura de datos mayor

Por ejemplo, los números, los caracteres o las cadenas son datos primitivos en la
mayor parte de lenguajes de programación. Sin embargo, resulta poco frecuente que

10
podamos hacer todas estas cosas con una función o un procedimiento. Es una de las
características más sorprendentes de los lenguajes funcionales:

Una variable puede tener como valor un procedimiento

 Podemos definir una función que toma como argumentos otras funciones
 Podemos devolver un procedimiento como resultado de una llamada a otro
procedimiento
 Podemos construir estructuras de datos que contengan procedimientos como
elementos (listas de procedimientos, por ejemplo)

Vamos a ver un ejemplo de cada una de estas características en Scheme:

Una función puede ser nombrada con un identificador.

(define suma +) suma >(suma 1 2 3) 6 (define + -) (+ 4 2) 2 (define + suma)

En Scheme cuando se define una función con un nombre, la relación entre la función
y el nombre es la misma que la relación entre una variable y un valor. El nombre de la
función es el identificador que está ligado al procedimiento.

(https://rua.ua.es/dspace/bitstream/10045/4032/1/tema02.pdf)

“Podemos comprobar en el ejemplo que los símbolos 'suma', '+' o '-' no son más
que identificadores ligados a procedimientos”

Una función puede ser el argumento de otra función.

Por ejemplo, podemos definir la siguiente función aplicar que toma como argumento
una función f 2 argumentos y realiza la llamada a f con los 2 argumentos:

(define (aplicar f x y) (f x y)) > (aplicar + 2 3) 5 > (aplicar * 2 3) 6 (aplicar word 'hola
'adios)

holaadios

En el siguiente ejemplo definimos una función que toma dos procedimientos unarios
(de un argumento) f y g y los aplica a un número:

(define (aplicar-2 f g x) (f (g x))) (define (5+ x) (+ 5 x)) (define (doble x) (* 2 x)) > (aplicar-
2 5+ doble 8) 21

11
(https://rua.ua.es/dspace/bitstream/10045/4032/1/tema02.pdf)

“El autor nos demuestra como una funcion puede ser el argumento de otra con
el ejemplo de hacer una suma del 5 mas el doble del 8 que nos da igual a 21.”

Una función puede ser el valor devuelto por otra función.

Vamos a definir una función que devuelve otra función. La siguiente función hacer-
suma1 define en su interior la función suma1 y la devuelve.

(define (hacer-suma1) (define (suma1 x) (+ x 1)) suma1) (define f (hacer-suma1)) > (f


3) 4

Vemos que el resultado de la llamada a hacer-suma1 se guarda en la variable f y


después comprobamos que realmente es una función que suma 1.

Podemos complicar un poco más el ejemplo definiendo un parámetro con el número


que queremos sumar en la función que devolvemos. Así, la función hacer-sumak
construye otra función que suma el parámetro k al argumento x que se le pasa:

(define (hacer-sumak k) (define (sumak x) (+ x k)) sumak) (define g (hacer-sumak 8)) >
(g 5) 13

(https://rua.ua.es/dspace/bitstream/10045/4032/1/tema02.pdf)

“Básicamente este ejemplo en Scheme es una forma mas elaborada de hacer


una sumatoria, la función que declaramos nos va a regresar el valor con otra
función, en el primer ejemplo es una sumatoria de (+ 1 3) que es 4 y en el
segundo (+ 8 5) 13.”

Una función puede ser parte de una estructura de datos mayor

Veamos un ejemplo que a primera vista puede parecer algo complicado (ya que
mezclamos recursión y funciones como tipos de datos primitivos). Se trata de construir
una función que recorra una lista de funciones unarias (se aplican a un único
argumento) y las vaya aplicando a un argumento.

Vamos a verlo paso a paso. Vamos a ver en primer lugar qué es eso de una lista de
funciones unarias.

12
(define (2+ x) (+ 2 x)) (define (10* x) (* 10 x)) (define (cuadrado x) (* x x)) (define lista-
funcs (list cuadrado 2+ 10*))

La lista lista-funcs es una lista que contiene funciones (no identificadores). Vamos a
comprobarlo. Si miramos su primer elemento el intérprete nos dice lo siguiente:

> (car lista-funcs) #procedure:cuadrado

Podemos probar qué ese primer elemento es un procedimiento aplicándolo a un


número y viendo que devuelve su cuadrado:

>((car lista-funcs) 3) 9

Podemos ahora hacer la función aplica-funcs que recorre la lista de funciones y las
aplica a un número:

(define (aplica-funcs lista x) (if (empty? (cdr lista)) ((car lista) x) ((car lista) (aplica-funcs
(cdr lista) x))))

El caso base de la recursión es el caso en que la lista de funciones tiene un único


argumento (eso es lo que significa (empty? (cdr lista))). En ese caso se devuelve el
resultado de aplicar esa única función al número x.

En el caso general, aplicamos la primera función al resultado de la llamada recursiva


de aplicar el resto de funciones al número x.

(https://rua.ua.es/dspace/bitstream/10045/4032/1/tema02.pdf)

“En este ejemplo el autor nos quiere dar a entender como una función puede ser

parte de una estructura de datos mayor, por eso nos enseña como poner una

lista de funciones unarias, se esta utilizando car el cual es utilizado en Lisp para

crear listas.”

13
Conclusión

Yo creo que la programación funcional es muy poderosa ya que nos permite pensar

de una forma totalmente diferente cuanto tenemos que resolver un problema, como

cuando utilizamos la lógica matemática de alto nivel, podemos resolver varios

problemas de forma cotidiana, mas sin embargo no son las formas más rápidas y

eficientes de hacerlas (programación imperativa) en cambio con la programación

funcional podemos resolver esos problemas cotidianos de forma mucho más rápida y

más eficiente (memoria) que con el otro paradigma de programación. Incluso hay

ocasiones que solo con este paradigma podremos resolver problemas que queremos

resolver (por ejemplo, del tema de la inteligencia artificial). Sin lugar a dudas este

paradigma será un aprendizaje importante en nuestra formación en la carrera y un

arma poderosa en la Ingeniería de software pues los programas con este paradigma

carecen de efectos colaterales.

14
Mapa Conceptual

15
Referencias

Structure and Interpretation of Computer Programs

(http://mitpress.mit.edu/sicp/fulltext/book/book.html), Abelson y Sussman, MIT Press

1996 (pp. 13-17).

Encyclopedia of Computer Science (Wiley, 2000).

Concepts in Programming Languages, John C. Mitchel, Cambridge University Press,

2003. Cap. 3, 4.2 y 4.4.

(http://en.wikipedia.org/wiki/Lambda_calculus) (Wikipedia)

[GLA-84] Glacer H. & Hankin C. & Till D. "Principles of Functional Programming".

Prentice-Hall. 1984.

Jones, J & Maynard, C. & Stewart, I. "The Art of Lisp Programming". Springer-Verlag,

1990.

McCarthy & Abrahams & Edwars & Hart & Levin. "LISP 1.5 Programmer's Manual".

Cambridge, Massachusets: The MIT Press, 1962.

Patrick, H. & Berthold K. "LISP". 3ra. edición. 1989.

Peterson, H. "Functional Programming Application and Inplementation". Prentice-Hall,

1980.

[SET-89] Sethi, Ravi. "Programming Languajes, Concepts and Constructs". Addison-

Weley, 1989.

Weissman Clark. "LISP 1.5 Primer". Dickenson Publishing Company. 1967.

16