GeneratorとSequence
GeneratorType
generatorとは新しい要素を返す処理のことで、以下のGeneratorTypeプロトコルに従う。
protocol GeneratorType {
typealias Element
func next() -> Element?
}
例えば、文字列を各行を返すgeneratorはこんな感じで実装する。
class LineGenerator: GeneratorType {
typealias Element = String
var lines: [String]
init(text: String) {
self.lines = text.componentsSeparatedByString("\n")
}
func next() -> Element? {
return lines.isEmpty ? nil : lines.removeAtIndex(0)
}
}
let text = "いろはにほへと ちりぬるを\nわかよたれそ つねならむ\nういのおくやま けふこえて\nあさきゆめみし よひもせず"
let generator1 = LineGenerator(text: text)
generator1.next() //=> いろはにほへと ちりぬるを
generator1.next() //=> わかよたれそ つねならむ
generator1.next() //=> ういのおくやま けふこえて
generator1.next() //=> あさきゆめみし よひもせず
generator1.next() //=> nil
GeneratorOf
generatorを書くとき上のようにクラスを定義するのが面倒なら、GeneratorOf<T>構造体が便利。以下のような感じで定義されている。
struct GeneratorOf<T>: GeneratorType, SequenceType {
init(_ nextElement: () -> T?)
mutating func next() -> T?
}
GeneratorOf構造体はGeneratorTypeプロトコルに従っており、初期化時に渡されたクロージャをnext()で実行するようになっている。なので、上のコードは以下のように書き直せる。
func lineGenerator(#text: String) -> GeneratorOf<String> {
var lines = text.componentsSeparatedByString("\n")
return GeneratorOf { return lines.isEmpty ? nil : lines.removeAtIndex(0) }
}
var generator2 = lineGenerator(text: text)
generator2.next() //=> いろはにほへと ちりぬるを
generator2.next() //=> わかよたれそ つねならむ
generator2.next() //=> ういのおくやま けふこえて
generator2.next() //=> あさきゆめみし よひもせず
generator2.next() //=> nil
SequenceType
sequenceはループによって中身の要素を走査できる構造のことで、以下のSequenceTypeプロトコルに従う。
protocol SequenceType {
typealias Generator: GeneratorType
func generate() -> Generator
}
sequenceはgeneratorをgenerate()で生成して、それを使って中身の要素に順にアクセスする。SequenceTypeプロトコルに従うオブジェクトはfor-inループに渡すことができる。
class LineSequence: SequenceType {
typealias Generator = LineGenerator
var text: String
init(text: String) {
self.text = text
}
func generate() -> Generator {
return LineGenerator(text: text)
}
}
for line in LineSequence(text: text) {
print(line)
}
SequenceOf
GeneratorOfと同様にSequenceOfも存在する。
struct SequenceOf<T>: SequenceType {
init<G: GeneratorType where T == T>(_ makeUnderlyingGenerator: () -> G)
func generate() -> GeneratorOf<T>
}
func lineSequence(#text: String) -> SequenceOf<String> {
return SequenceOf { return lineGenerator(text: text) }
}
for line in lineSequence(text: text) {
print(line)
}