Está en la página 1de 9

Lisp, un Lenguaje que se Aprende en 10 Minutos http://www.davidam.com/docu/un-lenguaje-en-diez-minutos.

html

Lisp, un Lenguaje que se Aprende en


10 Minutos
Volver a davidam.com

1 Introduccin
Lisp es uno de los lenguajes de programacio n ma s simples que existen. Unos pocos conceptos, siete u
ocho, abarcan todo el lenguaje. Hablamos de la especi icacio n base del lenguaje, naturalmente. Los
distintos dialectos han ido extendiendo el lenguaje y an adiendo nuevas construcciones. Pero, y esto
es tremendamente importante, lo han hecho siempre utilizando esos mismos conceptos base. Eso
implica que, una vez conocidos los pocos conceptos que constituyen la base del lenguaje, el lector
sera capaz de interpretar pra cticamente cualquier programa Lisp.

Esta circunstancia (ser un lenguaje muy sencillo) permite abarcar su estudio desde un punto de vista
bastante original, y extremadamente productivo. En el aprendizaje de cualquier otro lenguaje de
propo sito general se tiende (muy acertadamente) a comenzar impartiendo las caractersticas a nivel
de lenguaje como le xico (co mo se escribe una palabra reservada), sintaxis (en que consiste una
sentencia, o una expresio n) y sema ntica (co mo evalu a una expresio n aritme tica). Una vez conocidos
estos aspectos del lenguaje, por lo general se pasa a exponer las caractersticas ma s espec icas del
entorno y, por tanto, de co mo se traducen las sema nticas expresadas en el lenguaje en una ejecucio n
(las variables globales en C se inicializan a 0, cosa que no pasa con las variables locales, que
contienen un valor inde inido, por ejemplo).

En el caso de Lisp, vamos a hacerlo al reve s: partiendo del conocimiento de co mo funciona (o co mo


"ve el mundo") un inte rprete de Lisp, pasaremos a exponer los aspectos ma s importantes del
lenguaje.

2 Memoria: qu hay?
Como podemos ver en la igura 1, un inte rprete Lisp mantiene dos regiones en memoria,
diferenciadas:

1 de 9 07-08-17 17:35
Lisp, un Lenguaje que se Aprende en 10 Minutos http://www.davidam.com/docu/un-lenguaje-en-diez-minutos.html

Figura 1: Memoria de un inte rprete Lisp

La memoria de datos (a la derecha, en la igura).


Un diccionario de te rminos, que asocia smbolos con dos punteros (a la izquierda, en la igura).

El inte rprete utiliza la memoria de datos para almacenar todos los datos presentes en un proceso
Lisp. Se trata de una memoria "idealizada", en el sentido de que es capaz de almacenar distintos
tipos de datos (conocidos como "a tomos" en Lisp) y cada uno de ellos ocupa una sola posicio n. Por
ejemplo, un nu mero entero y una cadena de caracteres son ambos a tomos y, por tanto, ambos
pueden almacenarse en una posicio n de la memoria de datos (la cadena de caracteres es ato mica, y
no esta compuesta de partes).

El contenido de la memoria de datos esta sujeta a recoleccio n de basura. Como veremos ma s


adelante, existen referencias a los elementos de la memoria desde el diccionario de smbolos.
Cuando un a tomo ya no es referenciado (ni directa ni indirectamente) es candidato a recoleccio n de
basura, y el colector de memoria marcara la posicio n que ocupa en la memoria como disponible.

As pues, la memoria de datos contiene a tomos Lisp. Pese a que los tipos de a tomos pueden variar
dependiendo del dialecto que se este utilizando (Common Lisp, Emacs Lisp, Scheme, etc.) existe un
conjunto de tipos bastante estandarizado que podremos encontrar en cualquiera de ellos.
Vea moslos.

