ブロックとイテレータ
コンテナ: 1つ以上の他のオブジェクトのリファレンスを保持するオブジェクトのことです。
コードブロック: ブレースまたは do ... end で囲まれたコードのかたまりです。 一般的に、1行のブロックにはブレースを、複数行のブロックには do/end を使います。
例:
| { puts “Hi” } | # 1行のブロック ※{...} の優先度が高い  | 
|
| do | # 複数行のブロック ※do...end は {...} よりも 結合度が弱くなります。  | 
|
| ...... ......  | 
||
| end | ||
greet メソッド呼び出しに関連付けをしてみます。
| greet { puts “Hi” } | 
メソッドの引数がある場合、メソッドの直後・ブロックの直前に追加します。
| verbose_greet(”Sam”, “dearest hubby”) { puts “Hi” } | 
自分なりに yield 文でその関連付けを表してみます。(これで良いかどうかちょっと不安ですが・・・)
| def verbose_greet (name, relationship) | |
| yield puts “#{name}, my #{relationship} ”  | 
|
| end | |
| 出力結果: | |
| Hi Sam, my dearest hubby  | 
|
yieldが実行されるたびに、ブロック内のコードが呼び出されます。ブロックを抜けると、yield文の直後に制御が戻ります。
yield にパラメータを指定すると、それがブロックに渡されます。ブロック内では、これらのパラメータを受け取るための引数名のリストを縦棒 ( | ) の間に指定します。
| def call_block | |
| yield (”hello”, 3) | |
| end ↓ ↓ | |
| call_block{ |str, num| ... } | |
特定の値までのフィボナッチ数列を出力する簡単な関数を例にします。
| def fib_up_to(max) | 
||
| j1, j2 = 1, 1  # 多重代入 ( j1 = 1 and j2 = 1 ) | 
||
| while j1 <= max | 
||
| yield j1 | 
||
| j1, j2  = j2, j1+j2 | 
||
| end | 
||
| end | 
||
| fib_up_to(1000) { |f| print f, ” ” } | 
||
| 出力結果: | ||
| 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 | ||
コードブロックは、イテレータを実装するために使われています。
イテレータ: 配列などのコレクションから連続する要素を返すメソッドのことです。
| animals = %( ant bee cat dog elk ) | # 配列を作成する | |
| animals.each { |animal| puts animal } | # 配列の要素を繰り返し処理する | |
| 出力結果: | ||
| ant bee cat dog elk  | 
||
その他の例:
| [ ‘cat’, ‘dog’, ‘elk’ ].each { |name| print name, ” ” } | 
→ | cat dog elk | 
| 3.times { print “*” } | 
→ | *** | 
| 2.upto(5) { |i| print i } | 
→ | 2345 | 
| ( ‘v’..’z’ ).each { |char| print char } | 
→ | vwxyz | 
ブロックはメソッドに値を返すこともできます。ブロック内で最後に評価された式の値が、yieldの値としてメソッドに返されます。(Arrayクラスのfindメソッドはそんな感じです。)
|  class Array | 
|||
| def find | 
|||
| for i in 0 ... size | 
|||
| value = self[i] return value if yield(value)  | 
|||
| end | 
|||
| return nil | 
|||
| end | 
|||
| end | 
|||
| [1, 3, 5, 7, 9].find { |x| x*x > 30 } → 7 | 
|||
その他の例:
| [ ‘I’, ‘Q’ ].collect { |x| x.succ } | → | [ ‘J’, ‘R’ ] | 
| [1,3,5,7].inject(0) { |sum, element| sum+element } | → | 
16 | 
| [1,3,5,7].inject(1) { |prod, element| prod*element } | → | 
105 | 
| injectを引数なしで呼び出すと、コレクションの先頭要素が初期値として使われ、繰り返しは2番目の要素から開始されます。 | ||
| [1,3,5,7].inject { |sum, element| sum+element } | → | 16 | 
| [1,3,5,7].inject { |prod, element| prod*element } | → | 105 | 

コメント