在Common Lisp中,是否可以在特定的范围内重新定义已定义的函数?例如,给定调用函数B的函数A。我能否在调用A时临时重新定义B?
我正在寻找类似于let块的东西,但可以重新定义函数。
在Common Lisp中,是否可以在特定的范围内重新定义已定义的函数?例如,给定调用函数B的函数A。我能否在调用A时临时重新定义B?
我正在寻找类似于let块的东西,但可以重新定义函数。
在给定的词法范围内,是可以的。使用FLET或LABELS。使用FLET定义的任何函数将无法调用在同一词法范围中定义的函数,如果您需要这样做(比如一组互相递归的函数),则需要使用LABELS。
请注意,FLET和LABELS都只建立词法阴影,不应用于隐藏COMMON-LISP包中的函数,并且不会动态地更改从表单建立的词法范围之外调用的函数。
(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>
(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