块变量的括号规则
Rules on Parenthesis for Block Variables
我 运行 在阅读 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 语句是什么样的?
规则很基本:每个普查员都有一个“签名”。例如。它产生两个参数,那么要传递的过程应该期望接收两个参数:
[1,2,3].each_with_index { |o, i| ...}
当对象可能被展开时,比如hash item,可以使用括号展开。假设迭代器产生一个数组,[*arr]
-like 操作是允许的。
以下示例可能会对此有所启发:
[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
您的问题仅与块和块变量无关。相反,它涉及 "disambiguating" 数组的规则。
让我们考虑一下你的例子:
[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
视为 "compound enumerator",但它只是一个枚举数。
你看到 enum1
有三个元素。这些元素通过 Enumerator#each 传递给块。第一个是:
enum1.first
#=> [[1, {}], 0]
如果我们只有一个块变量,比如 a
,那么
a #=> [[1, {}], 0]
我们可以使用 "disambiguation" 以不同的方式分解它。例如,我们可以这样写:
a,b = [[1, {}], 0]
a #=> [1, {}]
b #=> 0
现在让我们找出所有元素:
a,b,c = [[1, {}], 0]
a #=> [1, {}]
b #=> 0
c #=> nil
糟糕!那不是我们想要的。我们刚刚经历了 "disambiguate" 中的 "ambiguous"。我们需要这样写,以便我们的意图明确。我们通过添加括号来做到这一点。通过这样做,您在告诉 Ruby、"decompose the array in this position to its constituent elements"。我们有:
(a,b),c = [[1, {}], 0]
a #=> 1
b #=> {}
c #=> 0
消除歧义非常有用。例如,假设一个方法返回数组:
[[1,[2,3],[[4,5],{a: 6}]],7]
并且我们希望提取所有的单个值。我们可以这样做:
(a,(b,c),((d,e),f)),g = [[1,[2,3],[[4,5],{a: 6}]],7]
a #=> 1
b #=> 2
c #=> 3
d #=> 4
e #=> 5
f #=> {:a=>6}
g #=> 7
同样,您只需要记住括号只是表示 "decompose the array in this position to its constituent elements"。
我 运行 在阅读 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 语句是什么样的?
规则很基本:每个普查员都有一个“签名”。例如。它产生两个参数,那么要传递的过程应该期望接收两个参数:
[1,2,3].each_with_index { |o, i| ...}
当对象可能被展开时,比如hash item,可以使用括号展开。假设迭代器产生一个数组,[*arr]
-like 操作是允许的。
以下示例可能会对此有所启发:
[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
您的问题仅与块和块变量无关。相反,它涉及 "disambiguating" 数组的规则。
让我们考虑一下你的例子:
[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
视为 "compound enumerator",但它只是一个枚举数。
你看到 enum1
有三个元素。这些元素通过 Enumerator#each 传递给块。第一个是:
enum1.first
#=> [[1, {}], 0]
如果我们只有一个块变量,比如 a
,那么
a #=> [[1, {}], 0]
我们可以使用 "disambiguation" 以不同的方式分解它。例如,我们可以这样写:
a,b = [[1, {}], 0]
a #=> [1, {}]
b #=> 0
现在让我们找出所有元素:
a,b,c = [[1, {}], 0]
a #=> [1, {}]
b #=> 0
c #=> nil
糟糕!那不是我们想要的。我们刚刚经历了 "disambiguate" 中的 "ambiguous"。我们需要这样写,以便我们的意图明确。我们通过添加括号来做到这一点。通过这样做,您在告诉 Ruby、"decompose the array in this position to its constituent elements"。我们有:
(a,b),c = [[1, {}], 0]
a #=> 1
b #=> {}
c #=> 0
消除歧义非常有用。例如,假设一个方法返回数组:
[[1,[2,3],[[4,5],{a: 6}]],7]
并且我们希望提取所有的单个值。我们可以这样做:
(a,(b,c),((d,e),f)),g = [[1,[2,3],[[4,5],{a: 6}]],7]
a #=> 1
b #=> 2
c #=> 3
d #=> 4
e #=> 5
f #=> {:a=>6}
g #=> 7
同样,您只需要记住括号只是表示 "decompose the array in this position to its constituent elements"。