【问题标题】:Why does Hash#select and Hash#reject pass a key to a unary block?为什么 Hash#select 和 Hash#reject 将密钥传递给一元块?
【发布时间】:2013-12-31 18:03:50
【问题描述】:

我的理解是 Hash#selectHash#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。参照。 Darek Nędza 的评论。

【问题讨论】:

  • 实现可能会检查block.arity 并且只产生正确数量的参数。但是为什么实现是一个有趣的问题。
  • b = {a: 1}; b.method(:select) # => #<Method: Hash#select>; b.method(:map) # => #<Method: Hash(Enumerable)#map> 可能会解释得更多,尽管我觉得你在这里了解更多
  • Enumerable#select 行为不同 b = {a: 1}; b.select { |x| p x } # => :a; b.to_enum.select { |x| p x } # => [:a, 1]
  • Hash 自己实现 select,但将 map 留给 Enumerable(为什么?这是个谜)。 Hash#select explicitly passes two arguments to the block 因为它知道哈希的特殊 k/v 结构。 Enumerable#map 使用 each,它只知道单个值。为什么有些 Enumerable 方法在 Hash 中特别实现,而有些则留给 Enumerable 是一个谜,可能会在某个 Ruby 特定论坛上得到更好的回答。
  • 这可能会有所帮助:{a: 1, b: 2}.select{|(k, v)| puts v.class; k == :a} #NilClass.

标签: ruby hash block


【解决方案1】:

它实际上总是传递两个参数。

您观察到的只是 procs 和 lambdas 处理多余参数的方式之间的区别。块(Procs,除非你告诉 Ruby

演示:

>> 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>'

顺便说一句,因为它在 cmets 中被提及: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]

在更改 pl 时进一步向上定义:

>> 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>'

【讨论】:

    猜你喜欢
    • 2015-06-01
    • 1970-01-01
    • 2014-09-17
    • 2019-09-21
    • 2012-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-24
    相关资源
    最近更新 更多