Vous êtes sur la page 1sur 11

Introducción a Common Lisp

Algunas Notas Sobre Computación Simbólica

Esta sección trata acerca del lenguaje de programación Lisp, (que toma su nombre de
procesamiento de listas, en inglés List Processing), presentando algunos conceptos básicos acerca del
manejo de símbolos y programación básica en Lisp.

A continuación se describe todo lo relacionado con el manejo de símbolos, resaltando su


importancia y explicando por qué es apropiado aprender Lisp como lenguaje para manejo de símbolos.

1) El manejo de símbolos es comparable a trabajar con palabras y oraciones

Dentro una computadora, todo es una cadena de dígitos binarios, ceros y unos, llamados bits.
Desde este punto de vista, las sucesiones de bits se pueden interpretar como una representación para
los dígitos decimales. Sin embargo, desde otra perspectiva, éstas se pueden considerar como un código
para objetos semejantes a palabras y oraciones.

• En Lisp los elementos fundamentales formados a partir de bits son objetos semejantes a palabras y
se denominan átomos.

Los siguientes son átomos válidos para Lisp:

arco
dintel
poste1
poste2

• Los grupos de átomos forman objetos parecidos a las oraciones y se denominan listas. Las listas
pueden agruparse para formar a su vez listas de nivel superior (listas de listas o listas anidadas).

Los siguientes son ejemplos de listas:

'(a b c d e f g) ; Una lista simple de siete elementos

'(a (b c) (d e) (f g)) ; Una lista de cuatro elementos,


; de los cuales el primero es un átomo y
; los otros tres son listas
'(partes dintel
columna1 columna2) ; Una lista simple de cuatro elementos

• En conjunto, los átomos y las listas se denominan expresiones simbólicas, o simplemente


expresiones. Una expresión puede representar ya sea una operación, un listado de operaciones,
un programa, un pedazo de conocimiento, etc. El manejo de símbolos mediante Lisp implica
trabajar con expresiones simbólicas.

Las siguientes son expresiones simbólicas que pueden tener sentido para un ingeniero civil:

'(trabe1
(debe-estar-sostenido-por columna1)
(debe-estar-sostenido-por columna2))

La expresión anterior trata de representar que se necesitan a la "columna1" y "columna2"


para sostener a "trabe1". Una persona puede escribir programas que puedan llegar a
conclusiones similares, a partir de expresiones como la anterior.
Las expresiones simbólicas también sirven para representar operaciones algebraicas. Los
siguientes son :

(+ 1 2 3 4 ) ; Una expresión que se puede


; evaluar directamente y da
; como resultado 10

(sin pi) ; Una expresión que se puede


; evaluar directamente y representa
; el seno de pi (π) radianes. Esto da
; como resultado 1.2246063538223773E-16
; (casi cero)

(sin (/ pi 8)) ; Una expresión que representa el seno de pi


; radianes entre 8. Esto da como resultado
; 0.3826834323650898

Las expresiones simbólicas también sirven para representar listas de operaciones, tanto
como si fuesen datos, así como expresiones que se pueden evaluar. Por ejemplo la siguiente expresión:

'((+ 1 2 3 4 ) (sin pi) (sin (/ pi 8)))

Es una lista compuesta por tres elementos, cada uno de ellos son a su vez operaciones
algebraicas. Pero si se le suministra al compilador tal y como se muestra, Lisp la entenderá como un
conjunto de datos y no devolverá cálculo alguno. La siguiente expresión sí lo hará:

