代码:
a = [1, 2, 3]
h = {a: 1}
def f args
p args
end
h.map(&method(:f))
a.map(&method(:f))
h.map do |k,v|
p [k,v]
end
输出:
[:a, 1]
1
2
3
[:a, 1]
为什么我不能为哈希定义f
如下?
def f k, v
p [k, v]
end
答案 0 :(得分:3)
你是正确的,原因是proc
和lambda
之间的两个主要区别之一。我会尝试以与你稍微不同的方式解释它。
考虑:
a = [:a, 1]
h = {a: 1}
def f(k,v)
p [k, v]
end
a.each(&method(:f))
#-> in `f': wrong number of arguments (1 for 2) (ArgumentError)
h.each(&method(:f))
#-> in `f': wrong number of arguments (1 for 2) (ArgumentError)
我使用#->
显示打印内容,#=>
显示返回的内容。您使用了map
,但each
更适合此处,并提出相同的观点。
在这两种情况下,接收器的元素都被传递到块 1 :
&method(:f)
(或多或少,正如我将解释的)相当于:
{ |k,v| p [k,v] }
该块正在抱怨(对于数组和散列)它期望两个参数但只接收一个,这是不可接受的。 “嗯”,读者正在思考,“为什么它不以正常的方式消除歧义?”
让我们直接尝试使用该块:
a.map { |k,v| p [k,v] }
#-> [:a, nil]
# [1, nil]
h.map { |k,v| p [k,v] }
#-> [:a, 1]
这可以按预期工作,但不会返回我们想要的数组。
a
(:a
)的第一个元素被传递到块中,块变量被赋值:
k,v = :a
#=> :a
k #=> :a
v #=> nil
和
p [k,v]
#-> :a
#-> nil
接下来,1
将传递到块,并打印[1,nil]
。
让我们再尝试一下,使用Proc::new创建的proc:
fp = Proc.new { |k,v| p [k, v] }
#=> #<Proc:0x007ffd6a0a8b00@(irb):34>
fp.lambda?
#=> false
a.each { |e| fp.call(e) }
#-> [:a, nil]
#-> [:a, 1]
h.each { |e| fp[e] }
#-> [:a, 1]
(这里我使用了Proc#call的三个别名中的一个。)我们看到调用proc
与使用块的结果相同。 proc
需要两个参数,但只接收一个,但与lambda
不同,不会抱怨 2 。
这告诉我们,我们需要对a
和f
进行细微更改:
a = [[:a, 1]]
h = {a: 1}
def f(*(k,v))
p [k, v]
end
a.each(&method(:f))
#-> [:a, 1]
h.each(&method(:f))
#-> [:a, 1]
顺便说一句,我认为你可能已经使用变量名args
来愚弄自己:
def f args
p args
end
因为该方法只有一个参数,无论你怎么称呼它。 : - )
1该块由&
在方法f
上调用Method#to_proc创建,然后将proc(实际上是lambda)转换为块。 < / p>
2来自Proc的文档:“对于使用lambda或 - &gt;()创建的proc,如果将错误数量的参数传递给具有多个参数的Proc,则会生成错误。使用Proc.new或Kernel.proc创建,额外的参数将被静默丢弃。“
答案 1 :(得分:0)
看起来,它必须是某种隐式解构(或非严格的参数处理),它适用于proc
s,但不适用于lambda
s:
irb(main):007:0> Proc.new { |k,v| p [k,v] }.call([1,2])
[1, 2]
=> [1, 2]
irb(main):009:0> lambda { |k,v| p [k,v] }.call([1,2])
ArgumentError: wrong number of arguments (1 for 2)
from (irb):9:in `block in irb_binding'
from (irb):9:in `call'
from (irb):9
from /home/yuri/.rubies/ruby-2.1.5/bin/irb:11:in `<main>'
但是可以让它发挥作用:
irb(main):010:0> lambda { |(k,v)| p [k,v] }.call([1,2])
[1, 2]
=> [1, 2]
因此:
def f ((k, v))
p [k, v]
end
所以Hash#map
总是传递一个参数。
<强> UPD 强>
这种隐式解构也发生在块参数中。
names = ["Arthur", "Ford", "Trillian"] ids = [42, 43, 44] id_names = ids.zip(names) #=> [[42, "Arthur"], [43, "Ford"], [44, "Trillian"]] id_names.each do |id, name| puts "user #{id} is #{name}" end
http://globaldev.co.uk/2013/09/ruby-tips-part-2/
UPD 不要误会我的意思。我不打算写这样的代码(def f ((k, v))
)。在我要求解释的问题中,不是解决方案。