Common lisp: Redefine an existing function within

2019-04-21 03:25发布

In Common Lisp, is it possible to redefine an already defined function within a certain scope? For example, given a function A that calls a function B. Can I temporarily redefine B during a call to A?

I'm looking for something along the lines of a let block, but that can redefine functions.

标签: common-lisp
4条回答
我欲成王,谁敢阻挡
2楼-- · 2019-04-21 03:40

If you want to redefine/shadow an existing function using dynamic scope, this is a macro I've been using for a while.

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

Usage:

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> 

Note that it relies on Doug Hoyte's defmacro! macro, available in Let Over Lambda.

Also as written, it's anaphoric (fun-orig is available within the body). If you want it completely hygienic, just change the fun-orig's to ,g!fun-orig's.

I most often redefine functions when writing unit tests. Mocking functions within the scope of a particular unit test is helpful, and sometimes that needs to be done with dynamic (not lexical) scope.

查看更多
戒情不戒烟
3楼-- · 2019-04-21 03:45

You can simulate dynamic-binding for funs like this:

(defmacro setvfun (symbol function)
      `(progn
         (setf ,symbol ,function)
         (setf (symbol-function ',symbol) (lambda (&rest args) (apply (symbol-value ',symbol) args)))))

and then ,for example, with

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

you get:

REPL> (test)
==>initial-definition
==>initial-definition
==>Dynamically REDEFINED with args: Hello
==>initial-definition
查看更多
贪生不怕死
4楼-- · 2019-04-21 03:56

Within a given lexical scope, yes. Use FLET or LABELS. Any function defined with FLET will be unable to call functions defined in the same lexical scope, if you want that (for, say, self-recursive of a group of mutually recursive functions), you will need to use LABELS.

Note that both FLET and LABELS only establish lexical shadowing, should not be used to shadow functions from the COMMON-LISP package and will not dynamically change what function is called from outside the lexical scope the form establishes.

查看更多
混吃等死
5楼-- · 2019-04-21 04:02

Local functions can be introduced with FLET and LABELS.

查看更多
登录 后发表回答