Существует несколько причин, по которым нельзя использовать EVAL
.
Основная причина для начинающих: вам это не нужно.
Пример (предполагается, что Common Lisp):
вычислите выражение с разными операторами:
(let ((ops '(+ *)))
(dolist (op ops)
(print (eval (list op 1 2 3)))))
Вот лучше написано как:
(let ((ops '(+ *)))
(dolist (op ops)
(print (funcall op 1 2 3))))
Есть много примеров, когда обучение начинающих Lisp думает, что им нужно EVAL
, но им это не нужно - поскольку выражения оцениваются, а также можно оценить удовольствие часть. В большинстве случаев использование EVAL
показывает отсутствие понимания оценщика.
Это та же проблема с макросами. Часто начинающие пишут макросы, где они должны писать функции - не понимая, какие макросы действительно нужны, и не понимают, что функция уже выполняет эту работу.
Часто бывает неправильным инструментом для использования EVAL
, и это часто указывает, что новичок не понимает обычные правила оценки Лиспа.
Если вы думаете, что вам нужно EVAL
, а затем проверить, если что-то вроде FUNCALL
, REDUCE
или APPLY
может быть использован вместо.
FUNCALL
- вызвать функцию с аргументами: (funcall '+ 1 2 3)
REDUCE
- вызвать функцию в списке значений и объединить результаты: (reduce '+ '(1 2 3))
APPLY
- вызвать функцию со списком в качестве аргументов: (apply '+ '(1 2 3))
,
Вопрос: мне действительно нужен eval или уже компилятор/оценщик, что я действительно хочу?
Основные причины, чтобы избежать EVAL
чуть более продвинутых пользователей:
вы хотите, чтобы убедиться, что ваш код компилируется, поскольку компилятор может проверить код для многих проблем и генерирует более быстрый код, иногда MUCH MUCH MUCH (это коэффициент 1000 ;-)) более быстрый код
код, который был сконструирован и нуждается в оценке, не может быть скомпилирован как можно раньше.
Eval произвольного пользовательского ввода открывает Проблемы безопасности
некоторые используют оценки с EVAL
может произойти в самое неподходящее время и создать строить проблемы
Чтобы объяснить последнюю точку с упрощенным Пример:
(defmacro foo (a b)
(list (if (eql a 3) 'sin 'cos) b))
Итак, я могу написать макрос, который на основе первого параметра использует eit ее SIN
или COS
.
(foo 3 4)
(sin 4)
и (foo 1 4)
(cos 4)
.
Теперь мы можем иметь:
(foo (+ 2 1) 4)
Это не дает желаемого результата.
один, то может потребоваться ремонт макро FOO
, вычислив переменную:
(defmacro foo (a b)
(list (if (eql (eval a) 3) 'sin 'cos) b))
(foo (+ 2 1) 4)
Но это по-прежнему не работает:
(defun bar (a b)
(foo a b))
Значение переменной просто не известна время компиляции.
Общая причина, по которой необходимо избегать EVAL
: часто используется для уродливых хаков.
eval не является злом, но зло - это то, что делает eval. – Anurag
@yar - Я думаю, что ваш комментарий указывает на то, что в мире есть очень одноразово-объектно-ориентированное мировоззрение. Вероятно, это допустимо для большинства языков, но будет отличаться в Common Lisp, где методы не относятся к классам и даже более разные в Clojure, где классы поддерживаются только через функции взаимодействия с Java. Jay отметил этот вопрос как Scheme, который не имеет встроенного понятия классов или методов (различные формы OO доступны в виде библиотек). – Zak
@ Zak, вы правы, я знаю только те языки, которые я знаю, но даже если вы работаете с документом Word без использования стилей, вы не являетесь СУХОЙ. Я хотел бы использовать эту технологию, чтобы не повторять себя. OO не универсальна, правда ... –