Создание лямбды из s-выражения - PullRequest
2 голосов
/ 19 октября 2008

У меня есть s-выражение, связанное с переменной в Common Lisp:

(defvar x '(+ a 2))

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

(let ((a 4))
  (lambda () (eval x)))

и

(let ((a 4))
  (eval `(lambda () ,x)))

Но оба они создают проблему: EVAL оценит код на верхнем уровне, поэтому я не могу перехватить переменные, содержащиеся в выражении. Обратите внимание, что я не могу поместить форму LET в EVAL. Есть ли какое-нибудь решение?

РЕДАКТИРОВАТЬ: Итак, если нет решения проблемы EVAL, как еще это можно сделать?

РЕДАКТИРОВАТЬ: Возник вопрос о том, что именно я пытаюсь сделать. Я пишу компилятор. Я хочу принять s-выражение с переменными, закрытыми в лексической среде, где выражение определено. Возможно, лучше написать макрос.

Ответы [ 5 ]

5 голосов
/ 05 марта 2009

Вам необходимо создать код, который имеет необходимые привязки. Оберните LET вокруг вашего кода и свяжите каждую переменную, которую вы хотите сделать доступной в вашем коде:

(defvar *x* '(+ a 2))

(let ((a 4))
  (eval `(let ((a ,a))
           ,*x*)))
3 голосов
/ 19 октября 2008

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

(ext:eval-env x (ext:the-environment))

См. http://clisp.cons.org/impnotes.html#eval-environ.

2 голосов
/ 20 октября 2008

Какую проблему вы хотите решить? Скорее всего, вы пытаетесь решить это неправильно. Лексические привязки предназначены для вещей, которые лексически появляются в их пределах, а не для случайных вещей, которые вы получаете извне.

Может быть, вы хотите динамическое закрытие? Такого нет в Common Lisp, хотя в некоторых диалектах Lisp (как я понимаю, например, в Pico Lisp).

Обратите внимание, что вы можете сделать следующее, что аналогично:

(defvar *a*)
(defvar *x* '(+ *a* 2))  ;'

(let ((a 10))
  ;; ...
  (let ((*a* a))
    (eval *x*)))

Я советую вам подумать над тем, действительно ли вы этого хотите.

0 голосов
/ 05 марта 2009

Можно использовать COMPILE, чтобы скомпилировать выражение в функцию, а затем использовать PROGV для FUNCALL скомпилированной функции в среде, где переменные задаются динамически. Или, лучше, используйте COMPILE, чтобы скомпилировать выражение в функцию, которая принимает переменные.

Компиляция принимает определение функции в виде списка и превращает его в функцию. В случае SBCL эта функция компилируется в машинный код и будет эффективно выполняться.

Первый вариант (с использованием compile и progv):

(defvar *fn* (compile nil '(lambda () (+ a 2)))
(progv '(a) '(4) (funcall *fn*))
=>
6

Второй вариант:

(defvar *fn* (compile nil '(lambda (a) (+ a 2))))
(funcall *fn* 4)
=>
6
0 голосов
/ 20 октября 2008

В Common Lisp вы можете определить * evalhook *, который позволяет передавать окружение в (eval ...). *evalhook* не зависит от платформы.

...