Ruby

eachメソッドでブロックパラメータを複数取れる場合

More than 3 years have passed since last update.

ふと気付いた

先日こういう記事を書きました。
Ruby - ブロックパラメータを複数取るEnumerableメソッドのあとにEnumeratorメソッドを重ねたときのブロックパラメータの指定方法(余談としてブロックローカル変数の宣言のこと) - Qiita

もしや、eachでも複数のブロックパラメータが取れる?

やってみた

配列の各要素がStringではなく2要素のArray。
ブロックパラメータを2つ指定すれば、それぞれArray要素が代入されるはず。

pry(main)> num = (0..9).to_a
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
pry(main)> pronoun = %w[zero one two three four five six seven eight nine]
=> ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"]
pry(main)> num_pronoun = num.zip(pronoun)
=> [[0, "zero"],
 [1, "one"],
 [2, "two"],
 [3, "three"],
 [4, "four"],
 [5, "five"],
 [6, "six"],
 [7, "seven"],
 [8, "eight"],
 [9, "nine"]]
pry(main)> num_pronoun.each{ |i, word| puts "#{i}: #{word}" };
0: zero
1: one
2: two
3: three
4: four
5: five
6: six
7: seven
8: eight
9: nine

うまくいきました^_^

配列がフラットだと(念のため確認)

pry(main)> num_pronoun_flat = num_pronoun.flatten
=> [0, "zero", 1, "one", 2, "two", 3, "three", 4, "four", 5, "five", 6, "six", 7, "seven", 8, "eight", 9, "nine"]
pry(main)> num_pronoun_flat.each{ |i, word| puts "#{i}: #{word}" };
0: 
zero: 
1: 
one: 
2: 
two: 
3: 
three: 
4: 
four: 
5: 
five: 
6: 
six: 
7: 
seven: 
8: 
eight: 
9: 
nine: 

つまりiに値が入ってwordに入らなかった。
Enumerator#eachは要素を一つずつ取り出すので、当然の結果でしたね。配列やハッシュなど、オブジェクトとして括られている必要があるわけで。

ということで

いままでは、いちいち

num_pronoun.each do |i_word|
  i, word = i_word
  puts "#{i}: #{word}" 
end

というように、ブロックパラメータからブロック内の変数に代入していたのですが、この手間を省いて

num_pronoun.each do |i, word| 
  puts "#{i}: #{word}" 
end

とできる。ありがたいです。

複雑な要素のeach

そういえば、上記した記事では、丸括弧で括れば要素の中の要素が取得できましたね。
ということで、試してみましょう。

[数字, [英語, 漢数字]]

pry(main)> eng = %w[zero one two three four five six seven eight nine]
=> ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"]
pry(main)> kanji = %w[零 一 二 三 四 五 六 七 八 九]
=> ["零", "一", "二", "三", "四", "五", "六", "七", "八", "九"]
pry(main)> lang = eng.zip(kanji)
=> [["zero", "零"],
 ["one", "一"],
 ["two", "二"],
 ["three", "三"],
 ["four", "四"],
 ["five", "五"],
 ["six", "六"],
 ["seven", "七"],
 ["eight", "八"],
 ["nine", "九"]]
pry(main)> num_lang = num.zip(lang)
=> [[0, ["zero", "零"]],
 [1, ["one", "一"]],
 [2, ["two", "二"]],
 [3, ["three", "三"]],
 [4, ["four", "四"]],
 [5, ["five", "五"]],
 [6, ["six", "六"]],
 [7, ["seven", "七"]],
 [8, ["eight", "八"]],
 [9, ["nine", "九"]]]

これで一要素を
[数字, [英語, 漢数字]]
という入れ子構造にできました。

|i, (eng, kanji)|

ではeachのブロックパラメータを|i, (eng, kanji)|としてみましょう。

pry(main)> num_lang.each{ |i, (eng, kanji)| puts "#{i}: #{eng} / #{kanji}" };
0: zero / 
1: one / 
2: two / 
3: three / 
4: four / 
5: five / 
6: six / 
7: seven / 
8: eight / 
9: nine / 
  • i: 数字
  • eng: 英語
  • kanji: 漢数字

と指定できていますね。

|i, eng, kanji|

では続いて|i, eng, kanji|で指定しましょう。

pry(main)> num_lang.each{ |i, eng, kanji| puts "#{i}: #{eng} / #{kanji}" };
0: ["zero", "零"] / 
1: ["one", "一"] / 
2: ["two", "二"] / 
3: ["three", "三"] / 
4: ["four", "四"] / 
5: ["five", "五"] / 
6: ["six", "六"] / 
7: ["seven", "七"] / 
8: ["eight", "八"] / 
9: ["nine", "九"] / 

ということで

  • i: 数字
  • eng: [英語, 漢数字]
  • kanji: nil

と解釈されてしまっています。

{ 数字 => [英語, 漢数字] }

続いて、要素の中にハッシュを組み合わせた場合を見てみます。
(ほぼ同じになるでしょうが)

前準備

pry(main)> num_to_lang_pair = num_lang.inject({}){ |hash, (i, a)| hash[i] = a; hash }
=> {0=>["zero", "零"],
 1=>["one", "一"],
 2=>["two", "二"],
 3=>["three", "三"],
 4=>["four", "四"],
 5=>["five", "五"],
 6=>["six", "六"],
 7=>["seven", "七"],
 8=>["eight", "八"],
 9=>["nine", "九"]}

|i, (eng, kanji)|

pry(main)> num_to_lang_pair.each{ |i, (eng, kanji)| puts "#{i}: #{eng} / #{kanji}" };
0: zero / 
1: one / 
2: two / 
3: three / 
4: four / 
5: five / 
6: six / 
7: seven / 
8: eight / 
9: nine / 

|i, eng, kanji|

pry(main)> num_to_lang_pair.each{ |i, eng, kanji| puts "#{i}: #{eng} / #{kanji}" };
0: ["zero", "零"] / 
1: ["one", "一"] / 
2: ["two", "二"] / 
3: ["three", "三"] / 
4: ["four", "四"] / 
5: ["five", "五"] / 
6: ["six", "六"] / 
7: ["seven", "七"] / 
8: ["eight", "八"] / 
9: ["nine", "九"] / 

[数字, [英語, 漢数字]]の場合と同じでしたね。

結論

丸括弧を適切に使うことで、希望通りにブロックパラメータを取得できますね。
すごいぞRubyインタプリタ☆