2015-03-02 55 views
2

這裏是你可以在計劃做:如何修改Common Lisp中的函數綁定?

> (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定義的函數?

+1

雖然問題可能不會(或可能)是重複的,你可以給你的答案質疑的一些其他問題,如[如何存儲找到答案函數在Lisp中的變量中並使用它](http://stackoverflow.com/q/19375270/1281433),[LISP動態定義函數](http://stackoverflow.com/q/22975732/1281433)和[爲什麼defun不一樣(setq )?](http://stackoverflow.com/q/11212717/1281433) – 2015-03-02 17:53:57

回答

6

由於Common Lisp具有不同的函數名稱空間,因此需要使用symbol-functionfdefinition

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> 
+1

[**(setf fdefinition)**](http://www.lispworks.com /documentation/HyperSpec/Body/f_fdefin.htm)可以比**(setf fdefinition)**更靈活一些,因爲它可以使用任意函數名稱,而不僅僅是符號。 – 2015-03-02 17:52:00

+0

@JoshuaTaylor:還有什麼可以是一個符號以外的函數名? – Matt 2015-03-02 18:41:20

+4

@Matt表單**(setf something)**的列表。你可以,例如**(defun(setf kar)(value kons)...)** – 2015-03-02 18:52:47

5

以這種方式重新定義函數的典型用例是在調試過程中。你的例子表明這一點。 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:

? (untrace sum) 

提供諮詢功能

在您的示例中,您輸入了該函數的參數。在許多Common Lisp實現中,還有另一種機制來增加具有附加功能的功能:建議。再次使用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 
+0

哇,非常有用的提示,謝謝!方面的問題:如果'endp'和'not'相同(或者重新排序if'表達式),那麼它有什麼意義? – Matt 2015-03-02 20:10:38

+3

@Matt:'ENDP'表示參數是一個列表。 '(endp somelist)'vs'(null anything)'。當讀取一個代碼更好一點時,也可能發出一個錯誤,當'ENDP'被調用列表以外的東西。 – 2015-03-02 20:15:36