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

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

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

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

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

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

p.s Исходный код


person Jiaji    schedule 20.08.2013    source источник


Ответы (4)


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

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

Изменить (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
person huaiyuan    schedule 20.08.2013
comment
Думаю, я имею в виду другое, посмотрите, пожалуйста, на Norvig исходный текст. - person Jiaji; 21.08.2013
comment
Следующее работает без изменения кода PAIP: (pat-match '(? X? Op? Y is? Z (? If (eql (funcall? Op? X? Y)? Z)))' (3 + 4 is 7)) - person ahsankhan; 19.02.2014
comment
@ahsankhan - согласен, намного проще. - person QuesterZen; 10.04.2018
comment
Альтернативой было бы написать новую версию eval для сопоставителя шаблонов, которая либо проверяет привязки, которые являются функциями, либо обнаруживает переменные привязки в позиции автомобиля оцениваемой пары cons и добавляет funcall к выражению - в любом случае немного беспорядочно. Использование 'subst', как предлагает huaiyuan, - хорошее быстрое решение, но оно потенциально меняет значение оценки, хотя, похоже, это не имеет значения для любого из вариантов использования в книге. Лично, переходя от Scheme к Common Lisp, я считаю, что пространство имен функций раздражает. - person QuesterZen; 10.04.2018
comment
@Jiaji, код Norvig не работает в моей версии SBCL, потому что (eval '(op arg)) не является правильным кодом в ANSI-Common Lisp, когда op привязан к функции, но, возможно, был в порядке в любой версии Common Lisp он использовал (возможно, у него была более умная реализация progv?). (progv '(op arg)' (1+ 1) (eval '(funcall op arg))) необходимо. Проблема заключается в том, как заставить устройство сопоставления с образцом делать правильные вещи. - person QuesterZen; 10.04.2018

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

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

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

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

person 6502    schedule 20.08.2013
comment
Спасибо за ответ, но автор, похоже, использовал его по-моему в его книге? - person Jiaji; 21.08.2013

Другие ответы на данный момент верны, поскольку фактическая форма оценки - это не переменные, связанные с 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.

person Joshua Taylor    schedule 20.08.2013

Проблема

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

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

Вероятным решением может быть явная проверка значения, которое должно быть привязано к переменной. Если значение является функцией, то оно должно быть привязано к значению функции переменной. Эту проверку можно выполнить через 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))))
person Mooncrater    schedule 22.06.2017