erg/doc/JA/syntax/18_iterator.md
2024-05-18 18:42:41 +09:00

3.9 KiB

イテレータ

badge

イテレータは、コンテナの要素を取り出すためのオブジェクトです。

for! 0..9, i =>
    print! i

このコードは0から9までの数字を出力します。 それぞれの数字(=Intオブジェクト)はiに代入され、=>以下の動作(=print! i)が実行されます。このような繰り返し実行のことを イテレーション といいます。

ではここでfor!プロシージャの型シグネチャを見てみましょう。

for!: |T: Type, I <: Iterable T| (I, T => None) => None

第一引数はIterableという型のオブジェクトを受け付けるようです。

Iterable.Iterator属性, .iterメソッドを要求メソッドに持つ型です。

Iterable T = Trait {
    .Iterator = {Iterator}
    .iter = (self: Self) -> Self.Iterator T
}

.Iterator属性の型{Iterator}はいわゆるセットカインド(カインドの説明はこちら)です。

assert [1, 2, 3] in Iterable(Int)
assert 1..3 in Iterable(Int)
assert [1, 2, 3].Iterator == ListIterator
assert (1..3).Iterator == RangeIterator

log [1, 2, 3].iter() # <ListIterator object>
log (1..3).iter() # <RangeIterator object>

ListIteratorRangeIteratorはどちらもIteratorを実装するクラスで、List, Rangeにイテレーション機能を与えるためだけに存在します。 このようなデザインパターンをコンパニオンクラス1と呼びます。 そしてIteratorImplパッチがイテレーション機能のコアです。Iterator.nextメソッド1つだけを要求し、IteratorImplは実に数十個のメソッドを提供します。ListIteratorRangeIterator.nextメソッドを実装するだけでIteratorImplの実装メソッドを使うことができるわけです。この利便性から、標準ライブラリでは多数のイテレータが実装されています。

classDiagram
    class List~T~ {
        ...
        iter() ListIterator~T~
    }
    class Range~T~ {
        ...
        iter() RangeIterator~T~
    }
    class Iterable~T~ {
        <<trait>>
        iter() Iterator~T~
    }
    Iterable~T~ <|.. List~T~: Impl
    Iterable~T~ <|.. Range~T~: Impl
    class ListIterator~T~ {
        array: List~T~
        next() T
    }
    class RangeIterator~T~ {
        range: Range~T~
        next() T
    }
    class Iterator~T~ {
        <<trait>>
        next() T
    }
    Iterator~T~ <|.. ListIterator~T~: Impl
    Iterator~T~ <|.. RangeIterator~T~: Impl

    List <-- ListIterator
    Range <-- RangeIterator

Iterableのような、トレイト(この場合はIterator)を静的ディスパッチでありながら統一的に扱えるインターフェースを提供する型をコンパニオンクラスアダプターと呼びます。


1 このパターンには統一された名前がないようであるが、Rustではcompanion struct patternと呼ばれており、それになぞらえて命名した。

Previous | Next