LISP
Upcoming SlideShare
Loading in...5
×
 

LISP

on

  • 659 views

manual sencillo de programación en lisp, uno de los lenguajes de programación con la curva de aprendizaje mas difícil

manual sencillo de programación en lisp, uno de los lenguajes de programación con la curva de aprendizaje mas difícil
Autor Dr Hugo Banda

Statistics

Views

Total Views
659
Views on SlideShare
659
Embed Views
0

Actions

Likes
0
Downloads
25
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

LISP LISP Document Transcript

  • ESCUELA POLITÉCNICA NACIONAL DEPARTAMENTO DE INFORMÁTICA Y CIENCIAS DE LA COMPUTACIÓN PROGRAMACIÓN EN LISP NIVEL BÁSICO Hugo A. Banda Gamboa PhD, MSc. 2003
  • PRESENTACIÓN Este manual de programación utilizando el lenguaje LISP, fue desarrollado como una guía para mis estudiantes de la materia Sistemas Inteligentes, que se dicta en el Programa de Ingeniería en Sistemas Informáticos y de Computación de la Escuela Politécnica Nacional. Básicamente, es una condensación del libro clásico LISP, de Patrick Hendry Winston y Berthold Klaus Paul Horn. También se incluyen aportes de otras obras consultadas, que se indican en la sección BIBLIOGRAFÍA, y experiencias recogidas durante 14 semestres que he dictado esta materia. Espero que esta obra cumpla con su propósito de incentivar al estudio y al trabajo experimental en las técnicas y aplicaciones de la inteligencia artificial, utilizando LISP. La referencia al interprete LISP y los ejemplos han sido desarrollados utilizando el producto ALLEGRO CL for WINDOWS, Version 5.0 Professional, de la casa: Franz Inc. 1995 University Avenue, Berkeley CA 94704, USA. Para los estudiantes que estén interesados en obtener una versión limitada del Allegro CL, en forma gratuita, pueden hacerlo de la página WWW que mantiene Franz Inc. Su dirección es: http://www.franz.com Finalmente, deseo dejar constancia de mi agradecimiento a mi familia, Paty y Huguito, quienes generosamente me dieron su apoyo para la realización de esta obra. Quito, junio de 2003 Atentamente, Hugo A. Banda Gamboa, PhD, MSc.. Profesor Principal Departamento de Informática y Ciencias de la Computación ESCUELA POLITÉCNICA NACIONAL Quito - Ecuador
  • SISTEMAS DE INTELIGENCIA ARTIFICIAL PROGRAMACIÓN EN LISP CONTENIDO 1. INTRODUCCIÓN ....................................................................................................................................... 1 2. TIPOS DE DATOS...................................................................................................................................... 1 3. INTERACCIÓN CON EL INTÉRPRETE LISP...................................................................................... 2 4. PRINCIPALES FUNCIONES PRIMITIVAS .......................................................................................... 3 4.1 SETF .................................................................................................................................................... 4 4.2 FIRST (CAR)....................................................................................................................................... 4 4.3 REST (CDR) ........................................................................................................................................ 4 4.4 NTHCDR ............................................................................................................................................. 4 4.5 BUTLAST............................................................................................................................................ 5 4.6 LAST.................................................................................................................................................... 5 4.7 LENGTH Y REVERSE........................................................................................................................ 5 4.8 CONS................................................................................................................................................... 5 4.9 APPEND .............................................................................................................................................. 6 4.10 LIST ..................................................................................................................................................... 6 4.11 REMOVE............................................................................................................................................. 6 4.12 ASSOC................................................................................................................................................. 6 4.13 SUMA, RESTA, PRODUCTO Y DIVISIÓN ...................................................................................... 7 4.14 FLOAT................................................................................................................................................. 7 4.15 ROUND................................................................................................................................................ 8 4.16 TRUNCATE ........................................................................................................................................ 8 4.17 REM..................................................................................................................................................... 8 4.18 MAX Y MIN......................................................................................................................................... 8 4.19 EXPT.................................................................................................................................................... 9 4.20 SQRT.................................................................................................................................................... 9 4.21 ABS...................................................................................................................................................... 9 5. DEFINICIÓN DE PROCEDIMIENTOS Y LIGADURA........................................................................ 9 5.1 DEFUN .............................................................................................................................................. 10 5.2 LET .................................................................................................................................................... 10 5.3 LET* .................................................................................................................................................. 11 6. PREDICADOS Y CONDICIONALES.................................................................................................... 11 6.1 =, EQ, EQL Y EQUAL....................................................................................................................... 11 6.2 MEMBER........................................................................................................................................... 12 6.3 ATOM, NUMBERP, SYMBOLP Y LISTP ....................................................................................... 13 6.4 NULL Y ENDP................................................................................................................................... 14 6.5 ZEROP, PLUSP, MINUSP, EVENP, ODDP, > Y <.......................................................................... 14 6.6 AND, OR Y NOT ............................................................................................................................... 14 6.7 IF, WHEN Y UNLESS....................................................................................................................... 15 6.8 COND ................................................................................................................................................ 15 6.9 CASE.................................................................................................................................................. 16 7. ABSTRACCIÓN DE PROCEDIMIENTOS Y RECURSIÓN .............................................................. 17 7.1 PROCEDIMIENTOS RECURSIVOS ............................................................................................... 17 7.2 PARÁMETROS OPCIONALES ....................................................................................................... 20 7.3 PARÁMETROS RESTANTES ......................................................................................................... 20 7.4 PARÁMETROS CLAVE................................................................................................................... 21 7.5 PARÁMETROS AUXILIARES........................................................................................................ 21 8. TRANSFORMACIONES Y FUNCIONES ANÓNIMAS...................................................................... 22 i
  • PROGRAMACIÓN EN LISP SISTEMAS DE INTELIGENCIA ARTIFICIAL 8.1 MAPCAR ...........................................................................................................................................22 8.2 REMOVE-IF Y REMOVE-IF-NOT...................................................................................................22 8.3 COUNT-IF Y FIND-IF .......................................................................................................................22 8.4 FUNCALL Y APPLY.........................................................................................................................23 8.5 FUNCIONES ANÓNIMAS (LAMBDA) ..........................................................................................23 9. ITERACIÓN ..............................................................................................................................................24 9.1 DOTIMES ..........................................................................................................................................24 9.2 DOLIST..............................................................................................................................................24 9.3 DO Y DO* ..........................................................................................................................................25 9.4 LOOP..................................................................................................................................................25 9.5 PROG1 Y PROGN..............................................................................................................................26 10. LECTURA Y ESCRITURA.................................................................................................................26 10.1 PRINT, PRIN1, PRINC Y TERPRI....................................................................................................26 10.2 READ .................................................................................................................................................27 10.3 FORMAT ...........................................................................................................................................27 10.4 WITH-OPEN-FILE............................................................................................................................28 10.5 ELT.....................................................................................................................................................29 10.6 STRING= Y STRING-EQUAL..........................................................................................................29 10.7 CHAR= Y CHAR-EQUAL.................................................................................................................29 10.8 SEARCH ............................................................................................................................................30 10.9 READ-CHAR.....................................................................................................................................30 10.10 READ-LINE.......................................................................................................................................30 11. BIBLIOGRAFÍA...................................................................................................................................31 ii
  • SISTEMAS INTELIGENTES PROGRAMACIÓN EN LISP PROGRAMACIÓN EN LISP 1. INTRODUCCIÓN La manipulación simbólica es el bloque constructivo básico de los programas de inteligencia artificial. Programas para manipulación simbólica pueden reconocer expresiones simbólicas particulares y pueden dividir expresiones para construir nuevas. LISP es un lenguaje diseñado para manipulación simbólica, en contraste con los lenguajes de programación convencionales que están primordialmente diseñados para procesamiento numérico. En lugar de manipular estructuras de datos numéricos (como números y arreglos), los programas en LISP típicamente manipulan estructuras de datos simbólicos (como palabras y oraciones). Lo que distingue a LISP de otros lenguajes de programación, es que está diseñado para evolucionar. Ofrece construcciones especiales que permite escribir programas que escriben programas. A continuación se presenta una colección de lecciones diseñadas para aprender los fundamentos de programación utilizando Common LISP. Este es un poderoso lenguaje de inteligencia artificial rico en construcciones y funciones para operar sobre estructuras de datos creados por el usuario. Sin embargo, existe un pequeño subconjunto del Common LISP, que se puede utilizar para ilustrar muchos de los conceptos fundamentales de programación, requeridos para comprender el lenguaje LISP. 2. TIPOS DE DATOS En LISP, virtualmente cualquier estructura de datos puede ser representada como un objeto. LISP utiliza dos tipos de objetos: átomos y listas. Los átomos son objetos simples que pueden ser simbólicos o numéricos. Los átomos numéricos o simplemente números, pueden ser enteros, racionales, reales y complejos, tanto positivos como negativos. Por ejemplo: 12 2.5 -30 0.005 -8.0 1997 2/3 #C(0 2) -5/8 Los átomos simbólicos o simplemente símbolos, pueden incluir palabras, letras y caracteres especiales. Por ejemplo: Hola P200 Esto_es_un_átomo + - / * Una lista es un objeto compuesto que consiste de cero o más átomos o listas encerradas entre paréntesis. Por ejemplo: () (9) (Politécnica Nacional) (+ 12 76) (colores (rojo azul verde)) A los átomos y listas se los denomina expresiones simbólicas o simplemente expresiones. Estas expresiones constituyen los tipos de datos. En LISP, los valores son los que tienen Hugo A. Banda Gamboa, PhD, MSc. Junio, 2003 1
  • PROGRAMACIÓN EN LISP SISTEMAS INTELIGENTES tipos, no las variables. No es necesario declarar los tipos de variables porque todas las variables pueden contener objetos de cualquier tipo. Las ocurrencias específicas de los diversos tipos de datos se denominan objetos. Así, 1997 es un objeto que pertenece al tipo de datos número, HOLA es un objeto del tipo de datos símbolo y (A B C) es un objeto del tipo de datos lista. LISP tiene también muchos otros tipos de datos, incluyendo caracteres, arreglos, cadenas y estructuras. La estructura básica, de mayor importancia en LISP, es la lista. Una lista puede utilizarse para representar una estructura de árbol. Por ejemplo la siguiente lista representa la descripción del árbol de la Figura 1. ( X ( A ) ( B ( E ) ( F ) ( G ) ) ( C ) ( D ) ) Figura. 1. Representación en árbol E F G DCA B X Las listas también se emplean en procedimientos y funciones. A LISP se lo puede considerar como un lenguaje de programación funcional porque utiliza funciones para manipular las estructuras de expresiones simbólicas. 3. INTERACCIÓN CON EL INTÉRPRETE LISP La interacción con LISP es muy similar a la que se realiza cuando se emplea una calculadora de bolsillo. En primer lugar, ante el símbolo del interprete (>) se ingresa una expresión, luego LISP lee la expresión, la evalúa y entrega el resultado. Para prevenir la evaluación de una expresión, se debe poner antes de la expresión el apóstrofe ( ' ). Si se ingresa el nombre de un símbolo sin el apóstrofe, el sistema retorna el valor asociado al símbolo si está definido, caso contrario despliega un mensaje de error. Por ejemplo: > HOLA ;; Error: Unbound variable HOLA in #<function 1 #x811040> ;; Returning to Top Level > 'HOLA HOLA > 2 Junio, 2003 Hugo A. Banda Gamboa, PhD, MSc.
  • SISTEMAS INTELIGENTES PROGRAMACIÓN EN LISP Por convención, ciertos átomos se evalúan a sí mismos. Entre los átomos simbólicos, T (VERDADERO) y NIL (NULO) siempre se auto evalúan. También los átomos numéricos se evalúan a sí mismos: > t T > nil NIL > 34 34 > 19.45 19.45 > Las listas se ingresan encerrando sus elementos entre paréntesis. No importa lo que esté en la lista, LISP trata de evaluarla asumiendo que el primer elemento es el nombre de una función o de un procedimiento y que los otros elementos son sus argumentos. Dado un conjunto ordenado de objetos, una función LISP retorna un valor único, basado en sus argumentos. Pero si se antepone a la lista el apóstrofe, el sistema responde con la misma lista. Por ejemplo: > (A) ;; Error: Call to undefined function A in #<function 0 #xE72950> ;; Returning to Top Level > '(A) (A) > (A B) ;; Error: Unbound variable B in #<function 0 #xE7B944> ;; Returning to Top Level > '(A B) (A B) Existen dos tipos de funciones en LISP, las definidas por el sistema y las definidas por el usuario. Las funciones definidas por el sistema se las denomina también funciones primitivas. Escribir programas en LISP consiste en utilizar las funciones primitivas y definir funciones adicionales para realizar alguna tarea deseada. 4. PRINCIPALES FUNCIONES PRIMITIVAS LISP es un acrónimo de LISt Processing. Como es de esperar, LISP proporciona funciones primitivas para operar sobre listas de objetos. En LISP, las llamadas a funciones utilizan el siguiente formato: (nombre arg1 arg2 … argN) Donde nombre corresponde al nombre de la función, arg1 es el primer argumento, arg2 es el segundo argumento, y así sucesivamente. Al proceso de reservar un lugar en la memoria del computador con el fin de almacenar un valor para un símbolo se lo denomina ligadura; y al proceso de almacenar un valor en ese lugar asignación. El proceso de recuperar un valor de ese lugar es un tipo de evaluación. Hugo A. Banda Gamboa, PhD, MSc. Junio, 2003 3
  • PROGRAMACIÓN EN LISP SISTEMAS INTELIGENTES 4.1 SETF LISP asocia símbolos conforme los encuentra. Una manera de asignar un valor a un símbolo es mediante la función primitiva SETF. Esta función hace que el valor de su segundo argumento se asigne a su primer argumento. > (SETF AÑO '1997) 1997 > AÑO 1997 > (SETF animales '(perro gato león tigre)) (PERRO GATO LEóN TIGRE) > animales (PERRO GATO LEóN TIGRE) Se pueden incluir varios pares símbolo - valor en una sola instrucción SETF. Los argumentos que están en posición impar no se evalúan, sólo los que están en lugares pares. La función devuelve el valor del argumento final. > (SETF FELINOS '(GATO LEÓN TIGRE) ROEDORES '(RATÓN CONEJO CUY)) (RATÓN CONEJO CUY) > FELINOS (GATO LEÓN TIGRE) > ROEDORES (RATÓN CONEJO CUY) Una de las operaciones más comunes es la extracción o selección de uno de los miembros de una lista. Las funciones que realizan esta operación de denominan funciones selectoras. Las principales funciones selectoras son: FIRST y REST. Los nombres dados a estas funciones en implementaciones antiguas son: CAR y CDR respectivamente. 4.2 FIRST (CAR) Devuelve el primer objeto de una lista dada como argumento. > (FIRST '(amarillo azul rojo)) AMARILLO 4.3 REST (CDR) Hace el trabajo complementario a FIRST. Devuelve una lista que contiene todos los objetos de la lista original, excepto el primero. > (REST '(amarillo azul rojo)) (AZUL ROJO) 4.4 NTHCDR Elimina los n primeros elementos (primer argumento), de una lista dada como segundo argumento. Si el primer argumento de NTHCDR es mayor o igual que el número de elementos de la lista, retorna NIL. > (NTHCDR 2 '(amarillo azul rojo)) (ROJO) > (NTHCDR 3 '(amarillo azul rojo)) NIL 4 Junio, 2003 Hugo A. Banda Gamboa, PhD, MSc.
  • SISTEMAS INTELIGENTES PROGRAMACIÓN EN LISP 4.5 BUTLAST Es similar a NTHCDR, sólo que en lugar de eliminar los primeros n elementos, elimina los n últimos. También difiere en el orden de los argumentos, la lista es el primer argumento y el valor de n es el segundo. Si no existe el segundo argumento, sólo se elimina el último elemento. > (BUTLAST '(amarillo azul rojo) 2) (AMARILLO) > (BUTLAST '(amarillo azul rojo)) (AMARILLO AZUL) 4.6 LAST Devuelve una lista en la que se ha eliminado todos los elementos, excepto el último. > (LAST '(amarillo azul rojo)) (ROJO) Es importante notar que LAST devuelve una lista conteniendo al último elemento de la lista original. Para extraer el último elemento de la lista, se debe combinar FIRST y LAST: > (FIRST (LAST '(amarillo azul rojo))) ROJO El anterior ejemplo ilustra la posibilidad de utilizar el valor retornado por la llamada a una función como argumento para otra función. 4.7 LENGTH y REVERSE La primitiva LENGTH cuenta el número de elementos del nivel superior que hay en una lista, y REVERSE invierte el orden de estos. Ambas funciones consideran que su argumento es una lista de elementos, sin importar que estos sean listas o átomos. Cuando sus argumentos son listas cuyos objetos son listas, no se afectan a las sublistas. > (LENGTH '(amarillo azul rojo)) 3 > (LENGTH '((Pichincha Quito) (Azuay Cuenca) (Carchi Tulcán))) 3 > (REVERSE '(amarillo azul rojo)) (ROJO AZUL AMARILLO) > (REVERSE '((Pichincha Quito) (Azuay Cuenca) (Carchi Tulcán))) ((CARCHI TULCáN) (AZUAY CUENCA) (PICHINCHA QUITO)) LISP también proporciona funciones constructoras. Estas son: CONS, APPEND y LIST. 4.8 CONS Toma sólo 2 argumentos: una expresión y una lista. Retorna una nueva lista, donde el primer elemento es la expresión y los restantes son los de la lista dada como segundo argumento. > (CONS 'perro '(gato loro ratón)) (PERRO GATO LORO RATóN) > (CONS '(A B) '(X Y)) ((A B) X Y) Hugo A. Banda Gamboa, PhD, MSc. Junio, 2003 5
  • PROGRAMACIÓN EN LISP SISTEMAS INTELIGENTES 4.9 APPEND Acepta una o más listas como argumentos. Retorna una lista en la que están combinados los elementos del nivel superior de las listas dadas como argumentos. > (APPEND '(perro) '(gato loro ratón)) (PERRO GATO LORO RATóN) > (APPEND '(A B) '(X Y)) (A B X Y) > (APPEND '(a) '(b c) '(w (x y) z)) (A B C W (X Y) Z) > 4.10 LIST Trabaja con uno o más argumentos. Su resultado es una nueva lista en la que cada uno de los argumentos se vuelve un elemento. > (LIST 'perro '(gato loro ratón)) (PERRO (GATO LORO RATóN)) > (LIST '(perro) '(gato loro ratón)) ((PERRO) (GATO LORO RATóN)) > (LIST 'perro 'gato 'loro 'ratón) (PERRO GATO LORO RATóN) 4.11 REMOVE Acepta dos argumentos: un símbolo y una lista. Retorna la lista dada como segundo argumento, en la que todas las ocurrencias en el nivel superior del símbolo dado como primer argumento ha sido removido. > (REMOVE 'd '(a (b c) d e f)) (A (B C) E F) > (REMOVE '(b c) '(a (b c) d e f)) (A (B C) D E F) > (REMOVE 'b '(a (b c) d e f)) (A (B C) D E F) > Es importante notar que las funciones CONS, APPEND, LIST y REMOVE no alteran el valor de los objetos utilizados como sus argumentos. 4.12 ASSOC Para acceder a sublistas, LISP proporciona la función ASSOC. Esta primitiva está especialmente diseñada para trabajar con listas de asociación. Una lista de asociación consta de una serie de sublistas. El primer elemento de cada sublista se utiliza como clave para recuperar la sublista completa. (ASSOC <clave> <lista de asociación>) La operación de esta primitiva se muestra en los siguientes ejemplos: > (SETF PC1 '((RAM 8KB) (DISCO 1.2GB)) PC2 '((RAM 32KB) (DISCO 2.0 GB))) ((RAM 32KB) (DISCO 2.0 GB)) 6 Junio, 2003 Hugo A. Banda Gamboa, PhD, MSc.
  • SISTEMAS INTELIGENTES PROGRAMACIÓN EN LISP > (ASSOC 'RAM PC1) (RAM 8KB) > (ASSOC 'DISCO PC2) (DISCO 2.0 GB) ASSOC siempre retorna la sublista completa cuyo primer elemento es igual a la clave. En el caso de que más de una sublista cumpla con la condición, sólo devuelve la primera ocurrencia, el resto de sublistas permanecen ocultas. LISP también proporciona primitivas para operar sobre números enteros, racionales, reales y complejos: +, -, *, /, FLOAT, ROUND, MAX, MIN, EXPT, SQRT, ABS. 4.13 SUMA, RESTA, PRODUCTO y DIVISIÓN Las funciones suma ( + ), resta ( - ), producto (*) y división ( / ), pueden operar con uno o más argumentos. > (+ 1.5 5 3.9 12) 22.4 > (- 16 4.5) 11.5 > (* 2 3 4) 24 > (+ #C(0 2) #C(1 -3)) #C(1 -1) Cuando sus 2 argumentos son números enteros y no se dividen exactamente, la división retorna un número racional. Basta con que uno de sus argumentos sea un número real, la división dará como resultado también un número real. > (/ 3 4) 3/4 > (/ 3.0 4) 0.75 > (/ #C(3 4) #C(1 -1)) #C(-1/2 7/2) > (/ #C(3.0 4.0) #C(1.0 -1.0)) #C(-0.5 3.5) Si la función divisora sólo tiene un argumento, calcula el recíproco del número dado: > (/ 3) > 1/3 > (/ #C(-4 3)) #C(-4/25 -3/25) > (/ #C(-4.0 3.0)) #C(-0.16 -0.12) 4.14 FLOAT Cuando se desea obtener un resultado de punto flotante, se puede utilizar FLOAT, una primitiva que convierte su argumento en número real. > (FLOAT (/ 3 4)) 0.75 Hugo A. Banda Gamboa, PhD, MSc. Junio, 2003 7
  • PROGRAMACIÓN EN LISP SISTEMAS INTELIGENTES 4.15 ROUND Redondea el resultado de la división hacia el entero más cercano. ROUND entrega dos valores. El primero es el entero más cercano. Si el resulado está justo en el medio de dos valores enteros, retorna el numero par. El segundo valor es el residuo producido por la operación de redondeo. > (ROUND 0.99) 1 -0.01 > (ROUND -1.5) -2 0.5 > (ROUND 3 2) 2 -1 4.16 TRUNCATE Trunca el resultado de la división de sus argumentos, en dirección al entero más cercano al cero. Retorna dos valores: el entero con el mismo signo que el resultado de la división y el residuo producido por la operación de truncado. > (TRUNCATE 0.99) 0 0.99 > (TRUNCATE -1.5) -1 -0.5 > (TRUNCATE 3 2) 1 1 > 4.17 REM Toma dos argumentos y retorna el residuo de la división entre ellos. > (REM 17 6) 5 > (REM -17 6) -5 > (REM -17 -6) -5 > (REM 17 -6) 5 > 4.18 MAX y MIN Las primitivas MAX y MIN, retornan el máximo y el mínimo, respectivamente, dada una secuencia de números: > (MAX 2 5 3) 5 > (MIN 2 5 3) 2 8 Junio, 2003 Hugo A. Banda Gamboa, PhD, MSc.
  • SISTEMAS INTELIGENTES PROGRAMACIÓN EN LISP 4.19 EXPT Calcula potencias elevando su primer argumento al segundo. Puede operar sobre números enteros y reales. > (EXPT 3 4) 81 > (EXPT 2.3 4.2) 33.0565032282171 4.20 SQRT SQRT extrae la raíz cuadrada de su argumento. Si el argumento es un número negativo, retorna un número complejo: > (SQRT 27) 5.19615242270663 > (SQRT -25) #C(0.0 5.0) 4.21 ABS Retorna el valor absoluto de su argumento: > (ABS -34) 34 5. DEFINICIÓN DE PROCEDIMIENTOS Y LIGADURA Una de las principales fortalezas de LISP es que los programas pueden ser usados como datos para otros programas. Esto se deriva del hecho de que los datos y los programas en LISP son representados de la misma forma. Los programas son construidos en base a formas y funciones. Las formas son las unidades fundamentales de los programas en LISP, los cuales son evaluados para retornar uno o más valores y también pueden producir otros efectos colaterales. Algunas pueden llamar a funciones. A su vez, una función es una colección de formas que son evaluadas cuando se llama a la función. Las funciones son llamadas con sus respectivos argumentos. A las funciones se las puede dar nombres utilizando símbolos. Para definir una función o procedimiento, LISP necesita conocer 3 cosas: • El nombre de la función o procedimiento. • Los de argumentos que necesita. • La tarea que la función o procedimiento debe realizar A los argumentos de la función o procedimiento se los denomina parámetros. Los parámetros de un procedimiento son variables ligadas a él. Una variable es un símbolo con el que se designa algún lugar de memoria destinado para almacenar un valor. Al iniciar la ejecución de la función, a cada parámetro se le asigna un valor. La tarea definida para ser realizada, constituye el cuerpo del procedimiento. El cuerpo de un procedimiento consta de las formas que son evaluadas cuando éste se utiliza. Hugo A. Banda Gamboa, PhD, MSc. Junio, 2003 9
  • PROGRAMACIÓN EN LISP SISTEMAS INTELIGENTES Para definir procedimientos LISP proporciona la primitiva DEFUN, un acrónimo de DEFinir FUNción. Para documentar las construcciones, LISP permite la inserción de comentarios. El punto y coma (;) indica al interprete que todo lo que está a la derecha hasta el fin de la línea, es comentario. 5.1 DEFUN La plantilla general para DEFUN es la siguiente: (DEFUN < nombre del procedimiento> ; nombre de la función ( < parámetro 1> <parámetro 2> … <parámetro N) ; argumentos < forma 1 > ; cuerpo de la función: < forma 2 > ; varias formas. … < forma M > ) Como ejemplos, a continuación se definen un procedimiento para rotar a la izquierda y otro para rotar a la derecha los elementos de una lista dada como argumento: > (DEFUN rotizq (Lista) (APPEND (REST Lista) (LIST (FIRST Lista)))) ROTIZQ > (rotizq '(a b c d e)) (B C D E A) > (DEFUN rotder (Lista) (APPEND (LAST Lista) (BUTLAST Lista))) ROTDER > (rotder '(a b c d e)) (E A B C D) 5.2 LET Es una primitiva que liga parámetros de la misma manera que éstos son ligados al iniciar la ejecución de un procedimiento. La plantilla general de LET es la siguiente: ( LET ( (< parámetro 1> <valor inicial 1 ) … ( parámetro N> <valor inicial N ) ) < forma 1> … < forma M> ) A continuación, utilizando LET, se construye una operación que retorna una lista que tiene los objetos extremos de la lista original dada. > (SETF semana '(LUN MAR MIE JUE VIE SAB DOM)) (LUN MAR MIE JUE VIE SAB DOM) > (LET ((primero (FIRST semana)) (último (LAST semana))) (CONS primero último)) (LUN DOM) 10 Junio, 2003 Hugo A. Banda Gamboa, PhD, MSc.
  • SISTEMAS INTELIGENTES PROGRAMACIÓN EN LISP Como se demuestra en el siguiente ejemplo, la primitiva LET evalúa en paralelo sus formas para valores iniciales, antes de que cualquiera de sus parámetros sea ligado. > (SETF a 'Valor-Externo) ; El valor de a es Valor-Externo VALOR-EXTERNO > (LET ((a 'Valor-Interno) ; El valor de a será Valor-Interno (b a)) ; El valor de b será Valor-Externo (LIST a b)) (VALOR-INTERNO VALOR-EXTERNO) > 5.3 LET* LET* es la versión de LET que evalúa en forma secuencial sus formas para valores iniciales. Esto es, liga los parámetros de tal forma que el valor de un parámetro ligado con anterioridad puede ser utilizado para calcular el valor de un parámetro ligado después. > (SETF a 'Valor-Externo) ; El valor de a es Valor-Externo VALOR-EXTERNO > (LET* ((a 'Valor-Interno) ; El valor de a será Valor-Interno (b a)) ; El valor de b será Valor-Interno (LIST a b)) (VALOR-INTERNO VALOR-INTERNO) 6. PREDICADOS Y CONDICIONALES Un predicado es un procedimiento que devuelve un valor que puede ser verdadero o falso. El resultado falso siempre se indica con NIL, mientras que el símbolo T o cualquier valor diferente de NIL se considera como verdadero. Estas pruebas, combinadas con condicionales permiten definir procedimientos mucho más poderosos. 6.1 =, EQ, EQL y EQUAL Existen varios predicados que determinan si sus argumentos son iguales. • El predicado = verifica que sus argumentos representen el mismo número, aun cuando no sean del mismo tipo numérico. • EQ verifica que sus argumentos estén representados en las misma localidades de memoria, es decir que sean símbolos idénticos. • EQL primero verifica si sus argumentos satisfacen EQ. Si no lo hacen trata de ver si son números del mismo tipo y con igual valor. • EQUAL primero verifica si sus argumentos satisfacen EQL. Si no lo hacen trata de verificar si son listas cuyos elementos satisfacen EQUAL. > (SETF X 4 Y 4.0 FELINO 'GATO AVE 'PATO DIAS '(LUN MAR MIE JUE VIE)) (LUN MAR MIE JUE VIE) > (= X Y) T > (EQ X Y) NIL > (EQ FELINO 'GATO) T > (EQ '(LUN MAR MIE JUE VIE) DIAS) NIL Hugo A. Banda Gamboa, PhD, MSc. Junio, 2003 11 > (EQL AVE 'PATO)
  • PROGRAMACIÓN EN LISP SISTEMAS INTELIGENTES T > (EQL '(LUN MAR MIE JUE VIE) DIAS) NIL > (EQUAL 'GATO FELINO) T > (EQUAL '(LUN MAR MIE JUE VIE) DIAS) T > (EQUAL X Y) NIL 6.2 MEMBER El predicado MEMBER verifica que su primer argumento sea un elemento del nivel superior de su segundo argumento, que debe ser una lista. Devuelve lo que queda de la lista al encontrar el símbolo coincidente, si éste pertenece a dicha lista. > (SETF ORACION '(La imaginación es más importante que el conocimiento) PAREJAS '((Luis Ana) (Rodrigo Martha) (Juan Rosa))) ((LUIS ANA) (RODRIGO MARTHA) (JUAN ROSA)) > (MEMBER 'importante ORACION) (IMPORTANTE QUE EL CONOCIMIENTO) > (MEMBER 'Rodrigo PAREJAS) NIL > (MEMBER '(Rodrigo Martha) PAREJAS) NIL MEMBER normalmente hace sus pruebas utilizando EQL, por este motivo, en el ejemplo anterior, no puede reconocer a la sublista (Rodrigo Martha) como miembro de PAREJAS. Pero COMMON LISP permite el uso de argumentos clave para modificar el comportamiento de ciertos procedimientos como MEMBER. La sintaxis para incluir argumentos clave en el predicado MEMBER, requiere de una palabra clave :TEST o :TEST-NOT, seguida por el argumento clave. El argumento clave está compuesto por los caracteres #’ y el nombre del procedimiento a ser utilizado, en este caso resultaría #’EQUAL. En particular, la palabra clave :TEST indica que el siguiente argumento especifica la prueba que debe usar MEMBER. Si en su lugar aparece la palabra clave :TEST-NOT, MEMBER devuelve lo que queda de la lista luego de la primera aparición de un elemento, si lo hay, que no sea igual al primer argumento, donde el argumento clave determina qué es lo que significa igual. El objetivo de los caracteres #’ es producir un procedimiento objeto a partir del nombre del procedimiento. Los cinco caracteres EQUAL constituyen el nombre del procedimiento. Las instrucciones de la computadora que ejecutan la prueba requerida constituyen el procedimiento objeto. De esta forma, el argumento clave resulta ser una variable cuyo valor es un procedimiento objeto, y como tal puede ser ligada a un símbolo. > (MEMBER 'Rodrigo PAREJAS :TEST #'EQUAL) NIL > (MEMBER 'Rodrigo PAREJAS :TEST-NOT #'EQUAL) ((LUIS ANA) (RODRIGO MARTHA) (JUAN ROSA)) > (SETF PRED #'EQUAL) ; Argumento clave es ligado a símbolo PRED #<function 2 #x8D2ADC> ; Respuesta del sistema > (MEMBER '(Rodrigo Martha) PAREJAS :TEST PRED) ((RODRIGO MARTHA) (JUAN ROSA)) 12 Junio, 2003 Hugo A. Banda Gamboa, PhD, MSc.
  • SISTEMAS INTELIGENTES PROGRAMACIÓN EN LISP > (MEMBER '(Rodrigo Martha) PAREJAS :TEST-NOT PRED) ((LUIS ANA) (RODRIGO MARTHA) (JUAN ROSA)) > (MEMBER '(Luis Ana) PAREJAS :TEST PRED) ((LUIS ANA) (RODRIGO MARTHA) (JUAN ROSA)) > (MEMBER '(Luis Ana) PAREJAS :TEST-NOT PRED) ((RODRIGO MARTHA) (JUAN ROSA)) > (MEMBER '(Juan Rosa) PAREJAS :TEST PRED) ((JUAN ROSA)) > (MEMBER '(Juan Rosa) PAREJAS :TEST-NOT PRED) ((LUIS ANA) (RODRIGO MARTHA) (JUAN ROSA)) En programas antiguos, se pueden ver expresiones como (FUNCTION EQUAL), en lugar de #’EQUAL. La combinación #’ es un tipo de refinamiento sintáctico. En realidad, el intérprete LISP al encontrar la secuencia #’<expresión> la convierte en (FUNCTION <expresión>). Algunas versiones modernas de LISP todavía soportan la sintaxis antigua, por compatibilidad. > (MEMBER '(Rodrigo Martha) PAREJAS :TEST (FUNCTION EQUAL)) ((RODRIGO MARTHA) (JUAN ROSA)) > 6.3 ATOM, NUMBERP, SYMBOLP y LISTP LISP tiene varios predicados que verifican si un objeto pertenece a un tipo especial de datos. PREDICADO PRUEBA ATOM ¿Es un átomo? NUMBERP ¿Es un número? SYMBOLP ¿Es un símbolo? LISTP ¿Es una lista? > PI 3.14159265358979 > (ATOM PI) T > (NUMBERP PI) T > (SYMBOLP PI) NIL > (SYMBOLP 'PI) T > (LISTP PI) NIL > (LISTP 'PI) NIL En LISP existe una importante peculiaridad ya NIL y la lista vacía ( ) son totalmente equivalentes. Además, NIL y ( ) son tanto símbolos como listas. > NIL NIL > () NIL > (ATOM NIL) T > (ATOM ()) Hugo A. Banda Gamboa, PhD, MSc. Junio, 2003 13
  • PROGRAMACIÓN EN LISP SISTEMAS INTELIGENTES T > (SYMBOLP NIL) T > (SYMBOLP ()) T > (LISTP NIL) T > (LISTP ()) T 6.4 NULL y ENDP Son predicados que verifican si su argumento es una lista vacía. Los siguientes ejemplos ilustran la diferencia entre los dos predicados. > (REST (LAST '(A B C D))) NIL > (NULL (REST (LAST '(A B C D)))) T > (ENDP (REST (LAST '(A B C D)))) T > (NULL PI) NIL > (ENDP PI) T > (NULL 'PI) NIL > (ENDP 'PI) T 6.5 ZEROP, PLUSP, MINUSP, EVENP, ODDP, > y < Además de NUMBERP, los predicados que trabajan con átomos numéricos, son los siguientes: PREDICADO PRUEBA ZEROP ¿Es cero? PLUSP ¿Es positivo? MINUSP ¿Es negativo? EVENP ¿Es número par? ODDP ¿Es número impar? > ¿Están en orden descendente? < ¿Están en orden ascendente? 6.6 AND, OR y NOT Para combinar los resultados de las pruebas de dos o más predicados, se pueden utilizar los operadores lógicos AND, OR y NOT. 14 Junio, 2003 Hugo A. Banda Gamboa, PhD, MSc.
  • SISTEMAS INTELIGENTES PROGRAMACIÓN EN LISP OPERADOR OPERACIÓN AND • Los argumentos son evaluados de izquierda a derecha. Si alguno de ellos tiene como valor NIL, el resto de los argumentos ya no se evalúan y el valor devuelto es NIL. • Si todos los argumentos tienen un valor diferente de NIL, se devuelve el valor del último de los argumentos. OR • Los argumentos son evaluados de izquierda a derecha. Si alguno de ellos tiene valor diferente de NIL, ninguno de los argumentos se evalúan y el valor devuelto es ese valor diferente de NIL. • Si ninguno de los argumentos tiene un valor diferente de NIL, se devuelve el valor NIL. NOT • Convierte valores NIL a T y valores diferentes de NIL a NIL. > (SETF mascotas '(perro gato)) (PERRO GATO) > (AND (MEMBER 'perro mascotas) (MEMBER 'tigre mascotas)) NIL > (OR (MEMBER 'perro mascotas) (MEMBER 'tigre mascotas)) (PERRO GATO) > (AND (MEMBER 'perro mascotas) (NOT (MEMBER 'tigre mascotas))) T > (OR (MEMBER 'perro mascotas) (NOT (MEMBER 'tigre mascotas))) (PERRO GATO) > (OR (NOT (MEMBER 'perro mascotas)) (MEMBER 'tigre mascotas)) NIL 6.7 IF, WHEN y UNLESS Los predicados se utilizan casi siempre dentro de condicionales, para determinar de entre varias formas cuál debe evaluarse. LISP proporciona los siguientes condicionales básicos: (IF <prueba> <forma a evaluar si la prueba es no NIL> <forma a evaluar si la prueba es NIL>) (WHEN <prueba> <forma(s) a evaluar si la prueba es no NIL>) (UNLESS <prueba> <forma(s) a evaluar si la prueba es NIL>) Tanto WHEN como UNLESS, pueden tener cualquier número de argumentos. El primero siempre es la forma de prueba; el último proporciona el valor que se devolverá. Sus argumentos, después del primero, sólo se evalúan si el valor de la prueba así lo indica. 6.8 COND Es un condicional mucho más versátil que IF, WHEN y UNLESS. La plantilla de esta forma es la siguiente: (COND (<prueba 1> <consecuente 1-1> <consecuente 1-2> … <consecuente 1-N>) (<prueba 2> <consecuente 2-1> <consecuente 2-2> … <consecuente 2-N>) … (<prueba M> <consecuente M-1> <consecuente M-2> … <consecuente M-N>)) Hugo A. Banda Gamboa, PhD, MSc. Junio, 2003 15
  • PROGRAMACIÓN EN LISP SISTEMAS INTELIGENTES Cada cláusula contiene una prueba y cero o más formas adicionales denominadas consecuentes. Se evalúan en secuencia las formas de prueba de cada cláusula hasta que se encuentre una cuyo valor sea diferente de NIL. En este caso se dice que la cláusula correspondiente se activa y se evalúan sus formas consecuentes. El valor que retorna COND, es el de la última forma consecuente de la cláusula activada. Si el valor de todas las formas de prueba es NIL, el valor que retorna COND, también es NIL. Si una cláusula con una forma de prueba diferente de NIL no tiene formas consecuentes, entonces el valor retornado por COND es el valor de la forma de prueba. > (DEFUN sol-ecuación-cuad (a b c) (SETF delta (- (* b b) (* 4 a c))) (COND ((PLUSP delta) (LET ((f1 (- (/ b (* 2 a)))) (f2 (/ (SQRT delta) (* 2 a)))) (LIST 'X1 '= (+ f1 f2) 'X2 '= (- f1 f2)))) ((MINUSP delta) (LIST 'No 'hay 'solución 'real!)) (T (LIST 'X1 '= 'X2 '= (- (/ b (* 2 a))))))) SOL-ECUACIóN-CUAD > (sol-ecuación-cuad 1 -1 20) (NO HAY SOLUCIóN REAL!) > (sol-ecuación-cuad 1 -1 -20) (X1 = 5.0 X2 = -4.0) > (sol-ecuación-cuad 1 -10 25) (X1 = X2 = 5) 6.9 CASE CASE evalúa la forma clave y la compara con todas las claves sin evaluar, usando EQL. Si la clave se encuentra, la cláusula correspondiente se activa y todas las formas consecuentes se evalúan. Su plantilla es la siguiente: (CASE <forma clave> (<clave 1> <consecuente 1-1> <consecuente 1-2> … <consecuente 1-N>) (<clave 2> <consecuente 2-1> <consecuente 2-2> … <consecuente 2-N>) (<clave M> <consecuente M-1> <consecuente M-2> … <consecuente M-N>)) • Si ninguna de las cláusulas se activa, CASE retorna NIL. • Si la clave en la última cláusula es T u OTHERWISE, y ninguna de las otras cláusulas se activa, se activa la última. • Si la clave es una lista en lugar de un átomo, CASE evalúa la forma clave y usa MEMBER para buscarla en la lista de claves sin evaluar. Si la clave se encuentra, la cláusula correspondiente se activa y todas las formas consecuentes se evalúan. > (DEFUN área (figura radio) (CASE figura (círculo (* pi radio radio)) (esfera (* 4 pi radio radio)) (OTHERWISE 'No-es-círculo-o-esfera))) áREA > (área 'círculo 3) 28.2743338823081 > (área 'esfera 4) 201.061929829747 > (área 'triángulo 5) NO-ES-CíRCULO-O-ESFERA 16 Junio, 2003 Hugo A. Banda Gamboa, PhD, MSc.
  • SISTEMAS INTELIGENTES PROGRAMACIÓN EN LISP 7. ABSTRACCIÓN DE PROCEDIMIENTOS Y RECURSIÓN La abstracción de procedimientos es un proceso que ayuda a construir programas grandes y complicados sobre la base de una combinación de funciones o procedimientos más simples. La abstracción de procedimientos ayuda a pensar en un nivel superior, al permitir obviar los detalles de cómo se hacen las cosas en un nivel inferior. Se puede programar de arriba hacia abajo, trabajando primero en los procedimientos de nivel superior, posponiendo los de nivel inferior. La abstracción de procedimientos ayuda a mantener las definiciones breves y comprensibles. Un caso especial e importante de la abstracción de procedimientos es aquel en el cual la abstracción sobre la que se construye un procedimiento es el mismo procedimiento. 7.1 PROCEDIMIENTOS RECURSIVOS Cuando un procedimiento se llama a sí mismo, se dice que hace una llamada recursiva. A las definiciones que describen un procedimiento parcialmente en términos de llamadas recursivas se las denomina definiciones recursivas. Suponiendo que no se tiene la función primitiva MEMBER, definamos como un procedimiento recursivo sencillo la función MBR. Dado un átomo y una lista como argumentos, se tiene la siguiente descripción para la función MBR: 1. Si la lista es nula, el átomo no es un miembro de la lista, retornar NIL. 2. Si el átomo es igual al primer elemento de la lista, entonces el átomo es miembro de la lista, retornar T. 3. Si el átomo no es igual al primer elemento de la lista, entonces el átomo es miembro de la lista si y sólo si es un miembro del resto de la lista. Las dos primeras consideraciones son relativamente sencillas de entender. La tercera es un poco más sutil y puede ser interpretada como una llamada recursiva a MBR, dándole como argumentos el átomo y el resto de la lista original. Esto, traducido a LISP, resulta: > (DEFUN MBR (átomo lista) (COND ((ENDP lista) NIL) ((EQL átomo (FIRST lista)) T) (T (MBR átomo (REST lista))))) MBR > (MBR 'a '(a b c d)) T > (MBR 'b '(a b c d)) T > (MBR 'd '(a b c d)) T > (MBR 'b '()) NIL Hugo A. Banda Gamboa, PhD, MSc. Junio, 2003 17
  • PROGRAMACIÓN EN LISP SISTEMAS INTELIGENTES Utilizando similares técnicas, definamos ahora la función EQLIST que retorna T si las dos listas de átomos dadas como argumentos son iguales. La descripción para esta función es como sigue: Definir EQLIST con argumentos lista1 y lista2: • Si la lista1 es vacía, retornar el resultado de comprobar si lista2 es vacía. • Si la lista2 es vacía, retornar NIL. • Si el primer elemento de lista1 y el primer elemento de lista2 no son iguales, retornar NIL. • Si no, realizar una llamada recursiva a EQLIST dando como arguentos el resto de lista1 y el resto de lista2. > (DEFUN EQLIST (lista1 lista2) (COND ((ENDP lista1) (ENDP lista2)) ((ENDP lista2) NIL) ((NOT (EQL (FIRST lista1) (FIRST lista2))) NIL) (T (EQLIST (REST lista1) (REST lista2))))) EQLIST > (EQLIST () '(A B C)) NIL > (EQLIST '(A B C) ()) NIL > (EQLIST () ()) T > (EQLIST '(A B C) '(A B C)) T > (EQLIST '(A B C) '(B C A)) NIL Ahora definamos una versión de la función llamada ELIMINAR, la misma que acepta dos argumentos: un átomo y una lista. El resultado es una lista en la que la ocurrencia de átomo ha sido eliminada de la lista original dada. La descripción es la siguiente: Definir ELIMINAR con átomo y lista como argumentos: 1. Si la lista es vacía, retornar NIL 2. Si el átomo es igual al primer elemento de la lista, retornar el resto de la lista. 3. Si no, construir una lista con el primer elemento de la lista y lo que retorne la llamada recursiva a ELIMINAR con los argumentos átomo y resto de la lista. > (SETF animales '(oso perro gato toro perro loro oso)) (OSO PERRO GATO TORO PERRO LORO OSO) > (DEFUN ELIMINAR (átomo lista) (COND ((ENDP lista) NIL) ((EQL átomo (FIRST lista)) (REST lista)) ((CONS (FIRST lista) (ELIMINAR átomo (REST lista)))))) ELIMINAR > (ELIMINAR ‘perro animales) (OSO GATO TORO PERRO LORO OSO) > (ELIMINAR 'oso animales) (PERRO GATO TORO PERRO LORO OSO) 18 Junio, 2003 Hugo A. Banda Gamboa, PhD, MSc.
  • SISTEMAS INTELIGENTES PROGRAMACIÓN EN LISP Como se puede ver de los resultados, sólo la primera ocurrencia de átomo es eliminada de la lista. Realizando una modificación al procedimiento anterior, se define la función ELIMINAR-TODO, capaz de eliminar todas la ocurrencias de átomo en la lista: Definir ELIMINAR-TODO con átomo y lista como argumentos: 1. Si la lista es vacía, retornar NIL 2. Si el átomo es igual al primer elemento de la lista, llamar recursivamente a ELIMINAR- TODO dándole como argumentos el átomo y el resto de la lista. 3. Si no, construir una lista con el primer elemento de la lista y lo que retorne la llamada recursiva a ELIMINAR-TODO con los argumentos átomo y resto de la lista. > (DEFUN ELIMINAR-TODO (átomo lista) (COND ((ENDP lista) NIL) ((EQL átomo (FIRST lista)) (ELIMINAR-TODO átomo (REST lista))) ((CONS (FIRST lista) (ELIMINAR-TODO átomo (REST lista)))))) ELIMINAR-TODO > (ELIMINAR-TODO 'perro animales) (OSO GATO TORO LORO OSO) > (ELIMINAR-TODO 'oso animales) (PERRO GATO TORO PERRO LORO) Como se vio anteriormente, la función primitiva REVERSE invierte todos los elementos del nivel superior de una lista dada como argumento. La siguiente función denominada INVERTIR, es una generalización de REVERSE, ya que invierte todos los elementos de una lista, sean estos átomos o listas. > (DEFUN INVERTIR (lista) (COND ((ENDP lista) NIL) ((LISTP (FIRST (LAST lista))) (CONS (INVERTIR (FIRST (LAST lista))) (INVERTIR (BUTLAST lista)))) (T (CONS (FIRST (LAST lista)) (INVERTIR (BUTLAST lista)))))) INVERTIR > (SETF lista1 '(a b c d e f)) (A B C D E F) > (SETF lista2 '((a b) (c d) e (f g))) ((A B) (C D) E (F G)) > (REVERSE lista1) (F E D C B A) > (INVERTIR lista1) (F E D C B A) > (REVERSE lista2) ((F G) E (C D) (A B)) > (INVERTIR lista2) ((G F) E (D C) (B A)) Hugo A. Banda Gamboa, PhD, MSc. Junio, 2003 19
  • PROGRAMACIÓN EN LISP SISTEMAS INTELIGENTES 7.2 PARÁMETROS OPCIONALES Los procedimientos hasta ahora definidos, han requerido de un argumento por cada parámetro. Pero LISP también permite definir procedimientos con parámetros opcionales, indicados con &OPTIONAL, para los cuales puede haber o no argumentos correspondientes. > (DEFUN raíz (x &optional n) (IF n (expt x (/ 1 n)) (sqrt x))) RAíZ > (raíz 9) ; llamada a raíz con un argumento, n se liga a NIL y se usa SQRT. 3.0 > (raíz 27 3) : llamada a raíz con 2 argumentos, n se liga a 3 y se usa EXPT. 3.0 Todos los parámetros opcionales que no tengan argumento correspondiente, se ligan al valor NIL por omisión. Pero también se puede especificar el valor por omisión al que se deben ligar los parámetros opcionales. > (DEFUN raíz (x &optional (n 2)) (expt x (/ 1 n))) RAíZ > (raíz 16) ; llamada a raíz con un argumento, n se liga a 2. 4.0 > (raíz 64 3) ; llamada a raíz con 2 argumentos, n se liga a 3. 4.0 Los parámetros opcionales casi siempre simplifican los programas, al reducir la necesidad de procedimientos auxiliares. > (DEFUN cuenta-elementos (lista &optional (resultado 0)) (IF (ENDP lista) resultado (cuenta-elementos (REST lista) (+ 1 resultado)))) CUENTA-ELEMENTOS > (cuenta-elementos '(La imaginación es más importante que el conocimiento)) 8 La llamada a cuenta-elementos con un sólo argumento hace que el valor inicial de resultado sea cero. Pero el mismo procedimiento es usado con dos argumentos en las llamadas recursivas. En este caso el valor por omisión de resultado se ignora en favor del valor del argumento proporcionado. 7.3 PARÁMETROS RESTANTES Un parámetro restante, indicado por &REST se liga a una lista de todos los valores de los argumentos que de otra manera no tendrían un parámetro correspondiente. > (DEFUN potencia (x &REST exponentes) (POTEXP x exponentes)) POTENCIA > (DEFUN potexp (resultado exponentes) (IF (ENDP exponentes) resultado (potexp (EXPT resultado (FIRST exponentes)) (REST exponentes)))) POTEXP > (potencia 3) 20 Junio, 2003 Hugo A. Banda Gamboa, PhD, MSc.
  • SISTEMAS INTELIGENTES PROGRAMACIÓN EN LISP 3 > (potencia 3 2) 9 > (potencia 3 2 4) 6561 7.4 PARÁMETROS CLAVE Un parámetro clave se usa en situaciones en las que hay varios parámetros, muchos de los cuales casi siempre se ligan a valores por omisión. En tales situaciones si se usaran parámetros opcionales, sería muy difícil recordar el orden de ellos, con su respectivo valor inicial. Cuando se define procedimientos con parámetros clave, estos se indican con &KEY. > (DEFUN rota-lista-der (lista n-puestos) (IF (ZEROP n-puestos) lista (rota-lista-der (APPEND (LAST lista) (BUTLAST lista)) (- n-puestos 1)))) ROTA-LISTA-DER > (DEFUN rota-lista-izq (lista n-puestos) (IF (ZEROP n-puestos) lista (rota-lista-izq (APPEND (REST lista) (LIST (FIRST lista))) (- n-puestos 1)))) ROTA-LISTA-IZQ > (DEFUN rota-lista (lista &KEY dirección (lugares 1)) (IF (EQ dirección 'izquierda) (rota-lista-izq lista lugares) (rota-lista-der lista lugares))) ROTA-LISTA > (rota-lista '(a b c d e) :dirección 'izquierda :lugares 3) (D E A B C) > (rota-lista '(a b c d e) :dirección 'derecha :lugares 3) (C D E A B) En el procedimiento ROTA-LISTA definido anteriormente, dirección y lugares, aparecen como parámetros clave. En la llamada a la función, las palabras clave :dirección y :lugares, indican la presencia de argumentos que deben ser asignados a los parámetros dirección y lugares. Las ligaduras son determinadas por las palabras clave, no por el orden de aparición en la llamada. Esto se demuestra en los siguientes ejemplos. > (rota-lista '(a b c d e) :lugares 4 :dirección 'izquierda) (E A B C D) > (rota-lista '(a b c d e) :lugares 2 :dirección 'derecha) (D E A B C) 7.5 PARÁMETROS AUXILIARES Un parámetro auxiliar, indicado por &AUX, no corresponde a ningún argumento. Los parámetros auxiliares en realidad son formas LET* disfrazadas. > (DEFUN extremos (lista &AUX (primero (FIRST lista)) (último (LAST lista))) (CONS primero último)) EXTREMOS > (extremos '(a b c d e)) (A E) Hugo A. Banda Gamboa, PhD, MSc. Junio, 2003 21
  • PROGRAMACIÓN EN LISP SISTEMAS INTELIGENTES 8. TRANSFORMACIONES Y FUNCIONES ANÓNIMAS Los procedimientos recursivos, permiten transformar y filtrar. Cuando se transforma una lista, la longitud de la lista transformada es la misma que la longitud de la lista original. Cuando se filtra una lista la longitud de la lista de salida es menor, a menos que todos los elementos de la lista original pasen la prueba del filtro. 8.1 MAPCAR Es una primitiva que facilita el procedimiento de transformación de listas. MAPCAR requiere del nombre de un procedimiento de transformación junto con la lista de los elementos que serán transformados. En el siguiente ejemplo MAPCAR se utiliza para comprobar los números que son impares, utilizando la primitiva ODDP. La secuencia #’, tal como se explico anteriormente, produce un procedimiento objeto, a partir de un nombre de procedimiento. Cuando se evalúa una forma MAPCAR, LISP suministra cada elemento de su segundo argumento al procedimiento de transformación especificado por su primer argumento. El valor devuelto es una lista de resultados. > (mapcar #'oddp '(1 2 3)) (T NIL T) El procedimiento usado por MAPCAR no está restringido a ser un procedimiento de un parámetro; si el procedimiento tiene más de un parámetro debe haber un número correspondiente de listas de las cuales extraer argumentos. En el siguiente ejemplo MAPCAR toma un elemento de cada lista de argumentos y los ensambla para un procedimiento de transformación. > (mapcar #'= '(1 2 3) '(3 2 1)) (NIL T NIL) 8.2 REMOVE-IF y REMOVE-IF-NOT Son primitivas que permiten filtrar listas. REMOVE-IF elimina todos los elementos que satisfacen el predicado dado. REMOVE-IF-NOT, en cambio elimina todos los elementos que no lo satisfacen. > (SETF digitos '(0 1 2 3 4 5 6 7 8 9)) (0 1 2 3 4 5 6 7 ...) > (REMOVE-IF #'EVENP digitos) (1 3 5 7 9) > (REMOVE-IF-NOT #'EVENP digitos) (0 2 4 6 8) 8.3 COUNT-IF y FIND-IF COUNT-IF cuenta los elementos de una lista, que satisfacen una determinada prueba. > (COUNT-IF #'EVENP digitos) 5 > (COUNT-IF-NOT #'ZEROP digitos) 9 22 Junio, 2003 Hugo A. Banda Gamboa, PhD, MSc.
  • SISTEMAS INTELIGENTES PROGRAMACIÓN EN LISP FIND-IF encuentra el primer elemento de una lista, que satisface una determinada prueba. > (FIND-IF #'EVENP digitos) 0 > (FIND-IF #'ODDP digitos) 1 8.4 FUNCALL y APPLY Aplica el valor de su primer argumento al valor de los otros argumentos. Usa tantos argumentos como requiera el procedimiento mencionado, más uno para el nombre del procedimiento. FUNCALL permite definir procedimientos que tengan procedimientos como argumentos. > (FUNCALL #'FIRST '(A B C D)) ; Equivale a: (FIRST '(A B C D)) A > (FUNCALL #'+ '(1 2 3 4 5)) (1 2 3 4 5) > (FUNCALL #'+ 1 2 3 4 5) 15 APPLY suele tener dos argumentos. Usa el valor de su primer argumento sobre los elementos del valor de su segundo argumento, el cual debe ser una lista. La lista tiene tantos elementos como requiera el procedimiento mencionado. > (APPLY #'FIRST '((A B C D))) A > (APPLY #'+ '(1 2 3 4 5)) 15 En los casos en que APPLY aparezca con más de dos argumentos, todos los argumentos excepto el primero y el último se combinan en una lista, a la cual se añade el último argumento. > (APPLY #'+ 1 2 3 '(4 5)) ; La lista de argumentos dados a + es 15 ; (APPEND (LIST 1 2 3) '(4 5)) 8.5 FUNCIONES ANÓNIMAS (LAMBDA) Para los casos en que las funciones definidas por el usuario son utilizadas una sola vez, es preferible definir funciones anónimas. Las funciones anónimas son como las otras funciones del LISP, excepto que no tienen asignado un nombre y se las usa una vez. Las funciones anónimas son definidas mediante la expresión LAMBDA. > (SETF colores '(azul verde rojo )) (AZUL VERDE ROJO) > ((LAMBDA (lista) (FIRST (REST lista))) colores) VERDE > (SETF aves '(gallo paloma pavo)) felinos '(león tigre pantera) mascotas '(perro gato loro) (PERRO GATO LORO) > (SETF animales (LIST aves felinos mascotas)) Hugo A. Banda Gamboa, PhD, MSc. Junio, 2003 23 ((GALLO PALOMA PAVO) (LEóN TIGRE PANTERA) (PERRO GATO LORO))
  • PROGRAMACIÓN EN LISP SISTEMAS INTELIGENTES > (MAPCAR #'(LAMBDA (lista) (FIRST (REST lista))) animales) (PALOMA TIGRE GATO) 9. ITERACIÓN Al igual que la recursión, la iteración es una estrategia general para controlar la evolución de los cálculos. La iteración puede realizarse con varias primitivas que proporciona LISP. 9.1 DOTIMES Permite escribir procedimientos sencillos con iteración controlada por un contador. La plantilla general es la siguiente: (DOTIMES (<parámetro de cuenta> <forma límite superior> <forma resultado>) <cuerpo>) Cuando empieza la ejecución de DOTIMES, la forma límite superior se evalúa produciendo un número n. Entonces los números desde 0 hasta n-1 se asignan, uno después de otro, al parámetro de cuenta. Para cada valor, se ejecuta el cuerpo. A la salida, la ligadura del parámetro de cuenta se elimina y la forma resultado se evalúa, produciendo el valor de la forma DOTIMES. Si DOTIMES no tiene forma resultado, devuelve NIL. > (DEFUN FACTORIAL (num) (LET ((resultado 1)) (DOTIMES (cuenta num resultado) (SETF resultado (* (+ 1 cuenta) resultado))))) FACTORIAL > (FACTORIAL 0) 1 > (FACTORIAL 1) 1 > (FACTORIAL 5) 120 9.2 DOLIST La primitiva DOLIST es similar a DOTIMES excepto que los parámetros de una forma lista se asignan al parámetro de cuenta uno después del otro. La plantilla es la siguiente: (DOLIST (<parámetro de cuenta> <forma lista> <forma resultado>) <cuerpo>) Para ilustrar el uso de DOLIST, a continuación se define la función REVERSE-DOLIST: > (DEFUN REVERSE-DOLIST (lista) (LET ((resultado NIL)) (DOLIST (cont lista resultado) (SETF resultado (CONS cont resultado))))) REVERSE-DOLIST > (REVERSE lista1) (F E D C B A) > (REVERSE-DOLIST lista1) (F E D C B A) 24 Junio, 2003 Hugo A. Banda Gamboa, PhD, MSc.
  • SISTEMAS INTELIGENTES PROGRAMACIÓN EN LISP 9.3 DO y DO* La primitiva DO puede utilizarse para hacer iteraciones cuando DOTIMES y DOLIST no son lo suficientemente flexibles. La plantilla de DO es la siguiente: (DO ((<parámetro 1> <valor inicial 1> <forma de actualización 1>) (<parámetro 2> <valor inicial 2> <forma de actualización 2>) … (<parámetro N> <valor inicial N> <forma de actualización N>)) (<prueba para finalizar> <formas intermedias, si existen> <forma resultado>) <cuerpo>) Los detalles de la operación de la primitiva DO, son los siguientes: • La primera parte de una forma DO siempre es una lista de parámetros que se ligarán a valores iniciales, al entrar al DO. Si no hay parámetros la lista vacía debe aparecer en la primera posición. Las especificaciones para parámetros pueden incluir formas de actualización, además de los nombres de variables y formas para valor inicial. Todas la formas valor inicial se evalúan antes de ligarlas a los parámetros. De manera similar, todas las formas de actualización se evalúan antes de hacer las nuevas asignaciones. Por consiguiente se dice que DO maneja sus parámetros en paralelo. En este aspecto DO es similar a LET, lo cual llega a ser crítico cuando un parámetro DO aparece en una forma de inicialización o de actualización. • La segunda parte de un DO establece cuándo termina el ciclo y qué valor se debe devolver. Esta parte consta de una lista cuya forma inicial es una prueba para terminación. Las formas que siguen en esta lista son evaluadas en orden cuando el valor de la forma de prueba es diferente de NIL. El valor devuelto por DO es el valor de la última forma en esta lista. Antes de cada pasada a través del cuerpo, se evalúa la prueba, incluyendo la primera vez. Si sólo hay una forma, ésta es la prueba y DO devuelve NIL. • La tercera parte de una forma DO, el cuerpo, consta de formas que son evaluadas secuencialmente. Todos los valores se ignoran, es decir las evaluaciones sólo se hacen por su posibles efectos secundarios. Siempre que dentro del cuerpo de un DO se encuentre una expresión que inicie con RETURN, el DO termina inmediatamente. El valor retornado es el indicado por la expresión RETURN. La primitiva DO* es similar a DO, excepto que hace la ligadura secuencial de valores, en lugar de hacerlo en paralelo. DO* es a DO, lo que LET es a LET*. > (DEFUN REVERSE-DO (lista) (DO ((list lista (REST list)) (resultado NIL (CONS (FIRST list) resultado))) ((ENDP list) resultado))) REVERSE-DO > (REVERSE lista1) (F E D C B A) > (REVERSE-DO lista1) (F E D C B A) 9.4 LOOP La primitiva LOOP, también se usa para iteración, pero a diferencia de las otras formas, sólo tiene un cuerpo. Las formas del cuerpo son evaluadas una y otra vez. Cuando se encuentra con una forma (RETURN <expresión>), la expresión es evaluada y LOOP termina retornando el valor de la expresión. Hugo A. Banda Gamboa, PhD, MSc. Junio, 2003 25
  • PROGRAMACIÓN EN LISP SISTEMAS INTELIGENTES 9.5 PROG1 y PROGN Las primitivas PROG1 y PROGN se utilizan para combinar explícitamente formas en secuencia. (PROG1 <forma de respuesta> <forma secundaria 1> … <forma secundaria N>) (PROGN <forma secundaria 1> <forma secundaria 2> … <forma de respuesta>) PROG1 establece como valor de respuesta para la forma completa la primera, mientras que PROGN devuelve el resultado de la evaluación de la última. La siguiente función calcula el promedio de una lista de números dada como argumento y utiliza las formas LOOP y PROGN. > (DEFUN promedio (lista) (IF (ENDP lista) 0 (LET* ((total (FIRST lista)) (cuenta 1)) (LOOP (SETF lista (REST lista)) (IF (ENDP lista) (RETURN (/ total cuenta)) (PROGN (SETF total (+ total (FIRST lista))) (SETF cuenta (+ cuenta 1)))))))) PROMEDIO > (promedio '()) 0 > (promedio '(3 5 7 9 11)) 7 10. LECTURA Y ESCRITURA Para establecer la comunicación entre los procedimientos y el usuario, LISP proporciona varias primitivas tanto para proporcionar como para obtener información. 10.1 PRINT, PRIN1, PRINC y TERPRI La primitiva PRINT evalúa su argumento y lo imprime en una nueva línea, seguido por un espacio en blanco. El valor devuelto por PRINT es el valor de su argumento. Ejemplos: > (SETF paciente '((nombre Andrade) (síntomas (fiebre comezón náusea)))) ((NOMBRE ANDRADE) (SíNTOMAS (FIEBRE COMEZóN NáUSEA))) > (PRINT paciente) ((NOMBRE ANDRADE) (SíNTOMAS (FIEBRE COMEZóN NáUSEA))) ; Acción de PRINT ((NOMBRE ANDRADE) (SíNTOMAS (FIEBRE COMEZóN NáUSEA))) ; Valor retornado por PRINT > (DEFUN reporte (paciente) (PROGN (PRINT (LIST 'Paciente (SECOND (ASSOC 'nombre paciente)) 'presenta (LENGTH (SECOND (ASSOC 'síntomas paciente))) 'síntomas (SECOND (ASSOC 'síntomas paciente)))) NIL)) REPORTE > (reporte paciente) (PACIENTE ANDRADE PRESENTA 3 SíNTOMAS (FIEBRE COMEZóN NáUSEA)) ; Acción de PRINT NIL ; Valor retornado por PROGN PRINT puede imprimir también cadenas, otro de los tipos de datos de LISP. Las cadenas son secuencias de caracteres limitadas por comillas al inicio y al final. 26 Junio, 2003 Hugo A. Banda Gamboa, PhD, MSc.
  • SISTEMAS INTELIGENTES PROGRAMACIÓN EN LISP > (PRINT "Hola que tal!") "Hola que tal!" ; Acción de PRINT "Hola que tal!" ; Valor retornado por PRINT Hay otras primitivas derivadas de PRINT que están disponibles para asistir en las labores de presentación de mensajes al usuario: • PRIN1 es igual a PRINT, excepto que no imprime en una línea nueva, ni pone un espacio al final de la línea. • PRINC imprime la cadena dada como argumento sin incluír las comillas, ni retorno ni espacio en blanco al final. • TERPRI no requiere de argumentos y su efecto es forzar un cambio de línea. El siguiente ejemplo ilustra el efecto de estas primitivas: > (DEFUN media4 (uno dos tres cuatro) (PRINT "El promedio de:") (TERPRI) (PRIN1 uno) (PRINC " ") (PRIN1 dos) (PRINC " ") (PRIN1 tres) (PRINC " ") (PRIN1 cuatro) (TERPRI) (PRINC "Da como resultado:") (/ (+ uno dos tres cuatro) 4)) MEDIA4 > (MEDIA4 3 5 7 9) "El promedio de:" 3 5 7 9 Da como resultado: 6 10.2 READ LISP se detiene cuando encuentra la primitiva READ, espera que se digite en el teclado una expresión. READ no imprime ningún mensaje, por lo que necesario utilizar una forma PRINT, para poner un mensaje al usuario indicando el tipo de respuesta que se espera. > (PROGN (SETF nombre NIL) (PRINT "Ingresar un nombre:") (SETF nombre (READ)) (PRINT (APPEND '(El nombre es) (LIST nombre))) NIL) "Ingresar un nombre:" Marcelo (EL NOMBRE ES MARCELO) NIL 10.3 FORMAT La primitiva FORMAT permite imprimir mensajes más elegantes, nítidos y similares a oraciones; conteniendo letras mayúsculas, minúsculas, y signos de puntuación. > (FORMAT T "Hola que tal!")Hola que tal! ;Impresión de FORMAT NIL ;El valor de FORMAT es NIL Hugo A. Banda Gamboa, PhD, MSc. Junio, 2003 27
  • PROGRAMACIÓN EN LISP SISTEMAS INTELIGENTES La letra T indica a FORMAT que imprima en el terminal. Para imprimir la cadena de caracteres en una nueva línea, se utiliza la directiva %. El signo ~ introduce una directiva. La directiva & también le dice a FORMAT que empiece en una nueva línea, pero no lo hace si ya hay una nueva línea. > (PROGN (FORMAT T "~%Hola que tal!~%") (FORMAT T "~%Esta línea se escribió luego de 1 línea en blanco. ~%") (FORMAT T "~&Pero esta línea está precedida por la directiva &.")) Hola que tal! Esta línea se escribió luego de 1 línea en blanco. Pero esta línea está precedida por la directiva &. NIL La directiva A, indica a FORMAT que debe insertar el valor del argumento adicional que aparece después de la cadena de caracteres de FORMAT. > (LET ((nombre NIL)) (FORMAT T "~%Ingresar un nombre: ") (SETF nombre (READ)) (FORMAT T "~%El nombre ingresado fue: ~A." nombre)) Ingresar un nombre: Roberto El nombre ingresado fue: ROBERTO. NIL 10.4 WITH-OPEN-FILE Esta primitiva crea flujos, liga variables a ellos y se conecta a archivos. Conceptualmente, los flujos son objetos LISP que sirven como fuentes o suministros de datos. Los flujos pueden conectarse a archivos en uno de sus extremos. Los flujos conectados a archivos que proporcionan datos se llaman flujos de entrada. Los flujos de entrada están involucrados con las formas READ. Los flujos conectados a archivos que reciben datos se llaman flujos de salida. Estos flujos están involucrados con las formas PRINT y FORMAT. La plantilla para WITH-OPEN-FILE permite la especificación de tres cosas: el nombre de la variable que se va a ligar al flujo, el nombre del archivo al cual se va a conectar el flujo; y la indicación si el flujo es de entrada o de salida. (WITH-OPEN-FILE (<nombre del flujo> <especificación del archivo> :direction :input) … (READ <nombre del flujo> ‘eof) ; leer datos del flujo de entrada, hasta el final del archivo (eof) …) (WITH-OPEN-FILE (<nombre del flujo> <especificación del archivo> :direction :output) … (PRINT <expresión para imprimir> <nombre del flujo>) …) 28 Junio, 2003 Hugo A. Banda Gamboa, PhD, MSc.
  • SISTEMAS INTELIGENTES PROGRAMACIÓN EN LISP 10.5 ELT Es un acrónimo de ELemenTo. Se desempeña tanto con listas, como con cadenas. Recibe dos argumentos: • Si el primer argumento es una lista, devuelve el elemento especificado por su segundo argumento. • Si el primer argumento es una cadena, devuelve el carácter especificado por su segundo argumento. El primer elemento se especifica con el número cero (0). > (ELT '(a b c) 0) A > (ELT '(a b c) 2) C > (ELT "abc" 0) #a > (ELT "abc" 2) #c Las primitivas LENGTH y REVERSE, también pueden operar con cadenas lo mismo que con listas. > (LENGTH "Politécnica Nacional") 20 > (REVERSE "Politécnica Nacional") "lanoicaN acincétiloP" 10.6 STRING= y STRING-EQUAL Estas primitivas se utilizan para determinar si dos cadenas son iguales. STRING= detecta la diferencia entre mayúsculas y minúsculas, STRING-EQUAL no. > (STRING= "abc" "xyz") NIL > (STRING= "abc" "abc") T > (STRING= "abc" "ABC") NIL > (STRING-EQUAL "abc" "xyz") NIL > (STRING-EQUAL "abc" "abc") T > (STRING-EQUAL "abc" "ABC") T 10.7 CHAR= y CHAR-EQUAL Estas primitivas permiten determinar si dos caracteres son iguales. CHAR= detecta la diferencia entre mayúsculas y minúsculas; CHAR-EQUAL, no. > (CHAR= #a #b) NIL > (CHAR= #a #a) Hugo A. Banda Gamboa, PhD, MSc. Junio, 2003 29
  • PROGRAMACIÓN EN LISP SISTEMAS INTELIGENTES T > (CHAR= #a #A) NIL > (CHAR-EQUAL #a #b) NIL > (CHAR-EQUAL #a #a) T > (CHAR-EQUAL #a #A) T 10.8 SEARCH Se utiliza para determinar si una cadena está contenida en otra. Si el primer argumento de SEARCH está contenido en el segundo, el resultado es la posición donde empieza la correspondencia. De otra forma SEARCH devuelve NIL. > (SEARCH "razones" "Corazones no entienden razones") 2 > (SEARCH "tienden" "Corazones no entienden razones") 15 > (SEARCH "corazones" "Corazones no entienden razones") NIL > (SEARCH "corazones" "Corazones no entienden razones" :TEST #'CHAR-EQUAL) 0 Al igual que LENGTH, REVERSE y ELT, SEARCH es también una primitiva que opera tanto con listas como con cadenas. > (SEARCH '(razones) '(Corazones no entienden razones)) 3 > (SEARCH '(tienden) '(Corazones no entienden razones)) NIL > (SEARCH '(corazones) '(Corazones no entienden razones)) 0 10.9 READ-CHAR Lee un sólo carácter, ya sea del terminal o de un archivo. > (READ-CHAR)H #H > (READ-CHAR)m #m 10.10 READ-LINE Lee una cadena de caracteres hasta cuando aparece el carácter de cambio de línea (<ENTER>) o el carácter de final de archivo (EOF). Retorna dos valores: La línea de caracteres leídos y el valor T. > (READ-LINE)Mientras leía esta línea de pronto me encontré con el final "Mientras leía esta línea de pronto me encontré con el final" T 30 Junio, 2003 Hugo A. Banda Gamboa, PhD, MSc.
  • SISTEMAS INTELIGENTES PROGRAMACIÓN EN LISP A continuación se definen dos procedimientos, para ilustrar el uso de las formas de entrada y salida: LEER-TEXTO y BUSCAR-TEXTO. Para probar estas funciones, se supone que en el directorio de trabajo del LISP, existe un archivo de texto llamado “buscatxt.txt”. > (DEFUN leer-texto (archivo) (WITH-OPEN-FILE (datos archivo :direction :input) (DO ((línea (READ-LINE datos NIL) (READ-LINE datos NIL))) ((NOT línea) T) (FORMAT T "~% ~A" línea)))) LEER-TEXTO > (leer-texto "buscatxt.txt") BUSCAR-TEXTO es un procedimiento que busca a través de tal archivo una línea que contenga una subcadena en particular, después de lo cual imprime la línea completa. WITH-OPEN-FILE construye el flujo apropiado y READ-LINE lee el archivo línea por línea. SEARCH prueba cada línea hasta encontrar una que corresponda con el texto dado, hecho lo cual, FORMAT imprime la línea. T > (DEFUN buscar-texto (texto archivo) (WITH-OPEN-FILE (datos archivo :direction :input) (DO ((línea (READ-LINE datos NIL) (READ-LINE datos NIL))) ((NOT línea) (FORMAT T "~%No hay tal texto en: ~A" archivo)) (WHEN (SEARCH texto línea :TEST #'CHAR-EQUAL) (FORMAT T "~% ~A" línea) (RETURN T))))) BUSCAR-TEXTO > (BUSCAR-TEXTO "WITH-OPEN-FILE" "buscatxt.txt") línea completa. WITH-OPEN-FILE construye el flujo apropiado y READ-LINE lee T 11. BIBLIOGRAFÍA [1] Bahrami A. Designing Artificial Intelligence Based Software. Sigma Press, Wilmslow, UK, 1988. [2] Franz Inc. Allegro CL for Windows Volume 1: Getting Started, Ver 3.0. Franz Inc., USA, 1995. [3] Graham P. ANSI Common Lisp. Prentice Hall, USA, 1996. [4] Hekmatpour S. Introduction to LISP and Symbol Manipulation. Prentice Hall, UK, 1988. [5] Winstanley G. Program Design for Knowledge Based Systems. Sigma Press, Wilmslow, UK, 1987. [6] Winston P H, Horn B K P. LISP, 3ra. Edición. Addison-Wesley Iberoamericana, USA, 1991. Hugo A. Banda Gamboa, PhD, MSc. Junio, 2003 31