我的理解是Hash#select
和Hash#reject
每个迭代都传递一个键数组及其值[key, value]
作为单个块参数,你可以直接在每个迭代中选择它们。阻止使用隐式破坏性赋值:
{a: 1, b: 2}.select{|k, v| k == :a} # => {:a => 1}
{a: 1, b: 2}.reject{|k, v| v == 1} # => {:b => 2}
或明确的破坏性任务:
{a: 1, b: 2}.select{|(k, v)| k == :a} # => {:a => 1}
我预计,当我传递一个一元块时,整个[key, value]
数组将被传递,但实际上,它似乎传递了密钥:
{a: 1}.select{|e| p e} # => Prints `:a` (I expected `[:a, 1]`)
为什么这样工作?对于Hash
等其他map
实例方法,将传递整个[key, value]
数组。
如果它特别设计为与二进制块相比对一元块的工作方式不同,那么我可以理解它是有用的。但是,然后我不明白为什么上面用明确的破坏性赋值的情况按原样工作。并且我也找不到任何提及此类规范的文件。
修改我的{a: 1, b: 2}.reject{|(k, v)| v == 1}
结果错误。在此更正:
{a: 1, b: 2}.reject{|(k, v)| v == 1} # => {:a=>1, :b=>2} (not `{:b=>2}`)
现在,这也表明(k, v)
是key
,而不是[key, value]
,因此v
始终是nil
。参看DarekNędza的评论。
答案 0 :(得分:7)
它实际上传递了两个参数。
你所观察到的仅仅是触发器和lambdas如何处理多余论点之间的区别。块(Proc除非你告诉Ruby否则)表现得好像它有一个额外的splat并丢弃多余的参数,而lambdas(和方法对象)由于不正确的arity而拒绝调用者。
演示:
>> p = proc { |e| p e }
=> #<Proc:0x007f8dfa1c8b50@(irb):1>
>> l = lambda { |e| p e }
=> #<Proc:0x007f8dfa838620@(irb):2 (lambda)>
>> {a: 1}.select &p
:a
=> {:a=>1}
>> {a: 1}.select &l
ArgumentError: wrong number of arguments (2 for 1)
from (irb):2:in `block in irb_binding'
from (irb):4:in `select'
from (irb):4
from /usr/local/bin/irb:11:in `<main>'
顺便说一句,因为在评论中提到了map
,相反,实际上传递了一个参数。它被分配给两个不同的变量,因为你可以在赋值运算符的右侧分配多个带有数组的变量,但它始终是一个参数。
演示:
>> {a: 1}.map { |k, v| p k, v }
:a
1
>> {a: 1}.map &p
[:a, 1]
=> [[:a, 1]]
>> {a: 1}.map &l
[:a, 1]
在更改p
和l
进一步定义后:
>> p = proc { |k, v| p k, v }
=> #<Proc:0x007ffd94089258@(irb):1>
>> l = lambda { |k, v| p k, v }
=> #<Proc:0x007ffd940783e0@(irb):2 (lambda)>
>> {a: 1}.map &p
:a
1
=> [[:a, 1]]
>> {a: 1}.map &l
ArgumentError: wrong number of arguments (1 for 2)
from (irb):2:in `block in irb_binding'
from (irb):4:in `each'
from (irb):4:in `map'
from (irb):4
from /usr/local/bin/irb:11:in `<main>'