Primera Lección de Lisp: El poder de lisp

8 replies [Last post]
albertoefg
Offline
Joined: 04/21/2016

Hola, debido a que he visto interés en Lisp y a que me encuentro maravillado con su poder y la sencillez que tiene para aprenderlo voy a escribir esta mini-clase de Lisp.

Lisp es una familia de lenguajes de programación, es el segundo lenguaje más viejo aún en uso sólo después de Fortran. Tiene sus orígenes en las matemáticas, en el Calculo Lambda. Así que a deferencia de muchos lenguajes de programación es un lenguaje lleno de funciones y creado por matemáticos, no por ingenieros.

Esto hace que puedas crear con el de una manera que en otros lenguajes no es imaginable. Su belleza y sencillez sólo puede ser explicada aprendiéndolo, así que comencemos.

(Nota: Lisp es una familia de lenguajes en la actualidad están en uso Common Lisp, Scheme, Clojure, Emacs Lisp, entre otros. Para este tutorial usaré el dialecto Scheme que también es una familia de lenguajes, usaré GNU Guile, pero cualquier dialecto de Scheme puede funcionar, (mit-scheme, racket, Guile, etc.) la razón es que Scheme es lenguaje con la sintaxis más sencilla y al mismo tiempo es muy potente).

Scheme tiene la sintaxis más sencilla de todos los lenguajes utiliza sólo paréntesis.

(función argumento argumento .....)

En primer lugar en los paréntesis siempre se va la función y después los argumentos.

(+ 1 2)
=> 3

(+ 1 2 3)
=> 6

(+ 1 2 3 4 5 6 7 8 9 10)
=>55

(* 2 4)
=>8

Hasta aquí todo normal. Si acaso la única diferencia es que muchos lenguajes te pedirían hacer algo como "1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10" aquí ya vemos la diferencia en la cantidad de código.

Pero lo especial es que podemos pasar funciones dentro de otras funciones

(+ 1 (* 2 2))
=> 5

Aquí Scheme evalua primero los paréntesis internos y luego los externos.

(+ 1 (* 2 2))
(+ 1 (4))
(+ 1 4)
(5)
=> 5

Veamos un ejemplo más interesante definiendo funciones.

Voy a definir primero 4 funciones:

(define (suma a b)
(+ a b))

Aquí he definido la función suma a la que le debemos pasar 2 argumentos que he nombrado "a" y "b"

(suma 4 5)
=> 9

Ahora defino otras 3 que espero no necesiten explicación:

(define (resta a b)
(- a b))

(define (cuadrado a)
(* a a))

(define (cubo a)
(* a a a))

He creado 3 funciones más resta, elevar al cuadrado y cubo. Ahora mezclemos un poquito las cosas para que sean más interesantes.

(define (suma-de-cuadrados x y)
(suma (cuadrado x) (cuadrado y)))

En esta función he hecho de las funciones que definimos anteriormente "suma" y "cuadrado" con esto puedo llamar a suma-de-cuadrados con dos argumentos y elevará estos números al cuadrado y me dará su suma.

(suma-de-cuadrados 3 4)

(suma-de-cuadrados x y)
(suma (cuadrado x) (cuadrado y)))

Si llamamos a suma-de-cuadrados de la siguiente forma:

(suma-de-cuadrados 3 4)

Lisp lo va a evaluar así:

(suma-de-cuadrados 3 4)
(suma (cuadrado x) (cuadrado y)))

(suma (cuadrado 3) (cuadrado 4))

(suma (* 3 3) (* 4 4))

(suma (9) (16))

(+ 9 16)

(25)
=>25

Podemos hacer lo mismo con otras funciones (aguantemos un poco ya viene lo más interesante):

(define (resta-de-cuadrados x y)
(resta (cuadrado x) (cuadrado y)))

Si llamamos a resta de cuadrado con 5 y 4
(resta-de-cuadrados 5 4)

Lisp lo va a evaluar de la siguiente forma:

(resta (cuadrado 5) (cuadrado 4))

(resta (* 5 5) (* 4 4))

(resta (* 5 5) (* 4 4))

(resta (25) (16))

(- 25 16)

(9)

=>9

Espero que me hayan acompañado hasta este punto. Lisp está a punto de sorprenderlos

Como podemos ver suma-de-cuadrados y resta-de-cuadrados tienen muchas cosas en común:

(define (suma-de-cuadrados x y)
(suma (cuadrado x) (cuadrado y)))

(define (resta-de-cuadrados x y)
(resta (cuadrado x) (cuadrado y)))

Podríamos crear incluso otras:

(define (multiplicación-de-cuadrados x y)
(* (cuadrado x) (cuadrado y)))

(define (división-de-cuadrados x y)
(/ (cuadrado x) (cuadrado y)))

Salvo por el nombre y la operación son prácticamente lo mismo. ¿Porque no crear una función que haga cualquiera de estas operaciones?

Con Lisp podemos crear una Función de más alto nivel (Higher Order Functions).

(define (operación-de-cuadrados x y operación)
(operación (cuadrado x) (cuadrado y)))

Aquí lisp necesita que le pasemos 3 argumentos x, y y una operación que puede ser suma, resta, multiplicación, etc.

Veamos esto en acción

(operación-de-cuadrados 3 4 suma)

Lisp hará la sustitución así:

(define (operación-de-cuadrados x y operación)
(operación (cuadrado x) (cuadrado y)))

(operación-de-cuadrados 3 4 suma)
(operación (cuadrado x) (cuadrado y))

(operación-de-cuadrados 3 4 suma)
(suma (cuadrado 3) (cuadrado 4))

(suma (cuadrado 3) (cuadrado 4))

(suma (* 3 3) (* 4 4))

(suma (9) (16))

(+ 9 16)

