Ответ следует из того, как реализовано «параллельное присваивание» в Ruby.
Как вы, наверное, знаете:
a,b,c = 1,2,3
a #=> 1
b #=> 2
c #=> 3
a,b,c = [1,2,3]
a #=> 1
b #=> 2
c #=> 3
a,b = [1,2,3]
a #=> 1
b #=> 2
a,*b = [1,2,3]
a #=> 1
b #=> [2, 3]
*a,b = [1,2,3]
a #=> [1, 2]
b #=> 3
a,(b,c) = [1,[2,3]]
a #=> 1
b #=> 2
c #=> 3
a,(b,(c,d)) = [1,[2,[3,4]]]
a #=> 1
b #=> 2
c #=> 3
d #=> 4
В последних двух примерах используется «устранение неоднозначности», которое некоторые люди предпочитают называть «разложением».
Теперь давайте посмотрим, как это применимо к присвоению значений блочным переменным.
Предполагать:
arr = [["studies", {:freq=>11, :cap_freq=>0, :value=>11}],
["theory", {:freq=>9, :cap_freq=>1, :value=>11}]]
и выполняем:
arr.each { |a| p a }
["studies", {:freq=>11, :cap_freq=>0, :value=>11}]
["theory", {:freq=>9, :cap_freq=>1, :value=>11}]
Давайте посмотрим на это более внимательно. Определять:
enum = arr.each
#=> #<Enumerator: [["studies", {:freq=>11, :cap_freq=>0, :value=>11}],
# ["theory", {:freq=>9, :cap_freq=>1, :value=>11}]]:each>
Первый элемент передается в блок и присваивается переменной блока v
:
v = enum.next
#=> ["studies", {:freq=>11, :cap_freq=>0, :value=>11}]
Мы можем предпочесть использовать параллельное присваивание с двумя блочными переменными (после enum.rewind
для сброса перечислителя):
a,h = enum.next
a #=> "studies"
h #=> {:freq=>11, :cap_freq=>0, :value=>11}
Это позволяет нам написать (например):
arr.each { |a,h| p h }
{:freq=>11, :cap_freq=>0, :value=>11}
{:freq=>9, :cap_freq=>1, :value=>11}
Здесь мы не используем блочную переменную a
. В этом случае мы можем заменить ее локальной переменной _
или, возможно, _a
:
arr.each { |_,h| p h }
arr.each { |_a,h| p h }
Это обращает внимание на то, что a
не используется и может помочь избежать ошибок. Что касается ошибок, предположим, что мы хотим:
[[1,2],[3,4]].map { |a,b| puts 1+b }
#=> [3,5]
но ненароком напишу:
[[1,2],[3,4]].map { |a,b| puts a+b }
#=> [3,7]
который выполняется просто отлично (но дает неверный результат). Напротив,
[[1,2],[3,4]].map { |_,b| puts a+b }
#NameError: undefined local variable or method 'a'
говорит нам, что есть проблема.
Вот более сложный пример того, что вы можете делать в блоках с параллельным присваиванием и устранением неоднозначности. Данный:
h = { :a=>[1,2], :b=>[3,4] }
предположим, что мы хотим получить:
{ :a=>3, :b=>7 }
Один из способов следующий:
h.each_with_object({}) { |(a,(b,c)),g| g[a] = b+c }
=> {:a=>3, :b=>7}
person
Cary Swoveland
schedule
02.08.2015
Hash#each
также дает только один элемент (это контрактeach
, в конце концов, он всегда дает ровно один элемент), массив с двумя элементами, ровно как в вашем случае. Тем не менее каким-то образом, несмотря на то, что эти два случая идентичны, вас смущает один, а не другой. - person Jörg W Mittag   schedule 02.08.2015hash#each
, похоже, ожидает/выдает два элемента: ключ и значение (each {| key, value | block } → hsh
). Это не идентичноeach { |item| block } → ary
, который, кажется, дает только один элемент. Конечно, отсутствие у меня опыта и знаний как разработчика может быть причиной непонимания вашей точки зрения. - person Flip   schedule 03.08.2015