Есть несколько причин, по которым нельзя использовать EVAL
.
Основная причина для начинающих: вам это не нужно.
Пример (в предположении Common Lisp):
EVALuate выражение с различными операторами:
(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
, и это часто означает, что новичок не понимает обычных правил оценки Lisp.
Если вы считаете, что вам нужно EVAL
, то проверьте, можно ли использовать что-то вроде FUNCALL
, REDUCE
или APPLY
.
FUNCALL
- вызвать функцию с аргументами: (funcall '+ 1 2 3)
REDUCE
- вызвать функцию из списка значений и объединить результаты: (reduce '+ '(1 2 3))
APPLY
- вызвать функцию со списком в качестве аргументов: (apply '+ '(1 2 3))
.
Q: мне действительно нужен eval или компилятор / оценщик уже того, чего я действительно хочу?
Основные причины, по которым следует избегать EVAL
для чуть более продвинутых пользователей:
вы хотите убедиться, что ваш код скомпилирован, потому что компилятор может проверять код на наличие множества проблем и генерирует более быстрый код, иногда намного (намного больше (это в 1000 раз больше);) более быстрый код
код, который создан и нуждается в оценке, не может быть скомпилирован как можно раньше.
Оценка произвольного пользовательского ввода открывает проблемы безопасности
некоторое использование оценки с EVAL
может произойти в неподходящее время и создать проблемы при сборке
Чтобы объяснить последний пункт в упрощенном примере:
(defmacro foo (a b)
(list (if (eql a 3) 'sin 'cos) b))
Итак, я могу написать макрос, который на основе первого параметра использует SIN
или COS
.
(foo 3 4)
делает (sin 4)
и (foo 1 4)
делает (cos 4)
.
Теперь мы можем иметь:
(foo (+ 2 1) 4)
Это не дает желаемого результата.
Затем можно захотеть восстановить макрос FOO
, EVALuating переменную:
(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
: это часто используется для уродливых хаков.