(25)
=>25

Ahora podemos pasarle una resta en lugar de una suma

(operación-de-cuadrados 5 4 resta)

Lisp hará la sustitución así:

(define (operación-de-cuadrados x y operación)
(operación (cuadrado x) (cuadrado y)))

(operación-de-cuadrados 5 4 resta)
(operación (cuadrado x) (cuadrado y))

(operación-de-cuadrados 5 4 resta)
(resta (cuadrado 5) (cuadrado 4))

(resta (cuadrado 5) (cuadrado 4))

(resta (* 5 5) (* 4 4))

(resta (* 5 5) (* 4 4))

(resta (25) (16))

(- 25 16)

(9)

=>9

Como podemos ver, con definir operación-de-cuadrados hemos definido una función que ya no necesita crear suma-de-cuadrados, resta-de-cuadrados, multiplicacíon-de-cuadrados.

Veamos un ejemplo mas:

(define (operación-de-cuadrados x y operación)
(operación (cuadrado x) (cuadrado y)))

(operación-de-cuadrados 5 4 /)
(operación (cuadrado x) (cuadrado y))

(operación-de-cuadrados 5 4 /)
(/ (cuadrado 5) (cuadrado 4))

(/ (cuadrado 5) (cuadrado 4))

(/ (* 5 5) (* 4 4))

(/ (* 5 5) (* 4 4))

(/ (25) (16))

(/ 25 16)

(1.5625)

=> 1.5625

Si, en realidad definí las funciones suma, resta, multiplicación y división por motivos de enseñar, pero pude bien haber pasado el puro símbolo "+" "-" "*" "/" y lisp hubiera entendido perfectamente.

Pero aún mejor podemos crear una función de más alto nivel

(define (muchas-operaciones x y operación-1 operacion-2 operacion-3)
(operación-1 (operacion-2 x) (operacion-3 y)))

(muchas-operaciones 5 6 suma cubo cuadrado)

Y lisp evaluara de la siguiente forma:

(define (muchas-operaciones x y operación-1 operacion-2 operacion-3)
(operación-1 (operacion-2 x) (operacion-3 y)))

(muchas-operaciones 5 6 + cubo cuadrado)

(muchas-operaciones 5 6 + cubo cuadrado)
(operación-1 (operacion-2 x) (operacion-3 y))

(muchas-operaciones 5 6 + cubo cuadrado)
(+ (cubo 5) (cuadrado 6))

(+ (* 5 5 5) (* 6 6))

(+ (125) (36))

(+ 125 36)

(+ 125 36)

(161)

=> 161

Como podemos ver hemos creado funciones que van como argumentos dentro de otras funciones. Pudiendo crear funciones de cada vez más alto nivel.

Muy bien. Hasta aquí. Creo que he avanzado demasiado y quizás algunas partes no han quedado muy claras. Si les ha interesado, puedo grabar algún video tutorial o ayudarles por aquí para dejar todo más claro.

He tenido que dejar fuera conceptos importantes como Lambda o las funciones recursivas e iterativas. Pero eso será después.

Espero que tengan interés en aprender Lisp, ya que es muy sencillo de aprender por todos. Incluso para los que, como yo, no hemos estudiado computación.

deshecho humano
Offline
Joined: 10/15/2016

No sabía que GNU Guile tuviese relación con lisp. Otro "dialecto" al que le tengo que echar un vistazo.

Cuando he llegado a la parte de (operacion-de-cuadrados x y operacion) me ha sorprendido que pusieras la operación al final ya que, como has dicho, los operadores suelen ir al principio y luego puedes asignar cualquier número de parámetros (+ 1 2 3 4 5 6). Sin embargo, ha cobrado sentido en el siguiente paso, cuando encadenabas varias operaciones sobre los mismos números. En las funciones predefinidas de suma y resta, tengo entendido que los parámetros que les puedes pasar son virtualmente infinitos. ¿Habría alguna forma de aplicar "infinitas" operaciones a ese par de valores? ¿Y si se puede, se podrían aplicar "infinitas" operaciones a "infinitos" números a tu función? Cuando hablo de infinito me refiero a un número indeterminado que estaría limitado por la memoria disponible en el sistema o algo similar.

Como puedes ver, a mí el tema me interesa pero como retroalimentación tengo que decir que visto a así de sopetón y en este formato para otras personas pueda resultar un poco "duro". Aunque yo me tragué ayer 2 horas de introducción a haskell sin tener ni idea de programación funcional y eso sí que fue duro, je je.

Gracias por compartir.

albertoefg
Offline
Joined: 04/21/2016


Debido a la sugerencia de Desecho Humano, he grabado un video que espero resulte más claro para todos

https://archive.org/details/lisp-principiantes-1

Este primer video es para mostrar el poder de lisp. Trataré de hacer más videos para explicar más de lisp.

farliz
Offline
Joined: 05/24/2015

Muy buen video albertoefg. Es de buena calidad y se entiende muy bien, gracias por compartirlo.

Mr_Robot
Offline
Joined: 08/15/2015

Gracias por compartirlo

dcapeletti
Offline
Joined: 04/03/2014

Gracias por compartir...

Sds

albertoefg
Offline
Joined: 04/21/2016

Gracias, espero que el vídeo se entienda un poco mejor.
Es mi primer tutorías y no sabía ni por donde empezar :/

dcapeletti
Offline
Joined: 04/03/2014

Has comenzado bien. Me fue útil. Gracias.

Sds

saravia
Offline
Joined: 11/06/2016

un tutorial mas avanzado porfavor está muy bueno explicas muy bien aca en mi casa no pensabamos encontrar tutoriales en español estamos muy interezados muy felicez de que te hagas estos aportes, mas videos porfavor ya tienes fans jeje