块变量的括号规则

时间:2015-05-19 13:25:50

标签: arrays ruby block

我在阅读 The Ruby Way

时遇到了以下代码
class Array
  def invert
    each_with_object({}).with_index { |(elem, hash), index| hash[elem] = index }
  end
end

我想确保我理解括号在(elem, hash)中的作用。

第一种方法(each_with_object({}))将为块生成两个对象。第一个对象是数组中的元素;第二个对象将是哈希。括号确保将这两个对象分配给不同的块变量。如果我改为使用{ |elem, index} #code },那么elem将是一个由元素和散列组成的数组。我认为这很清楚。

我的困惑在于,如果我没有链接这两种方法,我就不必使用括号,而是可以使用:each_with_object({}) { |elem, obj #code }

关于块变量中何时需要括号的规则是什么?为什么他们在这两个例子之间有所区别?我的简单解释是,当方法没有链接时,yield代码看起来像yield (elem, obj),但是当方法被链接时,代码看起来像yield([elem, obj], index)。 (如果我们链接第三种方法,我们可以推测第二个数组会被传入)。它是否正确?从最后一个链接方法传入的对象不是数组吗?

我想而不是所有这些猜想,问题归结为:"链接接受块的方法时,yield语句是什么样的?

2 个答案:

答案 0 :(得分:2)

您的问题仅与块和块变量相关。相反,它涉及“消除歧义”数组的规则。

让我们考虑你的例子:

localhost/Utilities.WebService/API/GetFileInfo

我们有:

[1,2,3].each_with_object({}).with_index {|(elem, hash), index| hash[elem] = index}

我们可以通过将它转换为数组来看到这个枚举器的元素:

enum0 = [1,2,3].each_with_object({})
  #=> #<Enumerator: [1, 2, 3]:each_with_object({})> 

我们接下来有:

enum0.to_a
  #=> [[1, {}], [2, {}], [3, {}]]

您可能希望将enum1 = enum0.with_index #=> #<Enumerator: #<Enumerator: [1, 2, 3]:each_with_object({})>:with_index> enum1.to_a #=> [[[1, {}], 0], [[2, {}], 1], [[3, {}], 2]] 视为“复合枚举器”,但它只是一个枚举器。

您看到enum1有三个要素。这些元素由Enumerator#each传递给块。第一个是:

enum1

如果我们有一个块变量,请说enum1.first #=> [[1, {}], 0] ,那么

a

我们可以使用“消歧”以不同的方式打破这种情况。例如,我们可以写:

a #=> [[1, {}], 0]

现在让我们剔除所有元素:

a,b = [[1, {}], 0]
a #=> [1, {}] 
b #=> 0 

糟糕!那不是我们想要的。我们刚刚经历了“消除歧义”中的“暧昧”。我们需要写这个,以便我们的意图是明确的。我们通过添加括号来做到这一点。通过这样做,你告诉Ruby,“将这个位置的数组分解为其组成元素”。我们有:

a,b,c = [[1, {}], 0]
a #=> [1, {}] 
b #=> 0 
c #=> nil 

消歧可能非常有用。例如,假设一个方法返回了数组:

(a,b),c = [[1, {}], 0]
a #=> 1 
b #=> {} 
c #=> 0 

我们希望提取所有个人价值观。我们可以这样做:

[[1,[2,3],[[4,5],{a: 6}]],7]

同样,你必须记住括号只是意味着“将这个位置的数组分解为其组成元素”。

答案 1 :(得分:0)

规则是基本的:每个普查员都有一个“签名”。它产生两个参数,然后要传递的proc应该期望接收两个参数:

[1,2,3].each_with_index { |o, i| ...}

当对象可能被扩展时,如散列项,可以使用括号进行扩展。假设,迭代器产生一个数组,[*arr] - 允许使用类似的操作。

以下示例可能会对此有所了解:

[1,2,3].each_with_object('first') # yielding |e,        obj|
       .with_index                # yielding |elem,     idx|
    #  but wait! elem might be expanded here  ⇑⇑⇑⇑
    #                                        |(e, obj), idx|
       .each_with_object('second') do |((elem, fst_obj), snd_idx), trd_obj| 
    puts "e: #{elem}, 1o: #{fst_obj}, 2i: #{snd_idx}, 3o: #{trd_obj}"
end

#⇒ e: 1, 1o: first, 2i: 0, 3o: second
#⇒ e: 2, 1o: first, 2i: 1, 3o: second
#⇒ e: 3, 1o: first, 2i: 2, 3o: second