(eval (first '((+ 1 2 3 4 ) (sin pi) (sin (/ pi 8)))))

La anterior es una lista anidada cuyo primer elemento es la función eval, la cual toma como
argumento al siguiente elemento de la lista, que a su vez es otra lista, cuyo primer elemento es la función
first, y a su vez toma como argumento a la lista de operaciones algebraicas definidas con anterioridad.
El resultado de esta expresión es la evaluación del primer elemento de dicha lista, y da como resultado
10.

Un programa que maneja símbolos se vale de expresiones simbólicas para trabajar con datos y
procedimientos, igual que las personas emplean lápiz, papel y algún lenguaje para hacer lo mismo. Un
programa que maneja símbolos, suele tener procedimientos para reconocer expresiones simbólicas
particulares, separar elementos de ellas y formar nuevas expresiones. Por ejemplo, la siguiente expresión
construye una lista que representa una operación algebráica:

(list '+ '1 '2 '3 '4 )

La anterior genera como resultado (+ 1 2 3 4), que como se ha visto anteriormente, es una
expresión que puede evaluarse en un momento dado.

A continuación se muestran dos ejemplos de expresiones simbólicas más complejas que tratan de
representar conocimiento. Los paréntesis indican el principio y el final de las listas. El primer ejemplo, es
una descripción de una estructura formada por bloques de juguete, el segundo, la descripción de una
universidad.
'(arco (partes dintel poste1 poste2)
(dintel debe-estar-sostenido-por poste1)
(dintel debe-estar-sostenído-por poste2)
(dintel es-un-tipo-de cuña)
(poste1 es-un-tipo-de ladrillo)
(poste2 es-un-tipo-de ladrillo)
(poste1 no-debe-tocar poste2)
(poste2 no-debe-tocar postel))

'(mit (un-tipo-de universidad)


(localidad (cambridge massachusetts))
(teléfono 253-1000)
(facultades
(arquitectura
administración
ingeniería
humanidades
ciencias))
(fundador (william barton rogers)))

Ciertamente, estos ejemplos no pueden evaluarse directamente, pero no deben amedrentar al


lector; ambos describen algo de acuerdo con ciertas convenciones en cuanto a la manera de organizar
los símbolos. He aquí otro ejemplo, pero esta vez expresando una regla para determinar si algún animal
es carnívoro.

(identifica6
((? animal) tiene dientes puntiagudos)
((? animal) tiene garras)
((? animal) tiene ojos al-frente)
((?animal) es carnívoro))

Acabamos de expresar de otra forma la idea de que un animal con dientes puntiagudos, garras y
ojos al frente probablemente es un carnívoro. Para emplear tal regla, un programa debe aislarla,
determinar si los patrones que involucran dientes, garras y ojos son compatibles con la información que
de algún animal específico se tenga en la base de datos y, en caso afirmativo, agregar a la base de datos
la conclusión de que el animal es carnívoro. Todo esto se realiza mediante el manejo de símbolos.

2) Lisp es uno de los lenguajes de programación utilizados en el desarrollo de sistemas que


se denominan "inteligentes"

En la actualidad, hay un creciente desarrollo de programas que presentan lo que la mayoría


considera comportamiento inteligente. Casi todos estos programas inteligentes, o que aparentan serlo,
están escritos en Lisp, y muchos tienen gran importancia práctica. He aquí algunos ejemplos.

• Expertos en resolver problemas. Uno de los primeros programas en Lisp resolvía problemas de
cálculo como los que resuelven los estudiantes universitarios de primer año. Otro de estos
programas resolvía problemas de analogías geométricas como los que se incluyen en pruebas de
inteligencia. Desde entonces se han creado nuevos programas para configurar computadoras,
diagnosticar infecciones de la sangre, entender circuitos electrónicos, evaluar formaciones
geológicas, planear diversiones, planificar fábricas, disertar cerraduras, inventar matemáticas
interesantes y mucho más. Todos ellos escritos en Lisp.

• Razonamiento con sentido común. Gran parte del pensamiento humano parece implicar una
pequeña cantidad de razonamiento y una gran cantidad de conocimiento. La representación del
conocimiento implica elegir un vocabulario de símbolos y establecer algunos acuerdos en cuanto a
cómo disponerlos. Las buenas representaciones hacen que las cosas correctas resulten explícitas.
Lisp es un lenguaje en el cual se realiza la mayoría de las investigaciones acerca de la
representación.

• Aprendizaje. Muchos han sido los trabajos que se han realizado acerca del aprendizaje de
conceptos por computadora y ciertamente la mayor parte de lo realizado también se apoya en los
avances logrados en las técnicas de representación del conocimiento. Una vez más, Lisp domina
en esta área.

