计划中的评估

时间:2009-06-25 15:11:38

标签: scheme

PAIP的Peter Norvig说:
“在现代的lisps中...... eval的使用频率较低(实际上,在Scheme中根本没有eval)” “如果你发现自己正在使用eval,那么你可能做错了事。”

有哪些方法可以避免在计划中使用eval?是否存在绝对需要评估的情况?

7 个答案:

答案 0 :(得分:7)

有些情况下eval是必需的,但它们总是涉及高级程序,这些程序可以动态加载某些代码(例如,Web服务器中的servlet)。至于“规避”使用它的方法 - 这取决于你试图解决的实际问题,除了... eval之外,没有任何神奇的解决方案来避免eval

(顺便说一句,我的猜测是PAIP是很久以前写的,在eval被添加到计划报告之前。)

答案 1 :(得分:4)

他错了。当然,Scheme中有eval

答案 2 :(得分:3)

在极少数情况下你需要eval。首先想到的情况是,您将使用程序构建程序然后执行它。这主要发生在遗传算法上。在这种情况下,您构建了许多您需要执行的随机程序。将eval与代码作为数据相结合使lisp成为进行遗传算法的最简单的编程语言。

拥有这些属性需要付出巨大的代价(在程序的速度和大小方面),因为你将删除所有可能对代码进行编译时优化的可能性,你必须保留完整的解释器结果二进制。

因此,当可以避免时,使用eval被认为设计不佳。

答案 3 :(得分:3)

至少对于最新版本的Scheme标准(R5RS及更高版本),Scheme没有eval的声明是不准确的。通常,你想要的是一个宏,它将在编译时生成代码。

确实应该避免使用eval。首先,我从未见过如何表现出令人满意的定义,例如:

  1. 在没有环境传递时应该评估哪些环境表达式?
  2. 当您通过环境时,这些工作如何?例如,标准指定无法预绑定该环境对象中的值。
  3. 也就是说,我使用了一个Scheme应用程序,该应用程序使用eval在运行时动态生成代码,以防在编译时无法知道计算结构。出于性能原因,目的是让Scheme系统在运行时编译代码 - 并且困难在于没有标准方法来告诉Scheme系统“编译此代码。”

    不言而喻,eval可能是一个巨大的安全风险。你永远不应该eval任何与用户输入没有巨大隔离墙的东西。基本上,如果你想安全地使用eval,你应该在编译器类系统的代码生成阶段的上下文中,在你解析了一些输入之后(使用全面定义的语法!)

答案 4 :(得分:1)

首先,PAIP是为Common Lisp而不是Scheme编写的,所以我不知道他会说同样的事情。 CL宏与eval的功能大致相同,尽管在编译时而不是运行时,您还可以执行其他操作。如果你给我看一个在Common Lisp中使用eval的例子,我可以试着想出其他做同样事情的方法。

我不是Scheme程序员。作为Common Lisp程序员,我只能从Norvig的角度讲。我不认为他在谈论Scheme,我不知道他是否特别熟悉或了解Scheme。

其次,Norvig说“你可能做错了”,而不是“你做错了”。这意味着,尽管他知道,有时eval是正确的事情。在像C这样的语言中,我会对goto说同样的话,虽然它们在某些受限制的情况下非常有用,但大多数goto使用的是那些不知道更好的人。

答案 5 :(得分:0)

我在脚本环境中看到的'eval'的一个用途是使用运行时值来参数化一些代码。例如,在psuedo-C:

param = read_integer();
fn = eval("int lambda(int x) { 
            int param = " + to_string(param) + "; 
            return param*x; }");

我希望你发现真的很难看。字符串粘贴在运行时创建代码?伊克。在Scheme和其他词法范围的Lisp中,您可以在不使用eval的情况下创建参数化函数。

(define make-my-fn
  (lambda (param)
    (lambda (x)  (* param x)))

(let* ([ param  (read-integer) ]
       [ fn     (make-my-fn param ])
  ;; etc.
 )

就像提到的那样,动态代码加载等仍需要eval,但参数化代码和代码组合可以用第一类函数生成。

答案 6 :(得分:-1)

你可以在方案中编写一个方案解释器。这当然是可能的,但这是不切实际的。

当然,这是一个普遍的答案,因为我没有使用过计划,但它可能对你有帮助。 :)