如何将块传递给名称以等号结尾的方法?

时间:2012-01-25 20:31:25

标签: ruby

这听起来很奇怪不是吗?

class Dummy
  def foo=(value); end
end

Dummy.new.foo = 1 { |x| x } # => syntax error
Dummy.new.foo=(1) { |x| x } # => syntax error
我尝试了每一个空白,括号,逗号的排列;没运气。我很困惑。我从不怀疑用'='结尾的方法很特别。这是一个错误吗?是打算吗?如果打算,为什么?有记录吗?哪里?请分享见解。

感谢

PS。 ruby是1.9.2p290(2011-07-09修订版32553)[x86_64-darwin11.0.1]

2 个答案:

答案 0 :(得分:2)

=结尾的方法的语法糖确实使它变得特别。你仍然可以做一些事情,比如将多个参数传递给该方法,或传递一个块,但不能以任何漂亮或方便的方式:

class Foo
  def bar=(a,b=nil)
    p [a,b]
    if block_given?
      yield "hi"
    else
      puts "No block"
    end
  end
end

f = Foo.new
f.bar = 42
#=> [42, nil]
#=> No block

f.bar = 42, 17
#=> [[42,17], nil]
#=> No block

f.send(:bar=,42,17) do |x|
  puts "x is #{x.inspect}"
end
#=> [42, 17]
#=> x is "hi"

这些方法特殊的另一种方式是,当使用语法糖调用时,它们会计算为右手值,而不是方法的返回值:

class Foo
  def bar=(a)
    return 17 # really explicit
  end
end

f = Foo.new

x = (f.bar = 42)
p x
#=> 42

x = f.send(:bar=,42)
p x
#=> 17

答案 1 :(得分:0)

方法本身并不特别,但更多的是与Ruby如何处理分配(如foo = bar)有关。首先评估右侧,然后评估左侧并采取适当的措施。如果左侧是对象属性,则调用适当的setter方法。

所以在你的例子中:

Dummy.new.foo = 1 { |x| x }

第一个ruby尝试评估1 { |x| x },这是导致语法错误的原因。

Dummy.new.foo=something实际上并不意味着“调用名为foo=的方法”,但实际上意味着更像“评估something”,然后确定`Dummy.new.foo是什么,如果它看起来像一个对象属性,请在名称中添加=并调用该方法“。这就是为什么Dummy.new.foo=Dummy.new.foo =都以相同的方式工作的原因。

您可以使用send调用这些方法,并可以使用以下方法传递一个块:

Dummy.new.send "foo=", 2 do
  puts "HI"
end

这是因为使用send您可以明确命名要调用的方法。

当然,最终的结果是以=结尾的方法似乎有一些你需要注意的“特殊”行为,但了解实际情况可能会有用。