常见的lisp:重新定义范围内的现有函数?

Eri*_*ebo 11 common-lisp

在Common Lisp中,是否可以在特定范围内重新定义已定义的函数?例如,给定一个调用函数B的函数A.我可以在调用A时暂时重新定义B吗?

我正在寻找一个let块的东西,但这可以重新定义函数.

Vat*_*ine 12

在给定的词汇范围内,是的.使用FLET或LABELS.使用FLET定义的任何函数都无法调用在相同词法范围内定义的函数,如果您需要(例如,对一组相互递归函数进行自递归),则需要使用LABELS.

请注意,FLET和LABELS都只建立词法阴影,不应该用于遮蔽COMMON-LISP包中的函数,也不会动态更改从表单建立的词法范围外调用的函数.


Rai*_*wig 7

可以使用FLET和LABELS引入本地函数.


Cla*_*ley 7

如果你想使用动态范围重新定义/遮蔽现有函数,这是我已经使用了一段时间的宏.

(defmacro! with-shadow ((fname fun) &body body)
  "Shadow the function named fname with fun
   Any call to fname within body will use fun, instead of the default function for fname.
   This macro is intentionally unhygienic:
   fun-orig is the anaphor, and can be used in body to access the shadowed function"
  `(let ((fun-orig))
     (cond ((fboundp ',fname)
            (setf fun-orig (symbol-function ',fname))
            (setf (symbol-function ',fname) ,fun)
            (unwind-protect (progn ,@body)
              (setf (symbol-function ',fname) fun-orig)))
           (t
            (setf (symbol-function ',fname) ,fun)
            (unwind-protect (progn ,@body)
              (fmakunbound ',fname))))))
Run Code Online (Sandbox Code Playgroud)

用法:

Clozure Common Lisp Version 1.9-r15759  (DarwinX8664)  Port: 4005  Pid: 4728
; SWANK 2012-03-06
CL-USER>  
(defun print-using-another-fname (x)
  (print x))
PRINT-USING-ANOTHER-FNAME

CL-USER> 
(let ((*warn-if-redefine-kernel* nil))
  (with-shadow (print (lambda (x)
                        (funcall fun-orig (+ x 5))))
    (print-using-another-fname 10)))

15 
15
CL-USER>                
(print 10)

10 
10
CL-USER> 
Run Code Online (Sandbox Code Playgroud)

请注意,它依赖于Doug Hoyte的defmacro!宏,可在Let Over Lambda中找到.

另外,正如所写,它是照应的(有趣的原始体内可用).如果你想要它完全卫生,只需将fun-orig改为,g!fun-orig's.

我经常在编写单元测试时重新定义函数.在特定单元测试范围内的模拟函数是有帮助的,有时需要使用动态(非词法)范围来完成.