Common lisp: переопределить существующую функцию в области видимости?

Можно ли в Common Lisp переопределить уже определенную функцию в определенной области? Например, если задана функция A, которая вызывает функцию B. Могу ли я временно переопределить B во время вызова A?

Я ищу что-то вроде блока let, но это может переопределить функции.


person Erik Öjebo    schedule 19.06.2010    source источник


Ответы (4)


В пределах данного лексического объема, да. Используйте FLET или LABELS. Любая функция, определенная с помощью FLET, не сможет вызывать функции, определенные в той же лексической области видимости, если вы этого хотите (например, для саморекурсивной группы взаимно рекурсивных функций), вам нужно будет использовать LABELS.

Обратите внимание, что и FLET, и LABELS устанавливают только лексическое затенение, не должны использоваться для затенения функций из пакета COMMON-LISP и не будут динамически изменять то, какая функция вызывается из-за пределов лексической области действия, устанавливаемой формой.

person Vatine    schedule 21.06.2010
comment
Итак, поскольку он имеет лексическую область действия, он будет влиять только на фактическое тело флета / метки, а не на всю иерархию вызовов функций, так сказать? - person Erik Öjebo; 23.06.2010
comment
Именно так, он будет существовать только в пределах лексической области видимости, не делая слепой разницы с другими функциями. Насколько мне известно, не существует никакого способа динамического связывания сопоставлений символов и функций. - person Vatine; 02.07.2010

Локальные функции можно ввести с помощью FLET и LABELS.

person Rainer Joswig    schedule 19.06.2010

Если вы хотите переопределить/затенить существующую функцию, используя динамическую область видимости, это макрос, который я использую некоторое время.

(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 Дуга Хойта! макрос, доступный в Let Over Lambda.

Кроме того, как написано, это анафорично (веселое начало доступно внутри тела). Если вы хотите, чтобы это было полностью гигиенично, просто измените fun-orig на ,g!fun-orig.

Я чаще всего переопределяю функции при написании модульных тестов. Имитация функций в рамках конкретного модульного теста полезна, и иногда это необходимо делать с динамической (не лексической) областью.

person Clayton Stanley    schedule 27.01.2012

Вы можете имитировать динамическое связывание для таких забав:

(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
person Paralife    schedule 16.09.2011