为什么Ruby会在代码的最后一行报错未定义的变量?

12

假设 foo bar baz 没有被定义,那么这行代码

foo bar baz

会引发以下错误:

NameError(未定义的局部变量或主对象的方法`baz')

在Python、PHP和JavaScript的REPL中, foo(bar(baz))的第一个问题是 foo 未定义。为什么Ruby首先抱怨 baz

2个回答

5

Ruby允许第一个被调用的方法(baz)动态定义另外两个方法。它在实际的方法调用发生之前不会尝试解析foobar作为方法调用,并且因为baz首先引起了错误,所以永远不会到达那个方法调用。

如果baz动态定义了方法foobar,那就没有问题:

def baz
  define_method(:foo) { |x| "foo #{x}" }
  define_method(:bar) { |x| "bar #{x}" }
  "baz!"
end

foo bar baz # => "foo bar baz!"

1
那么更好的问题是,为什么JavaScript等语言不做同样的事呢? - Jordan Running
2
@JordanRunning 两种语言解析和评估的方式不同,你需要问一个对这两种语言都有更深入了解的人为什么Ruby选择允许这样做而JavaScript不允许。 - user229044
@meagar “Ruby允许第一个被调用的方法(baz)动态定义其他两个方法。” 在我看来,这似乎不太正确。 Ruby不会自动定义方法,而是在解析方法本身之前先解析参数。否则,在引发错误后您将能够调用foo - 3limin4t0r
@JohanWentholt 我不确定你对什么持有异议。我并没有声称Ruby动态定义方法,我是说方法可以在解析之后但执行之前动态定义。我回答中的代码证明了这一点。 - user229044
问题在于为什么baz会先被评估,而不是它是否可以定义另外两个方法(如果foo首先被评估,您也可以在foo中定义barbaz)。这个答案基本上只是证明了OP已经注意到的内容,而不是提供一个答案。 - Mladen Jablanović

4

参考这篇文章ruby-magic-code-interpretation

在控制台中检查它

2.3.1 :001 > puts RubyVM::InstructionSequence.compile("foo bar baz").disasm
== disasm: #<ISeq:<compiled>@<compiled>>================================
0000 trace            1                                               (   1)
0002 putself          
0003 putself          
0004 putself          
0005 opt_send_without_block <callinfo!mid:baz, argc:0, FCALL|VCALL|ARGS_SIMPLE>, <callcache>
0008 opt_send_without_block <callinfo!mid:bar, argc:1, FCALL|ARGS_SIMPLE>, <callcache>
0011 opt_send_without_block <callinfo!mid:foo, argc:1, FCALL|ARGS_SIMPLE>, <callcache>
0014 leave            
 => nil 

由于 Ruby 解释器的顺序,它会抛出错误。
NameError (undefined local variable or method `baz' for main:Object)

我之前不知道这个技巧。 :) - Arup Rakshit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接