Имя функции и динамическое связывание в Common Lisp
Я читаю " Парадигмы искусственного интеллекта" Питера Норвига. В главе 6.2 автор использует код, подобный приведенному ниже (не исходный код, я выбрал тревожную часть):
Фрагмент кода:
(progv '(op arg) '(1+ 1)
(eval '(op arg)))
Как и предполагалось автором, этот код должен возвращать 2, но в sbcl 1.1.1
переводчик явно не смотрит оп в среду, выбрасывая op: undefined function
,
Это конкретная реализация? Так как код, должно быть, был проверен на каком-то другом lisp.
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))))