在Ruby 1.9中,当传递一个块时,构建器会抛出“参数数量错误”的错误。

3

我正在尝试将一个Ruby 1.8应用程序升级到1.9,但在这里遇到了一些困难。在Ruby 1.8.7中,我可以将一个块传递给Builder 3.0.0,并按预期调用它:

1.8.7 :003 > @builder = Builder::XmlMarkup.new
1.8.7 :004 > block = lambda { puts "foo" }
1.8.7 :005 > @builder.tag(&block)
foo

但在1.9版本中,我遇到了这个错误:
1.9.3p0 :002 > @builder = Builder::XmlMarkup.new
1.9.3p0 :003 > block = lambda { puts "foo" }
1.9.3p0 :004 > @builder.content(&block)
ArgumentError: wrong number of arguments (1 for 0)
  from (irb):3:in `block in irb_binding'
  from /Users/dev/.bundle/ruby/1.9.1/gems/builder-3.0.0/lib/builder/xmlbase.rb:155:in `call'
  ...

将其重写为一个刺人的lambda表达式(这只是语法糖,对吧?)也没有帮助:

1.9.3p0 :006 > block = -> { puts "foo" }
1.9.3p0 :007 > @builder.content(&block)
ArgumentError: wrong number of arguments (1 for 0)

传递实际的块而不是一个引用确实可以工作:
1.9.3p0 :008 > @builder.content { puts "foo" }
foo

Help?

2个回答

6

这实际上是因为在 Ruby 1.9 中,lambda 和 proc 的行为略有不同。可以将 lambda 视为数学上精确的函数,需要指定完全相同数量的参数,而 proc 则展现了 Ruby 1.8 更宽松的行为。例如:

a = lambda {|v| p v }
a.call()     # ArgumentError: wrong number of arguments (0 for 1)
a.call(1)    # prints "1"
a.call(1, 2) # ArgumentError: wrong number of arguments (2 for 1)

b = proc {|v| p v }
b.call()     # prints "nil"
b.call(1)    # prints "1"
b.call(1, 2) # prints "1"

请注意,这两个对象都是Proc类型的,但可以通过.lambda?方法区分它们。
a.class   # => Proc
a.lambda? # => true
a.arity   # => 1  (number of parameters)
b.class   # => Proc
b.lambda? # => false
b.arity   # => 1  (number of parameters)

proc 在 1.8 中生成一个 lambda,在 1.9 中生成一个 proc。Proc 和 lambda 的行为不同。 - Andrew Grimm
1
即使在1.8.7版本中,proclambda都会产生Proc类的对象。实际上,proc本质上是lambda的别名,在Ruby 1.9中没有区别。我的答案中的第一个代码块在所有情况下都可以在Ruby 1.8.7中成功运行,尽管当数字参数不匹配时,它会打印警告(对于ab都是如此)。 - marcianx

0
哦,我想通了。在Builder中引起问题的那一行是这个:
block.call(self)

换句话说,它将自身作为参数传递给块。在 Ruby 1.8 中,块可以忽略这一点,但在 1.9 中,它必须声明所有参数。因此:
1.9.3p0 :023 > block = lambda { |dummy| puts "foo" }
1.9.3p0 :024 > @builder.content(&block)
foo

太棒了!


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