Nu meros. Lisp permite almacenar en la memoria de datos a tomos de tipo nume rico. Esto
incluye tanto nu meros enteros (2, 4, 32222) como reales (2.0, 4.43, 3.2222). En forma escrita,
los a tomos nume ricos utilizan el le xico habitual de dgitos consecutivos, utilizando un punto
para denotar la coma decimal. En la igura 1 podemos ver dos a tomos nume ricos: uno consiste
en el nu mero 1 y el otro en el nu mero 2.
Cadena de caracteres. En Lisp las cadenas de caracteres son a tomos, y pueden contener
cualquier cara cter. En forma escrita, los a tomos cadena de caracteres utilizan la representacio n
habitual de caracteres encerrados entre comillas dobles. En la igura 1 podemos ver un a tomo
de tipo cadena de caracteres en la memoria de datos, siendo su valor "foo".
Smbolos. Los smbolos son nombres. Su u nica sema ntica es que dos smbolos de distinto
nombre se consideran como smbolos distintos. En forma escrita, los smbolos utilizan un
le xico similar al de los identi icadores de otros lenguajes de programacio n, aunque permiten la

2 de 9 07-08-17 17:35
Lisp, un Lenguaje que se Aprende en 10 Minutos http://www.davidam.com/docu/un-lenguaje-en-diez-minutos.html

inclusio n de muchos caracteres alfanume ricos dada la particular (y generosa) forma sinta ctica
de Lisp, como guiones y barras. En la igura 1 podemos ver dos a tomos de tipo smbolo, siendo
sus valores a y c.

Un aspecto importante de la memoria de datos es que, a diferencia de casi todos los dema s modelos,
no contiene variables, sino valores. Eso signi ica que las cajas (los a tomos) de las que se compone no
deben verse como "contenedores" de valores, sino como la idealizacio n del valor en s. Si un
programa Lisp necesita un valor nume rico 0, encontrara dicho valor nume rico en una zona de la
memoria de datos. Si necesita un 22, en lugar de "sobreescribir" el valor 0, utilizara el a tomo de valor
22 presente en otra zona de la memoria de datos. Como es natural, las distintas implementaciones
utilizan varias optimizaciones, ya que no es pra ctico tener en la memoria de datos todos los posibles
valores nume ricos y de cadenas de caracteres aunque, conceptualmente, puede considerarse que los
contiene.

De esta forma, un proceso Lisp puede almacenar valores nume ricos y de cadena de caracteres en la
memoria de datos. Esto es su iciente para efectuar operaciones aritme ticas o simbo licas. Sin
embargo, es conveniente y necesario el poder estructurar los datos. En nuestro caso, necesitamos
alguna forma de construir estructuras involucrando a a tomos almacenados en la memoria de datos.
Para ello, Lisp proporciona una u nica primitiva de estructuracio n: los llamados pares o conses
(abreviatura de "cons cell").

Los pares tambie n son a tomos Lisp, y por consiguiente tambie n se almacenan en la memoria de
datos. Consisten en un par de valores, que son referencias a otros a tomos presentes en la memoria.
De esta forma, podemos construir estructuras de pares. En la igura 1 hemos representado los
conses por dos cajitas consecutivas (que, ojo, componen un solo a tomo). La primera cajita esta
marcada por la palabra "car", y la segunda por "cdr". En un par, el "car" es la referencia al primer
elemento del par, y el "cdr" es la referencia al segundo elemento del par (esta nomenclatura tan
particular obedece a motivos histo ricos, ya que la primera implementacio n de Lisp estaba escrita en
ensamblador, y "car" y "cdr" eran las instrucciones ensamblador para recuperar la primera y la
segunda parte del par de la memoria de la ma quina).

Por ejemplo, en la igura 1 podemos ver un par cuyo primer elemento es el a tomo "foo" y el segundo
el a tomo c.

La forma escrita de un par o "cons cell" es la siguiente:

(car . cdr)

Donde car es el a tomo referenciado por la primera parte del par, y cdr el a tomo referenciado por la
segunda parte del par. En nuestro ejemplo (un par con "foo" y c):

("foo" . c)

Tambie n podramos especi icar pares relacionando dos valores nume ricos:

(2 . 3.0)

O una cadena de caracteres y un valor nume rico:

("Edad" . 25)

Y as podemos estructurar datos en Lisp, a base de pares. El lector puede pensar: "hum, so lo puedo
estructurar datos en pares?, pues vaya castan a". Pero Lisp esta muy bien disen ado, y realmente los