• Interfaces en lenguaje natural. Existe una creciente demanda de programas que interactúen con
las personas en inglés u otros lenguajes (a los cuales les denominaremos "naturales"). Se han
construido sistemas prácticos para recuperar información de bases de datos, que de otra forma son
difíciles de usar.

• Educación, sistemas de apoyo (o ayudas) inteligentes y capacitación. Para interactuan de manera


cómoda con las computadoras, es preciso que éstos sepan lo que la gente sabe y cómo decirle
más. Nadie desearía tener una vasta explicación acerca de lo que conoce bien, como tampoco a
nadie le agradaría recibir una explicación en términos telegráficos cuando se es principiante. Los
programas basados en Lisp están empezando a hacer modelos de usuario analizando lo que éste
hace. Estos programas usan los modelos para ajustar o elaborar explicaciones.

• Lenguaje y visión. Se ha probado que es sumamente difícil entender la manera como escuchamos
y vemos. Parece que aún no logramos conocer lo suficiente acerca de cómo el mundo físico limita
lo que percibimos por medio del tambor de nuestro oído o nuestra retina. Sin embargo, se ha
progresado en este sentido y gran parte de este avance se ha realizado a través de Lisp, aunque
se requiere una buena parte (le programación orientada a la aritmética. A decir verdad, Lisp no
ofrece ventajas especiales para hacer programación orientada a la aritmética, pero tampoco
plantea desventajas en ese aspecto.

En consecuencia, la gente que desea saber de la inteligencia utilizando las computadoras necesita
de una manera u otra entender lenguajes para procesamientos simbólico, tales como los basados en
Lisp.

3) LISP está orientado a la promoción de la productividad y facilita la educación

Lisp es un lenguaje importante incluso para cuestiones no relacionadas con representación de la


inteligencia. He aquí algunos ejemplos:

• Programación de aplicaciones. Los programadores talentosos se han dado cuenta que Lisp puede
incrementar enormemente su productividad al permitirles escribir programas extensos mucho más
rápido y a un costo mucho menor. Esto puede tener efectos notables en la forma en que los
programas extensos se desarrollan. Anteriormente se empezaba por destinar varios años para la
especificación, luego se asignaban varios años más para la programación, y esto acababa en
sistemas que producían usuarios decepcionados y malhumorados. Actualmente se construyen
prototipos en pocos meses, con la evolución simultánea de la especificación y la programación, así
como de los desarrollos logrados en ambientes de programación gráficos, y con la constante
participación de los usuarios en la producción del resultado final.

• Programación de sistemas. Las máquinas LISP fueron estaciones de trabajo de gran potencia,
programadas enteramente en Lisp. El sistema operativo, los programas para uso general, los
editores, los compiladores y los intérpretes están todos escritos en Lisp, lo que demuestra el poder
y la versatilidad de este lenguaje. Con la evolución de la tecnología de computadoras que
actualmente se ha logrado, es posible utilizar todo el poder de aquellas estaciones de trabajo, en
computadoras de escritorio.
• Educación en ciencias de la computación. Lisp facilita la abstracción de procedimientos y de datos,
dando énfasis con ello a (los ideas de suma importancia en la programación. Y en vista de que
LISP es un lenguaje orientado a la implantación, resulta adecuado para construir intérpretes y
compiladores para una amplia gama de lenguajes, incluyendo el propio Lisp.

4) LISP es el lenguaje para manejo de símbolos más usado en América

Existen numerosos lenguajes de programación, de los cuales, sólo unos cuantos se destinan al
manejo de símbolos. De éstos, Lisp es el más usado en el continente americano. Una vez que Lisp se
entiende, la mayoría de los otros lenguajes para manejo de símbolos son relativamente fáciles de
aprender.

¿Por qué Lisp ha llegado a ser el lenguaje más utilizado para el manejo de símbolos, y últimamente para
muchas cosas más? A continuación se dan una serie de argumentos, cada uno con sus propios
partidarios:

• Interacción. Lisp está diseñado para programar en una terminal con respuesta rápida. Todos los
procedimientos y los datos pueden presentarse o modificarse a voluntad del usuario.

