200 likes | 377 Views
LISP II. Contenido de la sesión Organización de memoria y Operaciones destructivas Funciones anónimas. Variables. Alcances. A-listas. Pattern matching . Organización de la memoria (1). Celdas CONS Formadas por 2 punteros: CAR y CDR
E N D
LISP II • Contenido de la sesión • Organización de memoria y Operaciones destructivas • Funciones anónimas. Variables. Alcances. • A-listas. Pattern matching.
Organización de la memoria (1) • Celdas CONS • Formadas por 2 punteros: CAR y CDR • La función CONS crea una celda CONS a partir de memoria libre > (CONS arg1 arg2) arg1: inicializa la celda CAR arg2: inicializa la celda CDR > (CONS ‘x ‘caza) (x . caza) La notación par-punto es la utilizada por LISP para indicar los elementos apuntados por el CAR y el CDR de una celda CONS. Cuando CDR apunta a NIL, LISP simplifica esta notación no mostrando dicho elemento apuntado. • MODIFICACIÓN DE CELDAS CONS • Con SETF • arg1: dirección de puntero en memoria • arg2: la nueva dirección que deseamos que contenga arg1 > (SETF *c* ‘( x . caza)) (x . caza) > (SETF (CAR *c*) ‘y) (y . caza) > (SETF (CDR *c*) ‘pesca) (y . pesca) > *c* (y . pesca)
x y x y car pesca caza cdr caza caza Organización de la memoria (2) Celda CONS: (CONS ‘x ‘caza) *c* (SETF *c* ‘(x . caza)) *c* (SETF (CAR *c*) ‘y) *c* (SETF (CDR *c*) ‘pesca)
Organización de la memoria (3) • Disgregación de celdas CONS • Asignando NIL al CDR de la/s celda/s que deseamos disgregar • Creando nuevas celdas CONS con SETF >(SETF *d* ‘(A B C)) (A B C) >(SETF *d2* (CDR *d*)) (B C) >*d* (A B C) >(SETF (CDR *d*) nil) nil >(SETF *d3* (CDR *d2*)) (C) >(SETF (CDR *d2*) nil) nil • cada vez que el CDR de una celda se apunta a NIL, el resto de celdas CONS que estaban apuntadas por dicha celda quedan ocupando memoria y no se pueden reutilizar. • Tratar de optimizar las asignación de memoria evitando el uso abusivo de variables globales • Recuperar la memoria usada e inservible (garbage collection)
Organización de la memoria (4) • Garbage Collection • Analizamos el siguiente ejemplo: >(SETF *e* ‘(dato1 dato2 dato3)) (dato1 dato2 dato3) >(SETF *e* ‘(dato4 dato5 dato6)) (dato4 dato5 dato6) >*e* (dato4 dato5 dato6) • La memoria utilizada en la primera asignación de SETF (una serie de datos) queda ligada a la lista inicial y no es accesible • Se denomina basura (garbage) a los objetos usados que ocupan memoria y no pueden volver a ser utilizados. En el caso anterior, son basura: (dato1 dato2 dato3)
e Old-e e dogo galgo mastín labrador dogo galgo mastín labrador Operaciones destructivas • Consideremos el siguiente ejemplo >(SETF e ‘(dogo galgo mastín labrador)) (dogo galgo mastín labrador) >(SETF old-e e) (dogo galgo mastín labrador) >(SETF e (NREVERSE old-e)) (labrador mastín galgo dogo) >e (labrador mastín galgo dogo) >old-e (dogo) • NREVERSE destruye la lista old-e, reasignando punteros con el fin de no generar basura. Al concluir, ambas listas comparten la última celda
NREVERSE redefinido (defun our-nreverse (n) (our-nreverse-1 nil n)) (defun our-nreverse-1 (head tail) (let ((residual (cdr tail))) (our-nreverse-2 (setf (cdr tail) head) residual tail))) (defun our-nreverse-2 (drop residual tail) (if (null residual) tail (our-nreverse-1 tail residual)
Funciones anónimas • Orígenes • Deriva de la notación usada por Whitehead y Russell de los Principia Mathematica • Aplicada por vez primera por Alonzo Church en 1941 en su definición del cálculo lambda del que deriva LISP. • ^x(x + x) (evolución de x-circunflejo) • Λx(x + x) (evolución de ^ a Λ) • λx(x + x) (evolución de Λ a λ) • (lambda (x) (+ x x)) (McCarthy 1958) • Utilización • Mediante la notación #’ • (funcall #’(lambda (x) (+ x x)))
Ejemplos de aplicación • Problema: “Dada la lista (1 2 3), obtener sus cuadrados” • Solución 1 > (defun n-square (n) (expt n 2)) > (defun square-iterate (list) (if (null list) nil (cons (n-square (car list)) (square-iterate (cdr list))))) > (square-iterate ‘(1 2 3)) (1 4 9) • Solución 2 > (mapcar #’(lambda (x) (expt x 2)) ‘(1 2 3)) (1 4 9) • Solución 3 > (mapcar #’n-square ‘(1 2 3)) (1 4 9)
Evaluación • Mediante • Eval: en desuso >(eval ‘(+ 1 2 3 4)) 10 • Funcall: se aplica sobre una lista de argumentos simples >(funcall #‘+ 1 2 3 4) 10 • Apply: se aplica sobre una lista de uno o más argumentos el último de los cuales debe ser una lista >(apply #’+ 1 2 ‘(3 4)) 10 >(apply #’+ ‘(1 2 3 4)) 10
z es lexicamente invisible Alcance léxico (lexical scope) • Este término hace referencia al conjunto de reglas necesarias para determinar, sin ambigüedad, la ligadura asociada a las variables utilizadas en un fragmento de código. • En él, un símbolo hace referencia a la variable que posee dicho nombre en el contexto en el que dicho símbolo aparece >(let ((x 10)) (defun foo () x)) >(let ((x 20)) (foo)) 10 • Cada vez que se efectúa una ligadura entre variables se determina un cierre léxico para la misma. Dicha variable es visible dentro del cierre léxico que le corresponda. > (defun main (z) (ejemplo-de-alcance z 3)) > (defun ejemplo-de-alcance (x y) (append (let ((x (car x))) (list x y)) x z))
Alcance dinámico (dynamic scope) > (let ((x 10)) (defun foo () (declare (special x)) x)) • Para que una variable posea alcance dinámico debemos declararla como special en todos los contextos en los que aparezca • En este ejemplo la x de la función no hace referencia a la variable léxica definida, lo hará a cualquier variable x que sea declarada como especial en el momento de invocar la función • > (let ((x 20)) (declare (special x)) (foo)) 20 • Por tanto, en el alcance dinámico buscamos la variable en el entorno en que fue definida la función. • Utilidad: para dar a una variable global un valor temporal diferente. > (let ((*print-base* 16)) (print 32)) 20 32
Tipos de variables • A la vista de los alcances léxico y dinámico comentados anteriormente diferenciamos los siguientes tipos de variables: • Locales • Su alcance es el del contexto en que se definen • implícitamente poseen alcance léxico • Globales • Se definen con setf (usar defparameter en archivos de código fuente) • Son visibles en cualquier lugar • Poseen alcance dinámico, implícitamente son special • Por convenio se denotan entre asteriscos • Libres • Se denomina a aquellas variables que se definen fuera de la función que las referencia (x en el ejemplo) > (setf fn (let ((i 3)) #’(lambda (x) (+ x i)))) > (funcall fn 2) 5 • Se pueden declarar (declare para local, declaim para global), tipos (type) de variables con propósitos de eficiencia de compilación (Graham 13.3).
A-listas. Pattern-matching. • Mediante EQUAL comparamos datos para ver si tienen la misma estructura >(EQUAL ‘(* pi (expt r 2) h) ‘(* pi (expt r 2) h)) T • EQUAL no puede comparar expresiones que tengan partes constantes y partes variables. Supongamos, por ejemplo, la función match: >(match ‘(a b c) ‘(a b ?v)) ((?v c)) >(match ‘(a c c) ‘(a b ?v)) nil • Observamos que se crea una ligadura variable-valor entre los términos que coincidan • Para conseguir este efecto utilizaremos las listas de asociación (a-listas) que sirven para comparar expresiones (patrones). • A-listas • Son listas formadas por listas anidadas • Cada sublista posee • Una llave (el CAR) • Un dato (el CDR) • ((?padre Juan) (?hijo José)) • Para explorar una a-lista utilizamos ASSOC. > (assoc :padre ((:padre Juan) (:hijo José))) (:padre Juan) > (assoc :abuelo ((:padre Juan) (:hijo José))) Nil
MOD NP N BV ADV DET DET 2 3 4 6 1 5 DET ADJ 7 MOD 8 ADJ N CNJ 9 CNJ Ejemplo de uso de assoc en NLP, (1) • La siguiente FSTN (Finite State Transition Network) permite reconocer oraciones en inglés como “A man is a consumer and often very very stupid” • NP: kim, sandy, lee • DET: a, the, her • N: consumer, man, woman • BV: is, was • CNJ: and, or • ADJ: happy, stupid • MOD: very • ADV: often, always, • Sometimes • #: “jump”
Ejemplo de uso de assoc en NLP, (2) • Definimos la asignación siguiente como representación de la red anterior (setf english ‘((:Initial (1)) (:Final (9)) (From 1 to 3 by NP) (From 1 to 2 by DET) (From 2 to 3 by N) (From 3 to 4 by BV) (From 4 to 5 by ADV) (From 4 to 5 by |#|) (From 5 to 6 by DET) (From 5 to 7 by DET) (From 5 to 8 by |#|) (From 6 to 6 by MOD) (From 6 to 7 by ADJ) (From 7 to 9 by N) (From 8 to 8 by MOD) (From 8 to 9 by ADJ) (From 9 to 4 by CNJ) (From 9 to 1 by CNJ)))
Ejemplo de uso de assoc en NLP, (3) • Las funciones siguientes nos permiten acceder a los elementos descriptores de la red (inicio, fin y transiciones) (defun initial-nodes (network) (nth 1 (assoc :Initial network))) (defun initial-nodes (network) (nth 1 (assoc :Final network))) (defun transitions (network) (cddr network)) • Las transiciones tienen la estructura de la lista • (From <node> to <newnode> by <label>) Ejercicio propuesto: Dado un nodo, obtener la lista de sus destinos <newnode> y etiquetas <label>. Ejemplo: > (nodo 1 red) ((3 NP) (2 DET))
Ejemplo de uso de assoc Match I, (1) • Podemos, por ejemplo, usar ASSOC para construir una función que haga comparación de patrones. (defun match (pattern data bindings) ;; caso base: pattern es un átomo (cond ((atom pattern) (if (variablep pattern) (psble-bind pattern data bindings) (if (equal pattern data) bindings ‘fallo))) ;; caso base: pattern no es un átomo y data sí ((atom data) ‘fallo) ;; caso recursivo (t (let ((car-bind (match (car pattern) (car data) bindings))) (if (eq car-bind ‘fallo) ‘fallo (match (cdr pattern) (cdr data) car-bind))))))
Ejemplo de uso de assoc Match II, (2) (defun psble-bind (var data bindings) (let ((ligaduras (assoc var bindings))) (if (null ligaduras) ;; si la variable no está ligada ;; añadimos una ligadura nueva (cons (list var data) bindings) ;; la variable ligada y el dato deben coincidir ;; caso contrario: fallo (if (equal (valor-bind ligaduras) data) bindings ‘fallo)))) (defun valor-bind (lista-variable-valor) ;; función de acceso al valor de los elementos ;; de la lista (second lista-variable-valor)) (defun variablep (v) ;; comprueba si pattern es una variable (and (symbolp v) (char= (char (symbol-name v) 0) #\?)))
Aplicación de match: reescritura de programas • Supongamos que deseamos convertir determinado código Lisp. Disponemos de las siguientes reglas de reescritura: lhs ->rhs • (1+ ?x) -> (+ 1 ?x) • (list ?x) -> (cons ?x nil) • (second ?x) -> (cadr ?x) • (cadr ?x) -> (car (cdr ?x)), etc. • lhs representa una expresión Lisp y rhs representa una expresión equivalente. • La expresión • (second *registros*) • hará match con lhs de regla 3 y se reescribirá como • (cadr *registros*) • que hará match con lhs de regla 4 obteniendo • (car (cdr *registros*)) • Con (SUBST new old list) podemos aplicar las reglas de sustitución necesarias cuando se verifica match. • (setf form ‘(car (cdr (?x))) (car (cdr ?x)) • (subst ‘(padre Juan Carlos) ‘?x form) (car (cdr (padre Juan Carlos)))