Clojure 为什么eval是邪恶的?
我知道Lisp和Scheme程序员通常说除非严格必要,否则应该避免使用Clojure 为什么eval是邪恶的?,clojure,scheme,lisp,common-lisp,eval,Clojure,Scheme,Lisp,Common Lisp,Eval,我知道Lisp和Scheme程序员通常说除非严格必要,否则应该避免使用eval。我已经看到了对几种编程语言的相同建议,但我还没有看到反对使用eval的明确论据列表。在哪里可以找到有关使用eval的潜在问题的说明 例如,我知道过程编程中的GOTO问题(使程序无法阅读和维护,使安全问题难以发现等),但我从未见过反对eval的论点 有趣的是,反对GOTO的相同论据应该对continuations有效,但我看到,例如,阴谋家不会说continuations是“邪恶的”——在使用它们时应该小心。他们更可能
eval
。我已经看到了对几种编程语言的相同建议,但我还没有看到反对使用eval
的明确论据列表。在哪里可以找到有关使用eval
的潜在问题的说明
例如,我知道过程编程中的GOTO
问题(使程序无法阅读和维护,使安全问题难以发现等),但我从未见过反对eval
的论点
有趣的是,反对
GOTO
的相同论据应该对continuations有效,但我看到,例如,阴谋家不会说continuations是“邪恶的”——在使用它们时应该小心。他们更可能不喜欢使用eval
的代码,而不喜欢使用continuations的代码(据我所知,我可能是错的)。只要你确切地知道eval是怎么回事。任何进入其中的用户输入都必须经过检查和验证。如果你不知道如何百分之百地确定,那就不要这样做
基本上,用户可以为所讨论的语言键入任何代码,然后执行。你可以自己想象他能造成多大的伤害。就像后藤“规则”:如果你不知道自己在做什么,你会把事情搞得一团糟
除了从已知的安全数据中构建一些东西之外,还有一个问题,即某些语言/实现无法充分优化代码。您可能最终会在eval
中找到解释过的代码,eval是不安全的。
例如,您有以下代码:
eval('
hello('.$_GET['user'].');
');
现在用户来到您的站点并输入url)$是_admin=true;回音(
然后生成的代码将是:
hello();$is_admin=true;echo();
eval
(在任何语言中)都不是邪恶的,就像电锯不是邪恶的一样。它是一种工具。它恰巧是一种强大的工具,当被误用时,可以切断四肢并取出内脏(隐喻地说),但程序员工具箱中的许多工具也是如此,包括:
和朋友转到
- 基于锁的线程
- 延续
- 宏(卫生或其他)
- 指针
- 可重启异常
- 自修改代码
- …还有成千上万的演员
eval
?”
“因为福。”“为什么是福?”
“有必要吗?”“因为……”
如果你走到了这条链的尽头,而这个工具看起来仍然是正确的,那么就去做。记录它。测试它。反复检查正确性和安全性。但是去做。有几个理由不应该使用
EVAL
初学者的主要原因是:你不需要它。
示例(假设公共Lisp):
使用不同的运算符计算表达式:
(let((ops'(+*))
(精神科医生(行动)
(打印(评估(列表op 1 2 3(()))))
最好写为:
(let((ops'(+*))
(精神科医生(行动)
(打印(所有操作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
的主要原因如下:
- 您希望确保您的代码已编译,因为编译器可以检查代码中的许多问题,并生成更快的代码,有时甚至更快(这是因子1000;-)的代码
- 无法尽早编译已构造并需要评估的代码
- 评估任意用户输入会带来安全问题
- 使用
进行评估可能会在错误的时间发生,并造成构建问题EVAL
(defmacro-foo(a-b)
(列表(如果(eql a 3)“sin”cos)b))
因此,我可能想编写一个基于第一个参数的宏,它使用SIN
或COS
(foo34)
做(sin4)
和(foo14)
做(cos4)
现在我们可能有:
(foo (+ 2 1) 4)
这并不能得到期望的结果
然后可能需要通过计算变量来修复宏FOO
:
(defmacro-foo(a-b)
(列表(如果(eql(评估a)3)“sin”cos)b))
(富(+21)4)
但这仍然不起作用:
(defun bar (a b)
(foo a b))
变量的值仅为
(eval `(macro ,arg0 ,arg1 ,arg2))))
(defun toggle-trace-aux (fspec &rest args)
(cond ((member fspec (eval '(trace)) :test #'equal)
(eval `(untrace ,fspec))
(format nil "~S is now untraced." fspec))
(t
(eval `(trace ,@(if args `(:encapsulate nil) (list)) ,fspec ,@args))
(format nil "~S is now traced." fspec))))
(eval (read-line))