• Ambiente. Lisp fue utilizado por una comunidad que requirió y obtuvo lo mejor en herramientas
para edición y depuración. Durante más de dos décadas, en los centros de inteligencia artificial
más importantes del mundo, se han creado complejos ambientes de programación en torno a Lisp,
creando una combinación inmejorable para escribir programas extensos y complicados.

• Evolución. COMMON Lisp es el resultado de más de treinta años de continuos experimentos y


refinamientos. En consecuencia, las versiones comerciales de Lisp tienen las características
apropiadas para estar al nivel de paquetes de software tales como Visual FORTRAN, Visual Basic
o Visual C++.

• Uniformidad. Los procedimientos y datos en Lisp tienen la misma forma. Un programa en Lisp
puede usar otro programa como dato, incluso puede crear otro programa y usarlo.

Lisp es un lenguaje fácil de aprender. No es necesario conocer otros lenguajes de programación.


En realidad, el contar con cierta experiencia puede ser un obstáculo ya que puede existir el riesgo de
desarrollar hábitos inadecuados. Esto obedece a que otros lenguajes hacen las cosas de manera
diferente y la traducción procedimiento por procedimiento conduce a construcciones confusas y a
prácticas deficientes de programación.

Una razón por la que Lisp es fácil de aprender, es que su sintaxis es sencilla. Como dato curioso,
la sintaxis actual de Lisp tiene raíces extrañas. El inventor de Lisp, John McCarthy, en un principio usó
una especie de Lisp antiguo, que es casi tan difícil de leer como el inglés antiguo. Sin embargo, en
determinado momento él quiso utilizar Lisp para hacer un trabajo de matemáticas donde se requería que
tanto los procedimientos como los datos tuvieran la misma forma sintáctica. La forma resultante de Lisp
se dedujo rápidamente.

5) COMMON LISP es el Lisp que se debe aprender

El Lisp que se usa en este libro es COMMON LISP, dado que es moderno, estándar, y está
ampliamente disponible. COMMON Lisp también es el estándar aceptado para uso comercial. El lector
no deberá dejarse confundir por referencias a otros lenguajes extrañamente llamados Lisp. La mayoría
de ellos son dialectos obsoletos o COMMON Lisp con extensiones de alguna compañía en especial.

Recientemente, COMMON Lisp se ha extendido mediante la incorporación de características para


programación orientada a objetos por medio de CLOS, el sistema de objetos de COMMON Lisp.

6) Algunos de los principales mitos en contra de Lisp


No hay un lenguaje de programación perfecto y Lisp no es la excepción. Muchos de sus defectos
originales se han corregido, aunque algunas personas continúan refiriéndolos erróneamente en la
actualidad. Estos son algunos de los mitos más arraigados:

Lisp es lento al operar con números.

En efecto, esto fue cierto durante algún tiempo. Este problema se ha corregido mediante el
desarrollo de programas adecuados que permiten traducir el material producido por los programadores en
instrucciones, que pueden ser ejecutadas por una computadora de manera directa y sin más
descomposiciones. Dicho de otra forma, el problema se ha corregido mediante el diseño de eficaces
compiladores de Lisp.

Lisp es lento.

También esto fue cierto durante algún tiempo. Al principio, Lisp se usaba exclusivamente en
investigaciones donde la interacción era el aspecto fundamental, en tanto que a la alta velocidad para
depurar los programas orientados a aplicaciones se le confería una menor importancia. En la actualidad,
se han desarrollado excelentes compiladores de Lisp para apoyar la creciente demanda comercial para
programas en Lisp.

Los programas en Lisp son grandes.

En realidad este no es un mito. Algunos programas son grandes, pero esto se debe a que Lisp
permite crear programas lo suficientemente grandes como para realizar una gran cantidad de tareas.

Los programas en Lisp requieren computadoras caras.

Hace unos cuantos años, para empezar con Lisp se necesitaba un millón de dólares. Hoy día, se
pueden tener excelentes sistemas Lisp para computadoras personales que cuestan sólo unos cuantos
cientos de dólares (que ya por si misma es una gran diferencia), y aun los sistemas complejos que
operan en estaciones de trabajo orientadas a LISP tienen un costo inferior a los cien mil dólares.

