銀色うつ時間

思い出すたび何か胸につっかえてるだけ

Iteratorパターンの雑感とRubyでの実装

メモ

  • Iteratorパターンとは、内部表現を公開することなくアグリゲーションオブジェクトの要素に順次アクセスする方法を示した設計
  • Iteratorによる実装を行えば、複数のアグリゲーションオブジェクトの要素を走査する場合に透過的に扱うことができる。これによってその他のメソッド多態性のある機能として追加できる
  • 大事なのは、要素間のトラバースを行う役割をアグリゲーションオブジェクトではなくイテレータに移譲することで、これによりアグリゲーションオブジェクトは反復処理ではなく、本来の役割(オブジェクトのコレクション管理)に専念できるようになる
  • 内部イテレータと外部イテレータ   - 外部イテレータはクライアント側がnext()を呼び出して、次の要素への処理を決定する
    • 内部イテレータイテレータ自身が次の要素に対する処理を決定する。クライアントが反復処理を制御しないため、柔軟性に欠けるとも言えるが、反復処理を命令するだけで内部イテレータに全ての処理を行わせることが可能とも言い換えられるので、こちらの方が利便性が高いとも言われる
  • 上記の多態性のある機能とは、Iteratorを引数に取るメソッドのことである。Iteratorを活用することでコレクションの実装を考えることなく特定の振る舞いを実装できる。以下の様なもの
public void print(Iterator iterator) {
    while(iterator.hasNext()) {
        System.out.println(iterator.next());
    }
}
  • アグリゲーションオブジェクトを扱うクライアントは、アグリゲーションオブジェクトの具象クラスではなくインターフェースを参照して各アグリゲーションオブジェクトを利用するようにすると、クライアントと疎結合にできる
  • JavaのHashTable等のコレクションをIteratorで扱うとどうなるか
    • 順番を担保するものではない
    • あくまで「値に対する走査」なので、値を取得してからIteratorを生成する必要がある
  • Java.Util.Iteratorインターフェースを用いればよいのか
    • ケースバイケース
    • ArrayListはデフォルトでサポートされているので不要
  • Java.Util.Itreatorインターフェースのremove()どうするの。。。
    • 謎い。いみゅーたぼーにしたい場合はどう扱えばいいのか。

Rubyによる実装

# 内部イテレータ
# コードブロックの利用によって集約オブジェクトにロジックを伝える
# 繰り返し処理の全てが集約オブジェクト内で起こるため、内部イテレータと呼ぶ
p '内部イテレータ'
array = %w(red blue green)
array.each do |val|
  p val
end

# 外部イテレータ
# 外部機能として渡すため柔軟性に富む
# 複数コレクションから一つずつ要素を取り出して並行に処理するようなことも可能
p '外部イテレータ'
class ArrayIterator
  def initialize(array)
    @array = array
    @index = 0
  end

  def has_next?
    @index < @array.length
  end

  def item
    @array[@index]
  end

  def next_item
    value = @array[@index]
    @index += 1
    value
  end
end

# 配列を走査する
i = ArrayIterator.new(array)
while i.has_next?
  puts("item: #{i.next_item}")
end
# 文字列を走査する
i = ArrayIterator.new('abc')
while i.has_next?
  puts("item: #{i.next_item}")
end

# 平行させる
# こういうのは外部イテレータの方が容易
def merge(array1, array2)
  merged = []
  iterator1 = ArrayIterator.new(array1)
  iterator2 = ArrayIterator.new(array2)

  while iterator1.has_next? && iterator2.has_next?
    if iterator1.item < iterator2.item
      merged << iterator1.next_item
    else
      merged << iterator2.next_item
    end
  end

  set_merged(iterator1, merged)
  set_merged(iterator2, merged)

  merged
end

def set_merged(iterator, merged)
  while iterator.has_next?
    merged << iterator.next_item
  end
end

array1 = [10, 50, 100]
array2 = [15, 45, 95]
merged = merge(array1, array2)
merged.each do |val|
  p val
end