Модифицирующая функция; сохранение в новую функцию в lisp - PullRequest
3 голосов
/ 25 июня 2011

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

Пример игрушки в emacs lisp (возможно, не самый идеальный, поскольку это lisp-2):

У меня есть функция, foo, которая мне предоставлена:

(defun foo (x y)
    (+ x y)))

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

(defun newfoo (x y)
  (if (condition-met-p x) 
      (setq x (transform x)))
    (+ x y))

Пожалуйста, не обращайте внимания на то, что я мог бы использовать defadvice в этом конкретном примере, так как меня больше интересует общая задача модификации функций, где defadvice может не применяться. Я считаю, что могу изменить тело с помощью этой формы:

(setq conditional-transformation 
      '(if (condition-met x) (setq x (transform x))))

(setq newbody (append conditional-transformation 
              (nth 2 (symbol-function 'foo)))))

Мои вопросы конкретно о том, как

  1. создать копию от foo до newfoo и заменить тело значением newbody определено выше. (Я заглянул в fset, setf и function но, возможно, не используя их правильно.)
  2. возможно, обернуть это в функцию называется makenewfoo() или что-то так, чтобы я мог ссылаться makenewfoo(foo) и позвольте этому создать newfoo().

И, в более общем смысле,

  1. что-то вроде этого обычно сделано или есть более идиоматический способ изменить функции?
  2. это очень простой случай, но есть более общий способ, чем указание номера элемента списка nth для модификации. За Например, фактическая функция более сложный, так есть ли способ рекурсивно искать вниз по этому дерево s-выражения и тест для конкретный синтаксис и вставить это conditional-transformation выражение до или после него (возможно, используя equal), так что это менее чувствительны к изменениям, внесенным в оригинальная функция?

1 Ответ

3 голосов
/ 26 июня 2011

Это работает в Emacs Lisp:

elisp> (defun foo (x y)
         (+ x y))
foo
elisp> (fset 'newfoo
             (append (lambda (x y)
                       (when (< x 2)
                         (setq x (* x 2))))
                     (cddr (symbol-function 'foo))))
(lambda
  (x y)
  (when
      (< x 2)
    (setq x
          (* x 2)))
  (+ x y))

elisp> (newfoo 1 3)
5
elisp> (newfoo 3 3)
6

Но я действительно не думаю, что это обычно делается или идиоматично.Вы должны использовать defadvice, если хотите изменить поведение функций.

Что касается CL: некоторые реализации предоставляют схожие функции / макросы (например, в CCL: ccl:advise), и вы можетеукажите :before, :after и :around методы для универсальных функций.


Пример кода для вставки выражений:

(defun find-node (elt tree)
  (cond ((null tree) nil)
        ((equal (car tree) elt) tree)
        ((consp (car tree)) (let ((node (find-node elt (car tree))))
                              (if node node (find-node elt (cdr tree)))))
        (t (find-node elt (cdr tree)))))

(defun insert-before (node elt)
  (setcdr node (cons (car node) (cdr node)))
  (setcar node elt))

(let* ((function (copy-tree (symbol-function 'foo)))
       (node (find-node '(+ x y) function)))
  (when node
    (insert-before node '(if (< x 2) (setq x (* x 2))))
    (fset 'newfoo function)))
...