如何将方法传递给 Hash#map
How to pass a method to a Hash#map
代码:
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
看起来,它一定是某种隐式解构(或非严格参数处理),它适用于 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
This implicit destructuring also happens in block arguments.
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))
)。在问题中我要求的是解释,而不是解决方案。
你是对的,原因源于 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)
我使用 #->
显示打印的内容,使用 #=>
显示 returned 的内容。您使用了 map
,但 each
在这里更合适,并且说明了相同的观点。
在这两种情况下,接收器的元素都被传递到块1:
&method(:f)
这(或多或少,我将解释)相当于:
{ |k,v| p [k,v] }
块抱怨(对于数组和散列)它期望两个参数但只收到一个,这是不可接受的。 "Hmmm",reader在思考,"why doesn't it disambiguate in the normal way?"
让我们尝试直接使用该块:
a.map { |k,v| p [k,v] }
#-> [:a, nil]
# [1, nil]
h.map { |k,v| p [k,v] }
#-> [:a, 1]
这按预期工作,但 return 不是我们想要的数组。
a
(:a
)的第一个元素传入块中,并赋值块变量:
k,v = :a
#=> :a
k #=> :a
v #=> nil
和
p [k,v]
#-> :a
#-> nil
接下来,1
被传递到块并打印 [1,nil]
。
让我们再尝试一件事,使用通过 Proc::new:
创建的过程
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)转换为块来创建的。
2 来自 Proc 的文档:"For procs created using lambda or ->() an error is generated if the wrong number of parameters are passed to a Proc with multiple parameters. For procs created using Proc.new or Kernel.proc, extra parameters are silently discarded."
代码:
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
看起来,它一定是某种隐式解构(或非严格参数处理),它适用于 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
This implicit destructuring also happens in block arguments.
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))
)。在问题中我要求的是解释,而不是解决方案。
你是对的,原因源于 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)
我使用 #->
显示打印的内容,使用 #=>
显示 returned 的内容。您使用了 map
,但 each
在这里更合适,并且说明了相同的观点。
在这两种情况下,接收器的元素都被传递到块1:
&method(:f)
这(或多或少,我将解释)相当于:
{ |k,v| p [k,v] }
块抱怨(对于数组和散列)它期望两个参数但只收到一个,这是不可接受的。 "Hmmm",reader在思考,"why doesn't it disambiguate in the normal way?"
让我们尝试直接使用该块:
a.map { |k,v| p [k,v] }
#-> [:a, nil]
# [1, nil]
h.map { |k,v| p [k,v] }
#-> [:a, 1]
这按预期工作,但 return 不是我们想要的数组。
a
(:a
)的第一个元素传入块中,并赋值块变量:
k,v = :a
#=> :a
k #=> :a
v #=> nil
和
p [k,v]
#-> :a
#-> nil
接下来,1
被传递到块并打印 [1,nil]
。
让我们再尝试一件事,使用通过 Proc::new:
创建的过程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)转换为块来创建的。
2 来自 Proc 的文档:"For procs created using lambda or ->() an error is generated if the wrong number of parameters are passed to a Proc with multiple parameters. For procs created using Proc.new or Kernel.proc, extra parameters are silently discarded."