通用Lisp:在一个作用域内重新定义现有的函数?

11

在Common Lisp中,是否可以在特定的范围内重新定义已定义的函数?例如,给定调用函数B的函数A。我能否在调用A时临时重新定义B?

我正在寻找类似于let块的东西,但可以重新定义函数。

4个回答

12

在给定的词法范围内,是可以的。使用FLET或LABELS。使用FLET定义的任何函数将无法调用在同一词法范围中定义的函数,如果您需要这样做(比如一组互相递归的函数),则需要使用LABELS。

请注意,FLET和LABELS都只建立词法阴影,不应用于隐藏COMMON-LISP包中的函数,并且不会动态地更改从表单建立的词法范围之外调用的函数。


所以,由于它是词法作用域,它只会影响flet/labels的实际主体,而不是整个函数调用层次结构,可以这么说吗? - Erik Öjebo
确切地说,它只会在词法作用域内“存在”,对其他函数没有任何影响。据我所知,没有任何动态绑定符号到函数映射的方法。 - Vatine

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))))))

使用方法:

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> 

请注意,它依赖于Doug Hoyte的defmacro! 宏,在Let Over Lambda中可用。
另外,正如所写的那样,它是一种指代性的(fun-orig在函数体内可用)。如果您希望它完全卫生,请将fun-orig更改为,g!fun-orig。
在编写单元测试时,我经常重新定义函数。在特定单元测试范围内模拟函数是有帮助的,有时需要使用动态(而不是词法)作用域来完成这项工作。

7

2
您可以像这样模拟动态绑定,例如:
(defmacro setvfun (symbol function)
      `(progn
         (setf ,symbol ,function)
         (setf (symbol-function ',symbol) (lambda (&rest args) (apply (symbol-value ',symbol) args)))))

那么,例如,使用

(setvfun some-fun (lambda() (format t "initial-definition~%")))
(defun test-the-fun (&rest args) (apply #'some-fun args))

(defun test ()
   (test-the-fun)
   (flet ((some-fun () (format t "Lexically REDEFINED (if you see this, something is very wrong)~%")))
      (test-the-fun))
   (let ((some-fun (lambda (x) (format t "Dynamically REDEFINED with args: ~a~%" x))))
       (declare (special some-fun))
       (test-the-fun "Hello"))
   (test-the-fun))

你将获得:

REPL> (test)
==>initial-definition
==>initial-definition
==>Dynamically REDEFINED with args: Hello
==>initial-definition

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接