如何修改 Common Lisp 中的函数绑定?
How can I modify function bindings in Common Lisp?
以下是您可以在 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
如果我在 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
现在我怎样才能做类似 (setf sum #'sum-debug)
的事情来改变用 defun
定义的函数的绑定?
因为 Common Lisp 有不同的函数命名空间,所以您需要使用 symbol-function
或 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>
以这种方式重新定义函数的典型用例是在调试期间。你的例子表明了这一点。 Common Lisp 已经为此提供了更高级的机制:TRACE
。它在底层设置了交易品种的函数值,但它提供了更方便的用户界面。通常像 TRACE
这样的东西会有很多非标准的选项。
跟踪函数
下面的例子使用了Clozure Common Lisp,一直在编译代码:
? (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
然后去追踪:
? (untrace sum)
咨询功能
在您的示例中,您已经在输入函数时打印了参数。在许多 Common Lisp 实现中,还有另一种机制可以通过添加功能来扩充函数:advising。再次使用 Clozure Common Lisp 及其 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
以下是您可以在 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
如果我在 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
现在我怎样才能做类似 (setf sum #'sum-debug)
的事情来改变用 defun
定义的函数的绑定?
因为 Common Lisp 有不同的函数命名空间,所以您需要使用 symbol-function
或 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>
以这种方式重新定义函数的典型用例是在调试期间。你的例子表明了这一点。 Common Lisp 已经为此提供了更高级的机制:TRACE
。它在底层设置了交易品种的函数值,但它提供了更方便的用户界面。通常像 TRACE
这样的东西会有很多非标准的选项。
跟踪函数
下面的例子使用了Clozure Common Lisp,一直在编译代码:
? (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
然后去追踪:
? (untrace sum)
咨询功能
在您的示例中,您已经在输入函数时打印了参数。在许多 Common Lisp 实现中,还有另一种机制可以通过添加功能来扩充函数:advising。再次使用 Clozure Common Lisp 及其 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