Как "правило" GOTO: если вы не знаете, что делаете, вы можете устроить беспорядок.
Помимо создания чего-то только из известных и безопасных данных, существует проблема, заключающаяся в том, что некоторые языки / реализации не могут достаточно оптимизировать код. Вы можете получить интерпретируемый код внутри eval
.
Eval просто небезопасен. Например, у вас есть следующий код:
eval('
hello('.$_GET['user'].');
');
Теперь пользователь заходит на ваш сайт и вводит URL http://example.com/file.php?user=);$is_admin=true;echo(
Тогда результирующий код будет:
hello();$is_admin=true;echo();
eval
(на любом языке) не является злом, как бензопила - не злом. Это инструмент. Это мощный инструмент, который при неправильном использовании может отрезать конечности и выпотрошить (образно говоря), но то же самое можно сказать и о многих инструментах в наборе инструментов программиста, включая:
goto
и друзья Если вам приходится использовать любой из этих мощных, потенциально опасных инструментов, трижды спросите себя: «Почему?» в цепочке. Например:
«Почему я должен использовать
eval
?» "Из-за foo." "Зачем нужен foo ?" «Потому что ...»
Если вы дойдете до конца этой цепочки, а инструмент по-прежнему выглядит правильным, то сделайте это. Задокументируйте ад из него. Испытайте ад из него. Перепроверяйте правильность и безопасность снова и снова. Но сделай это.
IMO, этот вопрос не относится к LISP . Вот ответ на тот же вопрос для PHP, и он применим к LISP, Ruby и другим другим языкам, в которых есть eval:
Основные проблемы с eval ():
- Потенциально небезопасный ввод. Передача ненадежного параметра приводит к ошибке . Часто бывает нетривиальной задачей убедиться, что параметру (или его части ) полностью доверяют.
- Хитрость. Использование eval () делает код умным, поэтому усложняет выполнение кода. Цитируя Брайана Кернигана " Отладка вдвое сложнее, чем написание кода с самого начала. Следовательно, если вы пишете код как насколько это возможно, вы, по определению , недостаточно умен, чтобы отлаживать его "
Основная проблема с фактическим использованием eval () заключается только в one:
- неопытные разработчики, которые используют его без должного внимания.
Взято из здесь .
Я думаю, что хитрость - это замечательный момент. Одержимость кодом для гольфа и кратким кодом всегда приводила к «умному» коду (для которого evals - отличный инструмент). Но вы должны писать свой код для удобства чтения, IMO, а не для демонстрации того, что вы сообразительный, и , чтобы не экономить бумагу (вы все равно не будете его печатать).
Затем в LISP возникает некоторая проблема, связанная с контекстом, в котором выполняется eval, поэтому ненадежный код может получить доступ к большему количеству вещей; в любом случае эта проблема кажется распространенной.
Eval в порядке, если вы ТОЧНО знаете, что в нем происходит. Любой ввод пользователя в него ДОЛЖЕН быть проверен и подтвержден, и все такое. Если вы не знаете, как быть уверенным на 100%, не делайте этого.
По сути, пользователь может ввести любой код для рассматриваемого языка, и он будет выполнен. Вы можете себе представить, какой ущерб он может нанести.
"Когда мне следует использовать eval
?" может быть лучший вопрос.
Короткий ответ: «когда ваша программа предназначена для написания другой программы во время выполнения, а затем для ее выполнения». Генетическое программирование является примером ситуации, в которой, вероятно, имеет смысл использовать eval
.
Существует несколько причин, по которым не следует использовать 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))
. В: Действительно ли мне нужен eval или компилятор/оценщик уже то, что я действительно хочу?
Основные причины избегать EVAL
для немного более продвинутых пользователей:
вы хотите убедиться, что ваш код скомпилирован, потому что компилятор может проверять код на наличие многих проблем и генерирует более быстрый код, иногда НАМНОГО МНОГО (это фактор 1000 ;-) ) ) более быстрый код
код, который построен и нуждается в оценке, не может быть скомпилирован как можно раньше.
Eval произвольного пользовательского ввода открывает проблемы безопасности
Некоторое использование оценки с EVAL
может произойти в неподходящее время и создать проблемы сборки
Чтобы объяснить последний пункт с помощью упрощенного примера:
(defmacro foo (a b)
(list (if (eql a 3) 'sin 'cos) b))
Итак, я могу написать макрос, который на основе первого параметра использует либо SIN
, либо COS
.
(фу 3 4)
делает (грех 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
: он часто используется для уродливых взломов.