“Is 'eval' supposed to be nasty?”激发了这一点:
大多数人都认为eval
很糟糕,而且在大多数情况下会有更优雅/更安全的替代品。
所以我想问:如果eval
经常被误用,是否真的需要作为语言功能?它是在做更坏的而不是好事吗?
就个人而言,我发现它唯一有用的地方是插入配置文件中提供的字符串。
编辑:这个问题的目的是在eval是唯一或最佳解决方案时尽可能多地获得真实案例。所以,请不要进入“如果语言限制程序员的创造力”的方向。
Edit2:当我说eval
时,我当然是指eval
字符串,而不是将红宝石块传递给instance_eval
或class_eval
。
答案 0 :(得分:23)
我所知道的唯一情况(除了“我有这个字符串,我想执行它”)是动态处理本地和全局变量。 Ruby有方法来获取局部变量和全局变量的名称,但是缺少基于这些名称来获取或设置其值的方法。进行AFAIK的唯一方法是使用eval
。
任何其他用途几乎肯定是错误的。我不是大师,也不能明确说明没有其他人,但是我曾经见过的其他一些用例,有人说“你需要eval,”我找到了一个没有的解决方案。 / p>
请注意,我在这里谈论 string eval 。顺便说一下。 Ruby也有instance_eval
,可以在接收器的上下文中执行字符串或块。这种方法的块形式快速,安全且非常有用。
答案 1 :(得分:15)
什么时候有道理?我会说什么时候没有合理的选择。我能够想到一个我无法想到替代方案的用法:irb,如果你深入挖掘(workspace.rb
,我的副本中的第80行,如果你感兴趣的话)使用eval执行你的输入:
def evaluate(context, statements, file = __FILE__, line = __LINE__)
eval(statements, @binding, file, line)
end
这对我来说似乎很合理 - 在你被要求这样做的那一刻,你特别不知道你要执行什么代码的情况。动态和互动的东西似乎符合要求。
答案 2 :(得分:9)
eval存在的原因是因为当你需要它时,当你真正需要它时,没有替代品。毕竟,只有你可以用创造性的方法调度做这么多,并且在某些时候你需要执行任意代码。
仅仅因为语言具有可能危险的功能并不意味着它本身就是一件坏事。当一种语言假设比其用户更多时,那就是遇到麻烦的时候。
我认为当你发现一种没有危险的编程语言时,你会找到一种不太有用的编程语言。
eval何时合理?用务实的话来说,当你说的时候。如果它是你的程序并且你是程序员,你可以设置参数。
答案 3 :(得分:6)
eval()
有一个非常重要的用例,它不能使用任何其他东西来实现(AFAIK),那就是为绑定找到相应的对象引用。
假设您已经传递了一个块但是(由于某种原因)您需要访问绑定的对象上下文,您将执行以下操作:
obj = eval('self', block.binding)
定义以下内容也很有用:
class Proc
def __context__
eval('self', self.binding)
end
end
答案 4 :(得分:5)
IMO主要针对领域特定语言。
“Evaluation Options in Ruby”是Jay Fields关于InfoQ的文章。
答案 5 :(得分:3)
答案 6 :(得分:2)
例如,递归代码解析器中所需的方法/函数取决于要解析的语言。如果您的应用程序在运行中构建这样的解析器,那么使用eval可能是有意义的。你可以编写一个通用的解析器,但它可能不是一个优雅的解决方案。
“Programatically filling in a letrec in Scheme. Macros or eval?”是我在Scheme中发布的一个关于eval的问题,其使用大多是不可避免的。
答案 7 :(得分:2)
一般情况下,eval
是一种非常有用的语言功能,可以运行任意代码。这应该是一件罕见的事情,但也许您正在制作自己的REPL,或者您希望由于某种原因将ruby运行时暴露给最终用户。它可能发生,这就是该功能存在的原因。如果您使用它来解决语言的某些部分(例如全局变量),那么语言是有缺陷的,或者您对语言的理解是有缺陷的。解决方案通常不使用eval,而是要更好地理解语言或选择不同的语言。
值得注意的是,在ruby中,instance_eval
和class_eval
还有其他用途。
答案 8 :(得分:0)
您很可能在没有意识到的情况下经常使用 eval
;这就是 rubygems 如何加载 Gemspec 的内容。通过rubygems/lib/specification.rb
:
# Note: I've removed some lines from that listing to illustrate the core concept
def self.load(file)
code = File.read(file)
begin
_spec = eval code, binding, file # <-------- EVAL HAPPENS HERE
if Gem::Specification === _spec
return _spec
end
warn "[#{file}] isn't a Gem::Specification (#{_spec.class} instead)."
rescue SignalException, SystemExit
raise
rescue SyntaxError, Exception => e
warn "Invalid gemspec in [#{file}]: #{e}"
end
nil
end
通常,gem specification would look like this:
Gem::Specification.new do |s|
s.name = 'example'
s.version = '0.1.0'
s.licenses = ['MIT']
s.summary = "This is an example!"
s.description = "Much longer explanation of the example!"
s.authors = ["Ruby Coder"]
s.email = 'rubycoder@example.com'
s.files = ["lib/example.rb"]
s.homepage = 'https://rubygems.org/gems/example'
s.metadata = { "source_code_uri" => "https://github.com/example/example" }
end
请注意,gemspec 文件只是创建一个新对象,但不会分配它,也不会将其发送到任何地方。
尝试 load
或 require
这个文件(甚至用 Ruby 执行它)不会返回 Gem::Specification
值。 eval
是提取由外部 ruby 文件定义的值的唯一方法。
答案 9 :(得分:0)
eval 的一个用途是将另一种语言编译为 ruby:
ruby_code = "(def foo (f a b) (mapv f (cons a b)))".compile_to_ruby
# "foo_proc = ->(f a b) { mapv_proc.call(f, (cons_proc.call(a, b)) }"
eval ruby_code