3 de 9 07-08-17 17:35
Lisp, un Lenguaje que se Aprende en 10 Minutos http://www.davidam.com/docu/un-lenguaje-en-diez-minutos.html

pares pueden servirnos para construir cualquier estructura de datos en la memoria. Co mo es eso
posible? Si pensamos un poco en ello, nos daremos cuenta de que los pares tambie n son a tomos. Y si
un par relaciona dos a tomos, eso implica que un par puede contener otros pares. Luego puedo,
perfectamente, especi icar algo como:

("dos y tres" . (2 . 3))

Donde tenemos un par cuyo primer elemento es la cadena "dos y tres" y su segundo elemento es
otro par, cuyo primer elemento es 2, y el segundo 3. Esta estructuracio n no tiene lmites. Por
ejemplo, podramos denotar un a rbol binario compuesto de nu meros de la siguiente forma:

(setq arbol '(1 . ((2 . 3) . (4 . 5))))

Donde 1 es la raz del a rbol, y 2, 3, 4 y 5 las hojas divididas en dos suba rboles.

Trate de dibujar co mo quedara en un dibujo de cajas:

Dado este a rbol un ejercicio divertido es pensar en te rminos de la variable a rbol, de car y cdr co mo
acceder a algunos de los smbolos lisp. Por ejemplo el nu mero 4 se obtiene con:

(setq cuatro (caddr arbol))

que es es equivalente a:

(setq cuatro-bis (first (rest (rest arbol))))

Y el nu mero 3 se obtiene con:

(setq tres (cdadr arbol))

que es equivalente a:

(setq tres-bis (rest (first (rest arbol))))

Sin embargo, el manejo de pares puede resultar tedioso, complicado y por tanto tendente a cometer
errores. Todo ello lleva a la idea de que, pese a ser una primitiva de estructuracio n muy conveniente
a efectos de implementacio n, no resulta muy pra ctica desde el punto de vista del programador.

Para solventar este problema, Lisp de ine un tipo estructurado que se implementa a base de pares: la
lista, que da nombre al lenguaje.

Una lista esta de inida como una anidacio n de pares, donde los "car" identi ican los elementos de la
lista, y los "cdr" referencian al par que contiene el siguiente elemento. Para terminar la lista, el
u ltimo "cdr" referencia a un a tomo especial: nil. La igura 2 muestra una lista de tres a tomos: a, b y c.

La forma escrita de la lista sera la siguiente:

(setq l '(a . (b . (c . nil))))

4 de 9 07-08-17 17:35
Lisp, un Lenguaje que se Aprende en 10 Minutos http://www.davidam.com/docu/un-lenguaje-en-diez-minutos.html

Figura 2: Arbol en cajas car, cons

Afortunadamente, Lisp nos proporciona una notacio n abreviada, que es la siguiente:

(a b c)

Es importante comprender que cualquier lista Lisp esta compuesta de pares, y que para conformar
una lista los pares deben seguir las normas especi icadas antes: los "car" referencian los elementos,
los "cdr" el siguiente par, y el u ltimo "cdr" debe referenciar al a tomo nil. Cualquier otra estructura de
pares no es una lista, y no puede por tanto utilizarse la notacio n abreviada.

Visto esto, hay que decir que la forma ma s habitual de estructurar datos (y programas, como
veremos) en Lisp es mediante listas. La "notacio n de conveniencia" para las estructuras de pares que
denotan listas no es una mera curiosidad, sino que constituye la base del lenguaje.

Pra cticamente el 99% de los objetos Lisp que manejara el programador (incluidas las funciones) son
listas. Sin embargo, nada le impide utilizar pares directamente cuando lo crea conveniente. La
situacio n ma s habitual, co mo no, es cuando se quieren relacionar dos cosas distintas. Por ejemplo,
podra montarse una lista con informacio n sobre una persona (atributo - valor) de esta forma:

((Nombre . "Jose")
(Apellido . "Marchesi")
(Numero-de-pies . 2)
(Numero-de-ojos . 1.3))

Que es una lista de pares.

Y, lo crea o no el lector, ya hemos terminado con la memoria de datos de Lisp. No hay ma s. La


memoria de datos contiene a tomos (nu meros, cadenas de caracteres, smbolos y pares), ocupando
cada uno de ellos una posicio n. Con la ayuda de los pares, podemos construir estructuras arbitrarias
de a tomos.

Pasemos ahora a ver la otra parte de la memoria: el diccionario de smbolos (puesta a la izquierda en
la igura 1, que representa la memoria del inte rprete). Hemos visto que existen unos a tomos de tipo
"smbolo". Comenta bamos que su u nica sema ntica asociada es que son distintos unos de otros. Pues
bien, Lisp permite asociar un smbolo con una posicio n espec ica de la memoria de datos (en
concreto, con el a tomo que ocupa dicha posicio n). Esta es la forma que tiene Lisp de implementar el
concepto de "variable", aunque existen diferencias sutiles entre ambos conceptos.

Como veremos en el apartado de ejecucio n, el inte rprete de Lisp puede, en un momento dado,
obtener el valor de variable de un smbolo, o su valor de funcio n. Esto implica que un solo smbolo
puede identi icar un "dato" (el nombre de una variable, hablando en te rminos de lenguajes de
programacio n convencionales) y al mismo tiempo una "funcio n" (el nombre de una funcio n o
procedimiento). El valor utilizado por el inte rprete (el de variable o el de funcio n) dependera de
do nde este situado el smbolo dentro del programa. Lo veremos con detalle.

As pues, el inte rprete necesita poder asociar un smbolo con dos valores distintos: de variable y de
funcio n. Estos valores, como no poda ser de otra forma, consisten en a tomos en la memoria de
datos.

5 de 9 07-08-17 17:35
Lisp, un Lenguaje que se Aprende en 10 Minutos http://www.davidam.com/docu/un-lenguaje-en-diez-minutos.html

Para implementar esto, cada entrada del diccionario de smbolos consiste en la identi icacio n del
smbolo en s, un puntero que hace referencia a su valor de variable en la memoria de datos (D) y
otro puntero que hace referencia a su valor de funcio n (F).

En la igura de la memoria del inte rprete ( igura 1) tenemos algunos ejemplos. El smbolo uno tiene
un valor de variable de 1, y su valor de funcio n no esta de inido. El smbolo un-cons tiene un valor de
variable de:

("foo" . c)

En lista1 tenemos una lista:

(a "foo" c (1 2))

Cuyo u ltimo elemento es otra lista (atencio n a los nil).

Que hay acerca de lista2? El puntero de valor de variable hace referencia al segundo cons de lista1.
Esto implica que su valor como lista es:

("foo" c (1 2))

Esto ejempli ica el hecho de que los punteros del diccionario de smbolos son, ejem, punteros. En el
caso de lista1 y lista2, los elementos "foo", c y (1 2) son compartidos.

Por u ltimo, tenemos el smbolo funcion1. El puntero de valor de variable de funcion1 no esta
de inido (aunque pudiera estarlo) y sin embargo s que tiene de inido un valor como funcio n. El
valor como funcio n consiste en una estructura de datos (en este caso, una lista) en la memoria de
datos. Es decir, que en Lisp los datos tienen el mismo formato que el "co digo ejecutable", y adema s se
almacenan en el mismo sitio. Uno podra modi icar el "co digo ejecutable" de funcion1 simplemente
tocando la lista lista1, por ejemplo. Posibilidades, in initas

3 Ejecucin: qu hace?
Y vamos ya con la segunda parte de este mini-curso. Ya sabemos lo que el inte rprete de Lisp
almacena en memoria, y co mo lo almacena. En concreto, puede almacenar nu meros, cadenas de
caracteres, estructuras de pares (incluyendo las listas), etc. Sin embargo, nos falta por ver co mo
funciona el inte rprete, es decir, cua l es el mecanismo por el que se le ofrece un texto con un
programa Lisp y e ste lo ejecuta.

Comencemos por una idea importante: conceptualmente, el inte rprete ejecuta estructuras de datos
presentes en la memoria de datos. Es decir, que cualquier programa Lisp es expresable en tipos de
datos Lisp. Antes de ello, por supuesto, el inte rprete debe convertir el programa escrito (secuencia
de caracteres) en esas estructuras de datos. As pues, en la exposicio n que sigue hay que tener
presente que el Lisp que se expone en notacio n escrita esta almacenado en algu n lugar de la
memoria de datos.

Luego el inte rprete es capaz de "ejecutar" o evaluar el contenido de la memoria de datos Muy bien,
pero, cualquier contenido? La respuesta es s. El inte rprete puede evaluar cualquier a tomo o
estructura, dependiendo u nicamente el resultado de la evaluacio n.

Lo primero, lo ma s sencillo: a tomos nume ricos. Un a tomo nume rico evalu a simplemente a su valor.
Es decir, que el a tomo 1 evalu a a 1, y el a tomo 23.3 evalu a a 23.3. Sin ma s. Lo mismo ocurre con las
cadenas de caracteres: "abc" evalu a a "abc".

Entramos en harina cuando queremos evaluar un par o una lista (compuesta de pares). El proceso de

6 de 9 07-08-17 17:35
Lisp, un Lenguaje que se Aprende en 10 Minutos http://www.davidam.com/docu/un-lenguaje-en-diez-minutos.html

evaluacio n es el siguiente:

1. Se obtiene el "car" (el primer elemento) de la lista y se evalu a hasta obtener un valor de
funcio n.
2. Uno por uno (el orden es incierto) se evalu a el resto de loselementos de la lista hasta obtener
a tomos.
3. Se aplica la funcio n obtenida en el primer paso tomando como argumentos los a tomos
obtenidos en el segundo paso, en el mismo orden.

Pongamos inmediatamente un ejemplo. Supongamos que el inte rprete quiere evaluar:

(+ 2 (* 2 2))

En primer lugar, evalu a el "car" (o primer elemento) de la lista. Encuentra +, que es un a tomo de tipo
smbolo. Como esta buscando un valor de funcio n, busca el smbolo + en el diccionario de smbolos y
extrae su valor de funcio n de la memoria de datos. En este caso, su valor de funcio n es la funcio n
suma. A continuacio n, evalu a el segundo elemento de la lista, que ya es un a tomo (y vale 2). Luego,
evalu a el tercer elemento de la lista, que es otra lista. Y por tanto vuelve a empezar el proceso,
evaluando el smbolo * a su valor de funcio n (la funcio n multiplicacio n) y los dos doses evalu an a s
mismos. As que aplica la funcio n multiplicacio n a los dos doses. El resultado de evaluar (* 2 2) es
por tanto 4. Hemos vuelto al a mbito de la funcio n suma. Ya hemos evaluado ambos argumentos (2 y
4), as que aplicamos la funcio n suma y obtenemos el valor inal: 6.

Nos queda por ver a que evalu a un tipo de a tomo: el smbolo. La respuesta es: depende de do nde
este . Como hemos visto, si el smbolo es un "car" (primer elemento) de una lista o de un par,
entonces evalu a a su valor de funcio n, como en:

(fibonacci 23)

Sin embargo, si aparece en cualquier otro sitio, un smbolo siempre evalu a a su valor de variable,
como en:

(fibonacci num)

Donde el smbolo num evalu a a un nu mero concreto que se pasa como para metro a la funcio n
referenciada por ibonacci.

Recue rdese que un smbolo puede tener de inido tanto un valor como variable como un valor como
funcio n. As que lo siguiente es Lisp va lido, siempre y cuando el smbolo foo tenga de inidos valores
como variable y como funcio n:

(foo foo)

En el caso de que se evalu e un smbolo a su valor de variable y e ste no este de inido el inte r- prete,
faltara ma s, emite un error. Lo mismo aplica a la evaluacio n de smbolos a su valor de funcio n.
Intenta evaluar (foobarbaz) y lo vera s.

Pero aqu hay algo que no encaja Por un lado, hemos dejado claro que las listas y los smbolos son
datos por s mismos (de hecho, hay que usar las listas para estructurar los datos) y, por tanto, uno
podra esperar poder invocar a una funcio n que tome una lista como para metro, como, por ejemplo:

(numero-de-elementos (a b c))

7 de 9 07-08-17 17:35
Lisp, un Lenguaje que se Aprende en 10 Minutos http://www.davidam.com/docu/un-lenguaje-en-diez-minutos.html

Que invocara la funcio n numero-de-elementos pasa ndole la lista como argumento. Pero, segu n el
mecanismo de evaluacio n que hemos visto, el inte rprete tratara (a b c) como la invocacio n de la
funcio n "a" (el valor de funcio n del smbolo "a" pasa ndole como resultado las evaluaciones de "b" y
de "c" (sus valores como variables). Y eso no es lo que queremos. Para posibilitar esto, Lisp
proporciona una funcio n muy especial llamada quote, que inhibe la evaluacio n de su argumento.
Podramos poner, por tanto:

(numero-de-elementos (quote (a b c)))

Y obtendramos el resultado esperado, ya que quote inhibe la evaluacio n de (a b c) y, por tanto,


evalu a a la lista compuesta de tres a tomos smbolos. Por conveniencia, Lisp proporciona una
abreviatura para la funcio n quote, y es el cara cter del mismo nombre: ' . As que, simplemente,
escribiremos:

(numero-de-elementos (a b c))

De forma similar, podemos "quotear" smbolos:

(funcion-que-recibe-un-simbolo 'hola)

Para terminar este mini-curso conceptual sobre Lisp, vamos a introducir la primitiva set, que sirve
para asignar valores de variables a smbolos (es decir, introduce o cambia una entrada en el
diccionario de smbolos).

Si queremos, en nomenclatura convencional, "asignar el valor 23 a la variable edad", en Lisp


escribimos esto:

(set 'edad 23)

Como vemos, set es una funcio n que recibe dos para metros: un smbolo a insertar en el diccionario, y
un a tomo al que apuntara su puntero de valor de variable (en este caso 23). Este es un ejemplo de
funcio n que toma como argumento un smbolo, y por tanto debemos "quotearlo". Como es una
funcio n tan habitual, Lisp propor- ciona una "forma especial" (una construccio n Lisp que no se
evalu a de forma convencional) llamada setq (de "set quick"), en la que podemos omitir el quote del
smbolo y, por tanto, simplemente escribir:

(setq edad 23)

set y setq existen, con exactamente la misma sema ntica, en todos los dialectos de Lisp. Sin embargo,
las primitivas para asignar valores como funcio n varan de un dialecto a otro (aunque son similares).
Por ejemplo, Emacs Lisp proporciona para ello la forma especial defun, que funciona as:

(defun suma-dos (num)


(+ 2 2))

En cambio, en Scheme, haramos algo como esto:

(define (suma-dos num)

8 de 9 07-08-17 17:35
Lisp, un Lenguaje que se Aprende en 10 Minutos http://www.davidam.com/docu/un-lenguaje-en-diez-minutos.html

(+ 2 2))

4 Y ahora qu?
Con esto damos por terminado nuestro mini-curso conceptual sobre Lisp. Evidentemente, queda
mucho por aprender. Pero lo que podemos garantizar es que los conceptos fundamentales (sobre los
que se construye el resto del lenguaje) el lector ya los tiene aprendidos. A partir de aqu, la tarea
consiste en aprender primitivas y las caractersticas concretas de cada dialecto e inte rprete.

El valor de este mini-curso conceptual estriba en que cualquier inte rprete de Lisp (y cualquier
dialecto del lenguaje) se ajustara al modelo que acabamos de aprender. A partir de ahora, cuando el
lector vea un programa Lisp (sea lo complejo que sea) vera a tomos, pares, quotes, la memoria de
datos y el diccionario de smbolos. Y estara viendo las cosas tal y como son en realidad. Esa es la
mejor garanta para adquirir un dominio absoluto y productivo sobre el lenguaje. Buena suerte.

5 Licencia
Este documento esta bajo una Licencia Creative Commons Reconocimiento Unported 3.0, se publico
originalmente en Mundo Linux 85.

9 de 9 07-08-17 17:35