Lisp es difícil de leer y depurar por todos los paréntesis que requiere.

En realidad, el problema de los paréntesis desaparece en cuanto se aprende a usar un editor para
programas en Lisp que permite poner las cosas en la pantalla de edición, de manera apropiada. Nadie
encontraría especialmente claro el siguiente ejemplo:

(defun fibonacci (n) (if (or (= n


0) (= n l)) 1 (+ (fibonacci
n l)) (fibonacci (- n 2)))))

Con un poco de experiencia, se obtiene la versión equivalente, ya con formato:

(defun fibonacci (n)


(if (or (= n 0) (= n l))
1
(+ (fibonacci (- n l))
(fibonacci (- n 2)))))

Los editores orientados a Lisp facilitan la producción de versiones con formato, a medida que se
escriben.

Lisp es difícil de aprender.


Lisp adquirió esta mala reputación porque en sus primeros años se hizo acompañar de algunos
libros difíciles de leer. Ahora existen muchas obras buenas, cada una con sus características distintivas.

Aclaraciones

Antes de seguir adelante con esta introducción, hay que hacer algunas aclaraciones.

1) Operaciones

Las operaciones se pueden expresar de dos maneras (o notaciones): Infija y Prefija.

Notación Infija "x operador y"

Ventajas: Las expresiones cotidianas (al menos como comúnmente las aprendemos en las
escuelas) son de esta manera. Ej. 2 + 3, 4 + 4, 2 * 2 , 2 / 3, etc.

Desventajas: No son muy adecuadas para la computadora, pues la máquina espera primero que se le
proporcionen las acciones y luego los datos. Normalmente en los lenguajes como Fortran
o Basic, esto se soluciona mediante un proceso de pre-compilación o "parsing."

Notación Prefija " operador x y"

Ventajas: Las expresiones computacionales están más de acuerdo con la manera en que la
computadora las maneja. Se tiene la posibilidad de aplicar un operador a varios
elementos. Ej. en la expresión "+ x y z a b c" el operador "+" se aplica a todos los
elementos ella y solamente se declara una vez. En notación infija, la expresión anterior se
tendría que escribir de la siguiente manera "x + y + z + a + b + c"

Desventajas: No es la manera común en que las personas manejan las operaciones ni matemáticas ni
simbólicas.

Lisp estuvo pensado desde un principio para ser programado con notación prefija. Esto no debe de
ahuyentar al lector. Con un poco de práctica, es común que el estudiante del lenguaje se acostumbre a
ella. Incluso después de ello, muchas personas prefieren este tipo de notación, por sobre otras
encontradas en lenguajes tales como Pascal, Basic, o FORTRAN.

2) El uso de paréntesis

Una de las características más obvias de Lisp son los paréntesis. Aunque de primera instancia los
programas escritos en este lenguaje se asemejan a un conjunto de paréntesis sin mucho código de por
medio, estos paréntesis son necesarios para delimitar el "ámbito de aplicabilidad" de una operación. Por
ejemplo, en la expresión (+ n 1), el operador "+" sólo se aplica a "n" y a "1". Así mismo, en la
expresión (or (= n 0) (= n l)), el operador "or" sólo aplica al resultado de "(= n 0)" y "(= n
l)".

Casi todos los lenguajes de programación tienen alguna u otra forma delimitación del ámbito de
operaciones. En C/C++ lo son los corchetes "{...}", los paréntesis rectangulares "[...]", o el punto y
coma ";". En Basic, Visual Basic y demás versiones de dicho lenguaje, lo son el retorno de carro (o línea
nueva), los "If-Then-ElseIf-Else-Endif", los "Do-EndDo", etc. Lisp utiliza únicamente los
paréntesis para delimitar. Esto puede verse como una ventaja, si se considera la economía en notación.

3) Programación funcional

