バリケンのRuby日記 RSSフィード

2006-05-20

[] Rubyの「イテレータ」再考  Rubyの「イテレータ」再考 - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  Rubyの「イテレータ」再考 - バリケンのRuby日記  Rubyの「イテレータ」再考 - バリケンのRuby日記 のブックマークコメント

以前Rubyで「イテレータ」というエントリを書いたけど、もういちど復習するよ。

「手続きオブジェクト」の使い道として、おとといの「無名関数」昨日の「クロージャ」のほかに、「イテレータの定義」というものがあるよ。

メソッド定義中に「ブロックを指定せずにProc.newで手続きオブジェクトを生成」するようにメソッドを定義すると、そのメソッドを実行するときにブロックを与える「ブロック付きメソッド」として定義することが出来るよ。

じゃあ、Arrayクラス継承したMyArrayクラスに、配列の各要素に「与えたブロックの手続きを実行する」ブロック付きメソッドmy_iteratorを定義してみるよ。

class MyArray < Array

  def my_iterator
    i = 0
    pr = Proc.new
    while i < self.size
      pr.call self[i]
      i += 1
    end
  end
end

fruits = MyArray.new

fruits.push('りんご')
fruits.push('みかん')
fruits.push('いちご')

fruits.my_iterator {|fruit| puts fruit }

実行結果だよ。

りんご
みかん
いちご

配列の各要素に対して、与えられたブロックが実行されていくのがわかるよね。

でもpr = Proc.newとしたあとにループの中でpr.callとするのはちょっとカッコ悪いよね。この「まず最初に手続きオブジェクトを生成してから、ループ内とかでcallする」という場合、yieldを使って次のように書くことも出来るんだ。

class MyArray < Array

  def another_my_iterator
    i = 0
    while i < self.size
      yield self[i]
      i += 1
    end
  end
end

たぶんProc.newは「手続きオブジェクト」というものの理解を助けるための表現で、いちど「手続きオブジェクト」について理解できたら、次からはProc.newを直接使わないでyieldを使うようにするとRubyistっぽくてちょっとカッコイイかも。

ついでに言うと、クロージャを作りたいときはproc、さらに無名関数であることを強調したいときはlambda、と使い分けるともっとカッコイイかも。

余談1:

配列のように「複数の要素からなるもの」に対して、その各要素に一連の手続きを繰り返し実行することを「イテレータ」と言うみたいだよ。すべての「ブロック付きメソッド」を「イテレータ」と呼んでいることが多いけど、混乱の元だからぼくはお勧めしないよ。「イテレータ」は「繰り返し」をしているときにだけ使って、繰り返さないときは単に「ブロック付きメソッド」と言うほうが混乱しないと思うよ。

余談2:

ちなみにArray#pushは「<<」とも書けるよ。

fruits << 'りんご'
fruits << 'みかん'
fruits << 'いちご'

次のようにも書けるよ。

fruits << 'りんご' << 'みかん' << 'いちご'