How can I modify function bindings in Common Lisp?

随声附和 提交于 2019-11-28 14:11:31

Because Common Lisp has a different namespace for functions, you need to use symbol-function or 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>

A typical use case for redefining functions in such a way is during debugging. Your example indicates that. Common Lisp already provides higher-level machinery for that: TRACE. Under the hood it sets the function value of the symbol, but it provides a more convenient user interface. Often something like TRACE will have many, non-standard, options.

Tracing functions

The following example uses Clozure Common Lisp, which is always compiling code:

? (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

Then to untrace:

? (untrace sum)

Advising functions

In your example you have printed the arguments on entering the function. In many Common Lisp implementations there is another mechanism to augment functions with added functionality: advising. Again using Clozure Common Lisp and its advise macro:

? (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
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!