[1, 2, 3, -1, -2].filter({ $0 > 0 }).count // => 3
[1, 2, 3, -1, -2].lazy.filter({ $0 > 0 }).count // => 3
2番目のステートメントにlazy
を追加する利点は何ですか。私の理解によると、lazy
変数が使用されると、メモリは使用時にその変数に初期化されます。この文脈ではどのように意味がありますか?
LazySequence
の使用についてもう少し詳しく理解しようとしています。シーケンスでmap
、reduce
、filter
関数を使用しましたが、lazy
シーケンスでは使用していません。これを使用する理由を理解する必要がありますか?
lazy
は、配列の処理方法を変更します。 lazy
を使用しない場合、filter
は配列全体を処理し、結果を新しい配列に格納します。 lazy
を使用すると、シーケンスまたはコレクションの値が下流の関数から生成されますオンデマンド。値は配列に格納されません。それらは必要なときに生産されます。
私がreduce
の代わりにcount
を使用したこの変更された例を考えてください。
lazy
:を使用しない
この場合、何かがカウントされる前に、すべてのアイテムが最初にフィルタリングされます。
[1, 2, 3, -1, -2].filter({ print("filtered one"); return $0 > 0 })
.reduce(0) { (total, elem) -> Int in print("counted one"); return total + 1 }
filtered one filtered one filtered one filtered one filtered one counted one counted one counted one
lazy
:の使用
この場合、reduce
はカウントするアイテムを要求しており、filter
はアイテムが見つかるまで機能し、次にreduce
は別のアイテムを要求し、filter
別が見つかるまで機能します。
[1, 2, 3, -1, -2].lazy.filter({ print("filtered one"); return $0 > 0 })
.reduce(0) { (total, elem) -> Int in print("counted one"); return total + 1 }
filtered one counted one filtered one counted one filtered one counted one filtered one filtered one
lazy
:を使用する場合
option-lazy
をクリックすると、この説明が表示されます。
lazy
のDiscussionから:
操作をチェーンする場合は、遅延プロパティを使用します。
中間操作がストレージを割り当てないようにする
または
不要な計算を避けるために最終的なコレクションの一部のみが必要な場合
3番目を追加します。
ダウンストリームプロセスをより早く開始し、アップストリームプロセスがすべての作業を最初に実行するのを待つ必要がない場合
したがって、たとえば、最初の正のlazy
を検索する場合は、filter
の前にInt
を使用することをお勧めします。 filter
を使用すると、配列全体をフィルター処理する必要がなくなり、フィルター処理された配列にスペースを割り当てる必要がなくなります。
3番目の点として、filter
を使用して1...10_000_000
の範囲の素数を表示するプログラムがあるとします。素数を表示する前にすべてを計算するのを待つ必要があるよりも、素数を見つけたときに表示するほうがよいでしょう。
これまで見たことがないので、探して見つけました。
投稿した構文は遅延コレクションを作成します。遅延コレクションでは、コードの各ステップで一連の中間配列全体を作成する必要がありません。フィルターステートメントしかない場合はそれほど関係ありません。filter.map.map.filter.map
、遅延コレクションがないため、各ステップで新しい配列が作成されます。
詳細については、この記事を参照してください。
https://medium.com/developermind/lightning-read-1-lazy-collections-in-Swift-fa997564c1a
私はいくつかのベンチマークを行いましたが、マップやフィルターなどの一連の高次関数は、「通常の」コレクションよりも遅延コレクションの方が実際には少し遅いです。
レイジーコレクションでは、パフォーマンスが少し低下しますが、メモリフットプリントが小さくなります。
@discardableResult func timeTest() -> Double {
let start = Date()
let array = 1...1000000
let random = array
.map { (value) -> UInt32 in
let random = arc4random_uniform(100)
//print("Mapping", value, "to random val \(random)")
return random
}
let result = random.lazy //Remove the .lazy here to compare
.filter {
let result = $0 % 100 == 0
//print(" Testing \($0) < 50", result)
return result
}
.map { (val: UInt32) -> NSNumber in
//print(" Mapping", val, "to NSNumber")
return NSNumber(value: val)
}
.compactMap { (number) -> String? in
//print(" Mapping", number, "to String")
return formatter.string(from: number)
}
.sorted { (lhv, rhv) -> Bool in
//print(" Sorting strings")
return (lhv.compare(rhv, options: .numeric) == .orderedAscending)
}
let elapsed = Date().timeIntervalSince(start)
print("Completed in", String(format: "%0.3f", elapsed), "seconds. count = \(result.count)")
return elapsed
}
上記のコードで、行を変更すると
let result = random.lazy //Remove the .lazy here to compare
に
let result = random //Removes the .lazy here
その後、より速く実行されます。 lazyを使用すると、.lazyコレクションを使用すると、直線配列と比較して、約1.5倍の時間がかかります。