Имя функции и динамическое связывание в Common Lisp

Я читаю " Парадигмы искусственного интеллекта" Питера Норвига. В главе 6.2 автор использует код, подобный приведенному ниже (не исходный код, я выбрал тревожную часть):

Фрагмент кода:

(progv '(op arg) '(1+ 1)
(eval '(op arg)))

Как и предполагалось автором, этот код должен возвращать 2, но в sbcl 1.1.1переводчик явно не смотрит оп в среду, выбрасывая op: undefined function,

Это конкретная реализация? Так как код, должно быть, был проверен на каком-то другом lisp.

PS Оригинальный код

4 ответа

Решение

Вы, наверное, имеете в виду

(progv '(op arg) '(1+ 1)
  (eval '(funcall op arg)))

Edit (2013-08-21):

PAIP был написан в эпоху до ANSI-Common-Lisp, поэтому возможно, что код содержит несколько несоответствий по стандарту. Мы можем заставить примеры работать со следующей ревизией:

(defun match-if (pattern input bindings)
  "Test an arbitrary expression involving variables.
  The pattern looks like ((?if code) . rest)."
  (and (eval (reduce (lambda (code binding)
                       (destructuring-bind (var . val) binding
                         (subst val var code)))
                     bindings :initial-value (second (first pattern))))
       (pat-match (rest pattern) input bindings)))

;; CL-USER> (pat-match '(?x ?op ?y is ?z (?if (eql (?op ?x ?y) ?z))) '(3 + 4 is 7))
;; ((?Z . 7) (?Y . 4) (?OP . +) (?X . 3) (T . T))
;; CL-USER> (pat-match '(?x ?op ?y (?if (?op ?x ?y))) '(3 > 4))
;; NIL

Элементы в первых позициях рассматриваются не как значения, а как функции, и в пространстве имен функций отсутствует понятие динамического связывания.

Я бы сказал после быстрого взгляда, что оригинальный код был разработан для оценки в контексте, как

 (progv '(x y) '(12 34)
   (eval '(> (+ x y) 99)))

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

Остальные ответы до сих пор верны в том смысле, что фактическая оцениваемая форма не связана с переменными progv (просто (op arg)), но никто не упомянул, что оценивается. Фактически, комментарии в коде, на который вы ссылаетесь, дают (очень) краткое объяснение (это единственный код в этом файле, который использует progv):

(defun match-if (pattern input bindings)
  "Test an arbitrary expression involving variables.
  The pattern looks like ((?if code) . rest)."
  ;; *** fix, rjf 10/1/92 (used to eval binding values)
  (and (progv (mapcar #'car bindings)
              (mapcar #'cdr bindings)
          (eval (second (first pattern))))
       (pat-match (rest pattern) input bindings)))

Идея состоит в том, что вызов match-if называется как

(match-if '((?if code) . rest) input ((v1 val1) (v2 val2) ...))

а также eval называется с (second (first pattern)), значение которого code, Тем не мение, eval называется в пределах progv это связывает v1, v2 и т. д., к соответствующему val1, val2 и т. д., так что если любая из этих переменных будет свободна в code тогда они связаны, когда code оценивается.

проблема

Проблема, которую я вижу здесь, состоит в том, что по коду мы не можем сказать, должно ли значение быть сохранено как переменная symbol-value или же symbol-function, Таким образом, когда вы кладете + в качестве значения некоторой соответствующей переменной, скажем vто он всегда будет сохранен как symbol-value из varне это symbol-function,
Поэтому, когда вы попытаетесь использовать его как, скажем, (v 1 2) это не сработает. Потому что нет функции с именем v в пространстве имен функций(см. это).

Так что делать?

Вероятным решением может быть явная проверка значения, которое должно быть связано с переменной. Если значение является функцией, то оно должно быть связано со значением функции переменной. Эта проверка может быть сделана через fboundp,

Итак, мы можем сделать макрос functioner и модифицированная версия match-if, functioner проверяет, является ли значение функцией, и удачно ее устанавливает. match-if выполняет динамические локальные привязки и разрешает другой код в области видимости связанных переменных.

(defmacro functioner (var val)
  `(if (and (symbolp ',val)
            (fboundp ',val))
       (setf (symbol-function ',var) #',val)
       (setf ,var ,val)))


(defun match-if (pattern input bindings)
  (eval `(and (let ,(mapcar #'(lambda (x) (list (car x))) bindings)
                (declare (special ,@ (mapcar #'car bindings)))
                (loop for i in ',bindings
                      do (eval `(functioner ,(first i) ,(rest i))))
                (eval (second (first ',pattern))))
              (pat-match (rest ',pattern) ',input ',bindings))))
Другие вопросы по тегам