Вопрос

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

(defvar x '(+ a 2))

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

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

и

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

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

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

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

Это было полезно?

Решение

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

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

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

Другие советы

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

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

Видеть http://clisp.cons.org/impnotes.html#eval-environ.

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

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

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

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

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

Однако я советую вам хорошенько подумать, действительно ли вы этого хотите.

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

Можно использовать COMPILE для компиляции выражения в функцию, а затем использовать PROGV для ФУНКЦИОНАЛЬНОГО ВЫЗОВА скомпилированной функции в среде, где переменные устанавливаются динамически.Или, что еще лучше, используйте COMPILE для компиляции выражения в функцию, принимающую переменные.

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

Первый вариант (с использованием компиляции и 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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top