mapなどによる無駄な処理
[Int]
である配列の要素に1を加えて2を掛け最初の要素を取り出してみます。
処理の流れを見るために出力を付け加えています。
func add(_ i: Int) -> (Int) -> Int {
return {
print("<<", $0)
return $0 + i
}
}
func multiply(_ i: Int) -> (Int) -> Int {
return {
print($0 * i, ">>")
return $0 * i
}
}
let re = Array(0..<5).map(add(1)).map(multiply(2)).first
これを実行すると
<< 0
<< 1
<< 2
<< 3
<< 4
2 >>
4 >>
6 >>
8 >>
10 >>
と出力され、reには2が代入されます。
最初のmap
で全ての要素に1を加え、続くmap
で全ての要素に2をかけ、その後、最初の要素を取り出しています。
必要な値は
let re = (0 + 1) * 2
だけで計算可能であるのに、無意味な計算が多数行われます。
これを解決するのがLazySequence
/LazyCollection
とそのサブタイプです。
遅延評価
先ほどのプログラムにちょっと追加をします。
let re = Array(0..<5).lazy.map(add(1)).map(multiply(2)).first
lazy
を追加しただけです。
これを実行すると
<< 0
2 >>
と出力され、reには2が代入されます。
最初の要素、つまり
let re = (0 + 1) * 2
だけが計算されているのがわかります。
これはつまり、map
に渡された関数の実行はその場ではされず、実際に値が必要になった時必要な要素のみに、この場合はfirst
が呼ばれた時に必要である最初の要素に対してのみ関数が実行されているのです。
このように、実際の値が必要になるまで、map
などに与えられる関数の実行を遅延させるのが遅延評価であり、Swiftにおいてその役割を担っているのがLazySequence
/LazyCollection
とそのサブタイプです。
いつ実行されるの?
LazySequence
/LazyCollection
とそのサブタイプ自身はあまり覚える必要はありませんが、それらに対して、何をした時に何が起こるのかというのは重要です。
遅延評価させるつもりで書いたものが、遅延評価になっていなかったというのが一番問題なのです。
ということで、未完成ですが、僕が現在調べた結果を記しておきます。
ハイライトされて読みやすくなるようにメソッドにはfunc
をプロパティにはvar
をつけています。
実行されない
func map(_:)
func flatMap(_:)
func filter(_:)
func enumerated()
func reversed()
func prefix(_:)
func prefix(through:)
func prefix(upTo:)
func prefix(while:)
func suffix(_:)
func suffix(from:)
func dropFirst()
func dropFirst(_:)
func dropLast()
func dropLast(_:)
func joined()
該当箇所のみ実行される
var first
var last
条件が満たされるまで/満たされている間実行される
func first(where:)
func contains()
func index(where:)
var isEmpty
全て実行される
// 通常の配列などに変換する
let array = Array(lazySequence)
func reduce(_:,_:)
func sorted()
func sorted(by:)
func joined(separator:)
未完成
未完成なので、ちょっとつづ追加していきたい。