Lisp es un lenguaje "funcional". Esto quiere decir que todas las operaciones a realizar se
consideran "funciones" que devuelven un resultado siempre. Por ejemplo, (+ 1 2 3)se considera como
una función que devuelve "6", así como (first '(a b c d e)) se considera como una función que
devuelve "a". Para el compilador, el primer elemento de una lista siempre se considera una función,
cuyos argumentos son el (los) siguiente(s) elemento(s). Si la función está definida ya sea por el usuario o
porque sea parte del lenguaje, Lisp la tratará de evaluar siempre. Si uno desea suministrar una lista o un
átomo como si fuese solamente dato, la debe de suministrar antecedida por un apóstrofe sencillo ( ' ).

Ejemplo:

En la elaboración de un programa en un lenguaje funcional tal como Lisp, siempre se parte de


expresiones sencillas, conocidas como "primitivas", pues están compuestas de operaciones propias del
lenguaje. Por ejemplo, las siguientes expresiones tienen como objetivo el asignar la conectividad de cada
uno de los nodos de la siguiente gráfica, con sus nodos vecinos:

a d

c b e

(setf (get 's 'vecinos) '(a d)


(get 'a 'vecinos) '(s b d)
(get 'b 'vecinos) '(a c e)
(get 'c 'vecinos) '(b)
(get 'd 'vecinos) '(s a e)
(get 'e 'vecinos) '(b d f)
(get 'f 'vecinos) '(e))

La siguiente expresión asigna la posición (en coordenadas x, y) de estos mismos nodos en un


plano cartesiano:

(setf (get 's 'coordenadas) '(0 3)


(get 'a 'coordenadas) '(4 6)
(get 'b 'coordenadas) '(7 6)
(get 'c 'coordenadas) '(11 6)
(get 'd 'coordenadas) '(3 0)
(get 'e 'coordenadas) '(6 0)
(get 'f 'coordenadas) '(11 3))

Si se suministran las expresiones anteriores a la computadora, Lisp asignará lugares de memoria


específicos para cada uno de los nodos que se han definido. Para encontrar cuales son los nodos
vecinos de " 'a " por ejemplo, sólo basta con suministrar una instrucción como la siguiente:

(get 'a 'vecinos) y esto da como resultado (S B D)

Lo mismo se puede hacer para los demás nodos:


(get 's 'vecinos) resultado (A D)
(get 'b 'vecinos) resultado (A C E)
(get 'c 'vecinos) resultado (B)
(get 'd 'vecinos) resultado (S A E)

...etc. Para obtener las coordenadas cartesianas de un nodo basta con escribir la directiva get, el
nombre del nodo y el argumento coordenadas. Ejemplo:

(get 's 'coordenadas) resultado (0 3)


(get 'a 'coordenadas) resultado (4 6)
(get 'b 'coordenadas) resultado (7 6)
(get 'c 'coordenadas) resultado (11 6)

...etc.

Para obtener la distancia entre dos nodos, es necesario definir una función que utilice las
coordenadas cartesianas de ambos nodos para calcularla. La distancia entre dos puntos está definida
(para superficies planas) como:

[( X 2 − X 1 ) + (Y2 − Y1 )
2 2
]
Para definir una función, se debe de observar de cerca los componentes que la conforman. Esto es
necesario para cualquier lenguaje de programación. Para el caso particular de la función anterior, esta
está compuesta por cuatro operaciones que se deben de realizar a los componentes de los pares
cartesianos de asignados a cada nodo. Con anterioridad se definieron las coordenadas cartesianas de
cada nodo como una lista compuesta por dos números, de los cuales se sobreentiende que el primero
corresponde a X y el segundo a Y.

En Lisp las funciones se definen con la directiva defun, el nombre que se le quiere asignar a ella y
entre paréntesis los argumentos que utiliza. La función que nos proponemos construir entonces recibirá
como argumentos a dos nodos, a los cuales llamaremos nodo-1 y nodo-2.

(defun distancia-entre-nodos (nodo-1 nodo-2) )

El paso siguiente será que la función busque los pares cartesianos que corresponden a los dos
nodos que se le suministraron. Cabe hacer la aclaración que todos los resultados de las operaciones que
se realizan en un ámbito, no permanecen en la memoria para ser utilizados en otros lugares de una
función. Para nuestro caso en particular, se utiliza la directiva get de nuevo, pero se necesita que el
resultado que se devuelva quede almacenado en un lugar de la memoria, por lo que definiremos dos
variables propias de la función para este objetivo. A estas variables "temporales" las llamaremos
"coordenadas-1 " y "coordenadas-2 ". La directiva let permite tanto definir como asignar valores a
las variables dentro de una función. La sintaxis de la directiva let es de la siguiente manera:

(let ( (variable-1 valor-1)


(variable-2 valor-2)
...
(variable-N valor-N) )

(Operaciones a realizar aquí...) )


... donde valor-N es ya sea un número, un símbolo, una lista, el resultado de una operación, etc. En
nuestro caso, let quedaría de la siguiente manera:

(let ( (coordenadas-1 (get nodo-1 'coordenadas))


(coordenadas-2 (get nodo-2 'coordenadas)) )

Tanto "coordenadas-1 " y "coordenadas-2 " recibirán sendas listas que representan cada una
de las coordenadas de cada nodo. Hecho lo anterior, nuestra función quedaría de la siguiente manera:

(defun distancia-entre-nodos (nodo-1 nodo-2)


(let ((coordenadas-1 (get nodo-1 'coordenadas))
(coordenadas-2 (get nodo-2 'coordenadas)))
)

Para obtener las coordenadas X, utilizaremos la directiva first, que devuelve al primer elemento de
una lista. Para las coordenadas Y, utilizaremos la directiva second, que obtiene al segundo elemento de
una lista.

(first coordenadas-1)
(first coordenadas-2)

(second coordenadas-1)
(second coordenadas-2)

Respectivamente obtendremos las diferencias entre las coordenadas X e Y, para cada pareja de
coordenadas:

(- (first coordenadas-1)
(first coordenadas-2) )

(- (second coordenadas-1)
(second coordenadas-2) )

Para exponenciación, Lisp posee la directiva expt, cuya sintaxis es la siguiente: (expt valor
exponente) donde valor y exponente son números reales. Para nuestro caso:

(expt (- (first coordenadas-1)


(first coordenadas-2)) 2 )

(expt (- (second coordenadas-1)


(second coordenadas-2)) 2 )

El paso siguiente será sumar el resultado de estas dos operaciones:

(+ (expt (- (first coordenadas-1)


(first coordenadas-2)) 2)
(expt (- (second coordenadas-1)
(second coordenadas-2)) 2) )

Seguidamente, obtendremos la raíz cuadrada de la expresión anterior:

(sqrt (+ (expt (- (first coordenadas-1)


(first coordenadas-2)) 2)
(expt (- (second coordenadas-1)
(second coordenadas-2)) 2)) )
La función completa quedaría como sigue:

(defun distancia-entre-nodos (nodo-1 nodo-2)


(let ((coordenadas-1 (get nodo-1 'coordenadas))
(coordenadas-2 (get nodo-2 'coordenadas)))
(sqrt (+ (expt (- (first coordenadas-1)
(first coordenadas-2)) 2)
(expt (- (second coordenadas-1)
(second coordenadas-2)) 2)))))

Una vez definidas, las funciones se pueden llamar desde cualquier punto de un programa, o
inclusive dentro de otra función, pero en lugar de proporcionar los parámetros dentro de los paréntesis,
tanto el nombre de la función como sus argumentos se proporcionan con una lista. Ejemplo:

(distancia-entre-nodos 's 'a) da como resultado 5.0


(distancia-entre-nodos 'a 'd) da como resultado 6.082762530298219
...etc.

Ejercicios

Elabora un programa que haga lo mismo que el anterior, pero para ternas (X Y Z), con la siguiente
información:

nodos: vecinos (X Y Z)

g: h j (1 2 4)
h: g i j (3 2 1)
i: h k m (9 -1 10)
k: i (5 2 4)
j: g h m (-2 1 0)
m: i j z (0 0 -7)
z: m (-1 -1 -1)

Vous aimerez peut-être aussi