0.2.1.Emacs
Vamos a crear un fichero de texto utilizando el editor Emacs. Para
invocar al editor teclea lemacs. Si todo ha ido bien estás
dentro del editor emacs. Puedes teclear el texto que desees. Existen además
una serie de comandos básicos que permiten moverse por el texto,
hacer búsquedas, buscar una determinada línea, etc.
Para dar un comando al editor (por ejemplo, guardar
el fichero con los últimos cambios que hayamos realizado) es necesario
hacerlo por teclado. Para distinguirlos del texto que tecleamos normalmente
para que se añada a nuestro fichero de texto, los comandos se dan
al editor pulsando a la vez una tecla especial y otra normal de texto.
Estas teclas especiales son la tecla de Control (en vuestro teclado Ctrl
o Ctl) y la tecla Meta (etiquetada Meta, Alt o Edit). Cuando representemos
una de estas pulsaciones combinadas de teclas, en vez de escribir CONTROL
o META cada vez que queramos prefijar un carácter lo denotaremos
con Ctrl- <chr> o Alt-<chr>:
Línea previa Ctrl-P
Retrocede carácter, Ctrl-B Posición
del cursor Avanza carácter, Ctrl-F
Línea siguiente, Ctrl-N
Una vez que sepas moverte serás capaz de situarte en los sitios donde existan errores. Puedes borrar el carácter anterior al cursor con la tecla RUBOUT o DELETE.
A continuación debes guardar el programa en un fichero. Los comandos para el manejo de ficheros constan de Ctrl-X seguido por otro carácter. En este momento tienes tu programa en el buffer del emacs (en memoria, no guardado en el disco), es decir, si salieras de Emacs sin guardar los cambios perderías el trabajo realizado. (Tranquilo, antes de salir del editor, emacs te preguntará si quieres que se guarden los cambios). Teclea Ctrl-X Ctrl-S cuando quieras guardar el buffer en un fichero. Aparecerá en la parte inferior de la ventana un mensaje como File to save in:~ /practica1/; a continuación teclea el nombre del fichero File to save in:~/practica1/prueba1.lisp y pulsa RETURN).
Si el buffer ya tiene asignado un fichero cada vez que teclees Ctrl-X Ctrl-S guardará los cambios en ese fichero. Si quieres guardar el buffer en otro fichero teclea Ctrl-X Ctrl-W, de esta forma te preguntará siempre en que fichero quieres guardar el buffer.
Una vez que tengas el texto correctamente escrito y guardado en un fichero teclea Ctrl-X Ctrl-C para salir del editor. Comprueba que el fichero prueba1.lisp se encuentra en el directorio mediante la orden ls que listará los nombres de tus ficheros y subdirectorios.
0.2.1.1.Otras ordenes útiles
Búsquedas
Alt-E Avanza una sentencia Alt-A Retrocede una sentencia
Ctrl-A Mueve al principio de línea Ctrl-E Mueve al final de la línea
Alt-< Va al principio del fichero Alt-> Va al
final del fichero
Ctrl-K Borra desde la posición del cursor hasta el final de línea
Alt-K Borra desde el final de la sentencia actual
Cuando borras algo mayor que un carácter Emacs
lo guarda para tí. La forma de recuperarlo es mediante Ctrl-Y. Esto
es muy útil para desplazar un conjunto de líneas. Borra las
líneas que quieras desplazar repitiendo Ctrl-K. Sitúa el
cursor donde quieres pegar todo lo borrado y teclea Ctrl-Y. Además
puedes deshacer tu última acción tecleando Ctrl-X u (undo
o deshacer).
Ctrl-X b Buffer Selecciona o crea un buffer llamado buffer. (Cambio de buffer)
Ctrl-X Ctrl-B Muestra la lista de buffers
Ctrl-X Ctrl-K Elimina un buffer
Además puede dividirse la ventana en varias ventanas con un buffer activo cada una.
Ctrl-X 2 Divide la ventana en dos
Ctrl-X 1 Solo una ventana
Ctrl-X o Selecciona otra ventana
Una vez iniciado el interprete, ACL escribe un mensaje y entra en el bucle"promt-read-eval-print". El prompt por defecto será:
Nombre del paquete
|
v
USER(1):
^
|
Linea de instruccion
Cuando se comete un error se entra en el bucle "break", que da facilidades de depuración:
Nombre del paquete
|
v
[2] USER(1):
^ ^
/
|
Nivel de "break" |
Linea de instruccion
Se puede obtener AYUDA sobre las instrucciones
que se pueden introducir en el nivel "break" con :help.
Para volver al nivel superior teclea :pop.
USER(4): (Hola)
Error: attempt to call `HOLA' which is an undefined function.
[condition type: UNDEFINED-FUNCTION]
Restart actions (select using :continue):
0: Try calling HOLA again.
1: Return a value instead of calling HOLA.
2: Try calling a function other than HOLA.
3: Setf the symbol-function of HOLA and call it again.
[1] USER(5): :pop
USER(6):
Las instrucciones más importantes para depurar un programa lisp son las que permiten visualizar la pila. La pila es la entidad donde los argumentos de las funciones lisp son evaluados. Cuando una función es llamada, la función que llama evalúa y coloca en la cima de la pila los argumentos de la función llamada. Una ventana de la pila es el área de la pila donde residen los argumentos de una función invocada. Si por ejemplo, potencia llama a producto, que a su vez llama a suma, habra por lo menos tres ventanas activas cuando se invoque a suma. Cuando se entra en el nivel "break", la primera ventana de la pila que no está asociada con el mecanismo de manejo del error suele ser el más interesante.
Las siguientes funciones son las más interesantes para inspeccionar la pila, y así poder comprobar los argumentos que han recibido las funciones:
ERROR <-
+ <-
SUMA <- EVAL <-
TPL:TOP-LEVEL-READ-EVAL-PRINT-LOOP <-
TPL:START-INTERACTIVE-TOP-LEVEL
(ERROR TYPE-ERROR
:DATUM ...)
->(+ A 2)
(SUMA 1 2)
(EVAL (SUMA 1 2))
(TPL:TOP-LEVEL-READ-EVAL-PRINT-LOOP)
(TPL:START-INTERACTIVE-TOP-LEVEL
#<BIDIRECTIONAL-TERMINAL-STREAM @ #x40126e39>
#<Function TOP-LEVEL-READ-EVAL-PRINT-LOOP> ...)
+ <-
SUMA <- EVAL
<- TPL:TOP-LEVEL-READ-EVAL-PRINT-LOOP <-
TPL:START-INTERACTIVE-TOP-LEVEL
USER(1): (load "/iaic1/salidas/practicas/pract1/practica1.lsp")
; Loading /iaic1/salidas/practicas/pract1/practica1.lsp
T
Una vez evaluadas las funciones de un fichero podrás
utilizarlas. También puedes editar el código de una función
evaluada mediante la función
(ed '<nombre_de_la_función>).
Una vez depurados tus programas puedes compilar los
ficheros de forma que se evite el proceso de interpretación cada
vez que se invoca una función. La instrucción compile-file
compila un fichero, y la función load hace que las funciones
compiladas en el fichero estén disponibles. Las funciones compiladas
se ejecutan mucho más rápido, pero en caso de error perderás
las posibilidades de depuración que ofrece el entorno si las funciones
son interpretadas.
Para salir del entorno teclea :exit. Sal de Lisp antes de salir del editor emacs. ¡SI NO LO HACES ASI, EL PROCESO DE LISP NO TERMINA!
1.1.Objetivo
Por otra parte, veremos la utilidad de utilizar funciones recursivas y funciones de alto nivel en Common Lisp. Así comprobarás sus facilidades para hacer ``mapping", transformar, filtrar, contar o encontrar elementos particulares dentro de estructuras de datos como listas y estructuras. Common Lisp cuenta con un amplio repertorio de funciones, por lo que es importante adquirir el hábito de comprobar si el lenguaje ofrece una función antes de proceder a su implementación. Utiliza la ayuda en línea (clman).
Al finalizar la práctica deberás saber
editar, ejecutar y compilar un programa Common Lisp. Además, como
objetivo secundario se deben conocer las herramientas de depuración
LISP. Busca información de las funciones TRACE,
STEP, BREAK,
DESCRIBE, DOCUMENTATION,
TIME. (En el capítulo
10 del Wiston (LISP) se explican brevemente las primitivas de depuración).
(dribble ``juego.trz'')
(play)
(dribble)
El computador dibuja un tablero para jugar a cuatro en raya y espera un numero del 0 al 6 indicando tu movimiento. Cuando realices un movimiento aparecerá una X en el último cuadro sin ocupar de la columna que indicaste. A continuación el computador hará un movimiento y esperará tu siguiente movimiento. Gana el que antes haga 4 en raya (horizontal, vertical o diagonalmente).
Puedes repetir el juego, esta vez sin registrar la
traza y evaluando los ficheros compilados. Los ficheros compilados tienen
la extensión .fasl.
Si evalúas los ficheros compilados comprobarás que la ejecución
es mucho más rápida.
La base de datos contiene información acerca del título, autor, clasificación por palabras claves y año de edición de cada libro. En la versión propuesta la estructura de datos elegida para cada libro consiste en una lista con tres listas internas cuyos car respectivos son las palabras clave titulo, autor, clasificacion y agno. Para abstraer al usuario de la estructura de datos utilizada se ha definido el constructor (los parámetros identifican los valores de cada campo):
(crea-libro titulo autor clasificacion agno)
(lista-libros-de-ficcion libros)
(cuenta-libros-ficcion libros)
(apellidos-de-autores libros)
Tarea
(listado-palabras-clave libros)
Esta función debe recibir como parámetro una base de datos de libros y debe devolver el listado de todas las palabras clave (sin repeticiones de palabras).
La función debe recibir como primer parámetro una base de datos de libros y como segundo argumento una función que tome como argumentos dos elementos de la lista y devuelva T si y sólo si el primer argumento es estrictamente menor que el segundo en algún sentido. Por ejemplo la función por-antiguedad devuelve T si el año de edición del primer argumento es menor que el del segundo argumento.
Ordena los libros por antigüedad, y a continuación ordena la lista resultante colocando al principio los que tengan un mayor numero de palabras claves en la lista clasificacion.
Prepara un fichero con las tareas pedidas en la parte C y la traza registrada para comprobar el funcionamiento correcto de todas las funciones que has definido. Deberás entregar un único archivo P1NXXIII.lsp en el directorio /users2/IAIC1/entradas/P1.
El fichero debe estar preparado para ser evaluado
sin dar ningún error. Para ello las partes correspondientes a trazas
se colocarán entre los símbolos
#| y |#.
De esta forma se indica que el código comprendido entre estos símbolos
no será evaluada al leer los ficheros.
; La base de datos contiene informacion acerca del
titulo, autor y
;clasificacion por palabras claves de cada libro.
; En la version propuesta la estructura de datos
elegida para cada libro
;consiste en una lista con tres listas internas
cuyos 'car' respectivos
;son las palabras clave 'titulo', 'autor' y 'clasificacion'.
;Para abstraer al usuario de la estructura de datos
utilizada se ha
;definido el constructor (los parametros identifican
los valores de
;cada campo):'(crea-libro titulo autor clasificacion)'.
(defun crea-libro (titulo autor clasificacion agno)
(LIST (LIST 'TITULO TITULO)
(LIST 'AUTOR AUTOR)
(LIST 'CLASIFICACION CLASIFICACION)
(LIST 'AGNO AGNO)))
;Este constructor crea un nuevo libro
(setf libro-ejemplo
(crea-libro '(Common
Lisp)
'(Guy steele)
'(Tecnico Lisp)
'1991))
; Para seguir con el constuctor creamos un conjunto de lectores:
(defun libro-titulo (libro)
(second (assoc 'titulo libro)))
(defun libro-autor (libro)
(second (assoc 'autor libro)))
(defun libro-clasificacion (libro)
(second (assoc 'clasificacion libro)))
(defun libro-agno (libro)
(second (assoc 'agno libro)))
;Creamos ahora un procedimiento para cambiar el autor
(defun libro-cambio-autor (libro autor)
(if (eql 'autor (first (first libro)))
(cons (list 'autor autor) (rest
libro))
(cons (first libro)
(libro-cambio-autor (rest libro) autor))))
;Ahora construimos mas libros en la base de datos:
(setf bdlibros
(list
(crea-libro
'(Common Lisp)
'(Guy L. Steele)
'(Tecnico Lisp)
'1991)
(crea-libro
'(Artificial Intelligence)
'(P. H. Winston)
'(Tecnico IA)
'1989)
(crea-libro
'(Moby Dick)
'(H. Melville)
'(Ficcion)
'1850)
(crea-libro
'(Tom Sawyer)
'(M. Twain)
'(Ficcion)
'1894)
(crea-libro
'(The Black Orchid)
'(Rex Stout)
'(Ficcion Misterio)
'1710)))
;FILTRADO DE DATOS
; Puede ser interesante sacar listados que contengan
informacion parcial de
;la base de datos. Esto es, se requiere un filtrado.
(defun lista-autores (libros)
(if (null libros)
nil
(cons (libro-autor (first libros))
(lista-autores (rest libros)))))
; Se puede hacer un filtro que acceda a mayor profundidad:
; Primero creamos una funcion booleana que diga si
un libro cumple la
;condicion, p.e. que sea de ficcion.
(defun ficcionp (libro)
(member 'ficcion (libro-clasificacion libro)))
; Ahora la podemos usar para encontrar todos los libros de ficcion:
(defun lista-libros-de-ficcion (libros)
(cond ((null libros) nil)
((ficcionp
(first libros))
(cons (first libros)
(lista-libros-de-ficcion (rest libros))))
(t (lista-libros-de-ficcion
(rest libros)))))
;;; TAMBIEN ES POSIBLE CONTAR Y ENCONTRAR
;(length (LISTA-LIBROS-DE-FICCION bdlibros))
; Tambien se puede acceder a uno cualquiera de la
lista:
; (first (LISTA-LIBROS-DE-FICCION bdlibros))
;Pero esto es muy poco eficiente porque se necesita
crear nuevas listas.
;Pero puede contarse directamente:
(defun cuenta-libros-ficcion (libros)
(cond ((null libros) 0)
((ficcionp
(first libros))
(+ 1 (cuenta-libros-ficcion (rest libros))))
(t (cuenta-libros-ficcion
(rest libros)))))
(defun encuentra-primer-libro-ficcion (libros)
(cond ((null libros) nil)
((ficcionp
(first libros))
(first libros))
(t (encuentra-primer-libro-ficcion
(rest libros)))))
;Observar los patrones que se repiten en las funciones
anteriores.
;;; Funciones de MAPPING
;Para facilitar las cosas
;(lista-autores bdlibros)
;(mapcar #'libro-autor bdlibros)
(defun libro-autor-apellido (libro)
(first (last (libro-autor libro))))
;(mapcar #'libro-autor-apellido bdlibros)
;(mapcar #'(lambda (libro) (first (last (libro-autor
libro)))) bdlibros)
;;; Para simplificar las operaciones de filtrado:
REMOVE-IF y REMOVE-IF-NOT
;(remove-if-not #'ficcionp bdlibros) ;elimina
si NO se verifica la condicion
;(remove-if #'ficcionp bdlibros) ;elimina
SI se verifica la condicion
;(count-if #'ficcionp bdlibros) ;cuenta
SI se verifica la condicion
;(find-if #'ficcionp bdlibros) ;encuentra
SI se verifica la condicion