Vamos a ver un tipo de lenguaje funcional que es muy útil en el campo de la Inteligencia Artificial: Lisp.
Un poco de historia
- 60. McCarthy en el MIT.
- Muy usado en IA.
- En LISP la recursión se emplea a menudo como estructura de control, lo que resta eficiencia a las ejecuciones.
- Las últimas versiones de LISP incluyen un recolector de basura.
Un ejemplo sencillo
Perspectiva del lenguaje
- Interactivo (usualmente)
- Los datos en LISP son muy restringidos:
- Átomos literales (símbolos)
- Átomos numéricos
- La estructura de datos básica es la lista. Incluye primitivas para su manipulación.
- Los comentarios comienzan por ;
- Los parámetros de función van todos por valor o por referencia según la clasificación de la función.
- LISP es interpretado y usa una estructura de gestión de almacenamiento en montículo con recolección de basura como almacenamiento primario para datos y programas.
Un ejemplo más extenso
Objetos de datos
- Tipos de datos primitivos: átomos. Cada átomo tiene una lista de propiedades asociada, accesible a través del puntero que almacena el nombre del átomo.
- Tipos de datos estructurado: listas. Tienen asociado un puntero al primer elemento (car) y otro al elemento siguiente (cdr). Una lista vacía apunta a nil.
Para la asignación se utiliza setq(x val). - Representación y almacenamiento.
- Cada descriptor de un objeto de datos proporciona tipo y atributos.
- En los datos estructurados (listas) se tienen sólo punteros a primero y a siguiente.
No se distinguen may-min para identificadores.
Control de secuencia
- El traductor LISP es una función read() que toma el fuente del fichero y lo interpreta.
- La ejecución del programa consiste en la evaluación de las funciones contenidas en el mismo.
- Expresiones:
- Condicional
- Operaciones sobre átomos (en preorden): +, -, *, /
- Operaciones sobre listas: cons, car, cdr, list, replace, null, equal.
- Operaciones sobre propiedades: put, get.
- Enunciados: prog() para ejecución secuencial.
- Entrada y salida: open(), read(), print().
- Definición de funciones: defun, define.
Gestión de subprogramas
- Tres clases de funciones:
- Función interpretada, en forma de estructura de listas. Primitivas eval y apply.
- Función compilada, compiladas en un bloque de código máquina que puede ser ejecutado por el interprete del hardware.
- Macro, se declara con define. Es simplemente una función ordinaria en LISP. Puede ser interpretada y compilada.
Gestión de almacenamiento
- La memoria se estructura en forma de montículo, que maneja unidades de una palabra de tamaño fijo usando una lista de espacios libres y un recolector de basura.
- Entorno de referencia:
- Local, es el que se da en las listas, como asociaciones de átomos relacionados de una determinada manera.
- Global o común, se consigue mediante asociación de un átomo con una propiedad del mismo que contiene un puntero al datos referenciado. Se usa set y setq.
- Paso de parámetros:
- Transmisión por valor, consiste en evaluar las expresiones de una lista de parámetros y transmitir los valores resultantes.
- Transmisión por nombre, transmitir las expresiones de la lista de parámetros sin evaluar, y dejar que la función llamada los evalue usando eval. En funciones macro la transmisión por nombre es la norma. Para funciones lambda se puede especificar la transmisión por nombre usando nlambda, en lugar de lambda.
Funciones en Lisp
- Funciones normales, son las que se suelen incluir en las implementaciones de LISP (ver manual en cada caso).
- Funciones de lista, para manipulación de listas:
- car L, devuelve el primer elemento de L.
- cdr L, devuelve la cola (lista - primero).
- cons x y, devuelve uan lista formada por x e y.
- list x y z, devuelve la lista (x y z).
- quote x, no se evalúa x.
- Predicados
- atom x, devuelve True si x es un átomo.
- numberp x, devuelve True si x es un número.
- greaterp x y, devuelve True si x>y.
- lessp x y, devuelve True si x<y.
- null x, devuelve True si x es nulo.
- and x y, devuelve x and y.
- or x y, devuelve x or y.
- not x, devuelve not x.
- eq x y, devuelve True si x=y.
- Funciones aritméticas:
- +, -, *, y /.
- rem x y, devuelve el módulo x/y (remainder).
- Funciones de entrada y salida
- load nombrearchivo, lee el archivo a memoria.
- print x, imprime el elemento x.
- open nombrearchivo, abre un archivo y devuelve una puntero al mismo.
- read, lee del terminal un átomo.
- help, proporciona ayuda.
- trace, traza la función.
- bye, termina LISP.
Abstracción y encapsulamiento
- LISP, en origen, no incluye características de abstracción de datos.
- CLOS fue una ampliación de LISP con orientación de objetos. Características:
- Herencia múltiple.
- Funciones genéricas.
- Metaclases y metaobjetos.
- Técnica de creación e inicialización de objetos que permite control del proceso por parte del usuario.
Evaluación del lenguaje
- LISP ha evolucionado durante más de 30 años y desarrollado para inteligencia artificial, pero no es adecuado para aplicaciones convencionales.
- Las versiones compiladas son algo más eficientes.
Guillermo Aedo Contreras