如何将方法传递给Hash #map

时间:2015-03-27 23:28:22

标签: ruby hashmap hashtable

代码:

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

2 个答案:

答案 0 :(得分:3)

你是正确的,原因是proclambda之间的两个主要区别之一。我会尝试以与你稍微不同的方式解释它。

考虑:

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

这告诉我们,我们需要对af进行细微更改:

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)))。在我要求解释的问题中,不是解决方案。