variables - programacion - ¿Cómo puedo modificar enlaces de funciones en Common Lisp?
manejo de listas en scheme (2)
Aquí hay algo que puedes hacer en Scheme:
> (define (sum lst acc)
(if (null? lst)
acc
(sum (cdr lst) (+ acc (car lst)))))
> (define sum-original sum)
> (define (sum-debug lst acc)
(print lst)
(print acc)
(sum-original lst acc))
> (sum ''(1 2 3) 0)
6
> (set! sum sum-debug)
> (sum ''(1 2 3) 0)
(1 2 3)
0
(2 3)
1
(3)
3
()
6
6
> (set! sum sum-original)
> (sum ''(1 2 3) 0)
6
Si tuviera que hacer lo siguiente en Common Lisp:
> (defun sum (lst acc)
(if lst
(sum (cdr lst) (+ acc (car lst)))
acc))
SUM
> (defvar sum-original #''sum)
SUM-ORIGINAL
> (defun sum-debug (lst acc)
(print lst)
(print acc)
(funcall sum-original lst acc))
SUM-DEBUG
> (sum ''(1 2 3) 0)
6
Ahora, ¿cómo puedo hacer algo como (setf sum #''sum-debug)
que cambiaría el enlace de una función definida con defun
?
Debido a que Common Lisp tiene un espacio de nombre diferente para las funciones, necesita usar symbol-function
o fdefinition
.
CL-USER> (defun foo (a)
(+ 2 a))
FOO
CL-USER> (defun debug-foo (a)
(format t " DEBUGGING FOO: ~a" a)
(+ 2 a))
DEBUG-FOO
CL-USER> (defun debug-foo-again (a)
(format t " DEBUGGING ANOTHER FOO: ~a" a)
(+ 2 a))
DEBUG-FOO-AGAIN
CL-USER> (foo 4)
6
CL-USER> (setf (symbol-function ''foo) #''debug-foo)
#<FUNCTION DEBUG-FOO>
CL-USER> (foo 4)
DEBUGGING FOO: 4
6
CL-USER> (setf (fdefinition ''foo) #''debug-foo-again)
#<FUNCTION DEBUG-FOO-AGAIN>
CL-USER> (foo 4)
DEBUGGING ANOTHER FOO: 4
6
CL-USER>
Un caso de uso típico para redefinir funciones de esa manera es durante la depuración. Tu ejemplo indica eso. Common Lisp ya proporciona maquinaria de alto nivel para eso: TRACE
. Debajo del capó establece el valor de función del símbolo, pero proporciona una interfaz de usuario más conveniente. A menudo, algo como TRACE
tendrá muchas opciones no estándar.
Funciones de seguimiento
El siguiente ejemplo usa Clozure Common Lisp , que siempre está compilando código:
? (defun sum (list accumulator)
(declare (optimize debug)) ; note the debug declaration
(if (endp list)
accumulator
(sum (rest list) (+ accumulator (first list)))))
SUM
? (sum ''(1 2 3 4) 0)
10
? (trace sum)
NIL
? (sum ''(1 2 3 4) 0)
0> Calling (SUM (1 2 3 4) 0)
1> Calling (SUM (2 3 4) 1)
2> Calling (SUM (3 4) 3)
3> Calling (SUM (4) 6)
4> Calling (SUM NIL 10)
<4 SUM returned 10
<3 SUM returned 10
<2 SUM returned 10
<1 SUM returned 10
<0 SUM returned 10
10
Entonces para descubrir:
? (untrace sum)
Funciones de asesoramiento
En su ejemplo, ha impreso los argumentos al ingresar la función. En muchas implementaciones de Common Lisp hay otro mecanismo para aumentar funciones con funcionalidad adicional: asesoramiento . De nuevo usando Clozure Common Lisp y su macro advise
:
? (advise sum ; the name of the function
(format t "~%Arglist: ~a" arglist) ; the code
:when :before) ; where to add the code
#<Compiled-function (CCL::ADVISED ''SUM) (Non-Global) #x302000D7AC6F>
? (sum ''(1 2 3 4) 0)
Arglist: ((1 2 3 4) 0)
Arglist: ((2 3 4) 1)
Arglist: ((3 4) 3)
Arglist: ((4) 6)
Arglist: (NIL 10)
10