From 4bba172eec8342a8bb49aedf06481a7013a248b8 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Fri, 9 Oct 2020 18:18:13 +0100
Subject: [PATCH 01/45] Add scan function
---
Guides/Scan.md | 53 +++++++++++++++++
Sources/Algorithms/Scan.swift | 67 ++++++++++++++++++++++
Tests/SwiftAlgorithmsTests/ScanTests.swift | 20 +++++++
3 files changed, 140 insertions(+)
create mode 100644 Guides/Scan.md
create mode 100644 Sources/Algorithms/Scan.swift
create mode 100644 Tests/SwiftAlgorithmsTests/ScanTests.swift
diff --git a/Guides/Scan.md b/Guides/Scan.md
new file mode 100644
index 00000000..97098ee8
--- /dev/null
+++ b/Guides/Scan.md
@@ -0,0 +1,53 @@
+# Scan
+
+[[Source](https://github.com/apple/swift-algorithms/blob/main/Sources/Algorithms/Scan.swift) |
+ [Tests](https://github.com/apple/swift-algorithms/blob/main/Tests/SwiftAlgorithmsTests/ScanTests.swift)]
+
+Produces a sequence of values.
+
+This has the behaviour of reduce, but instead of returning the final result
+value, it returns the a sequence of the results returned from each element.
+
+```swift
+let accumulation = (1...5).scan(0, +)
+print(Array(accumulation))
+// prints [1, 3, 6, 10, 15]
+
+let runningMinimum = [3, 4, 2, 3, 1].scan(.max, min)
+print(Array(runningMinimum))
+// prints [3, 3, 2, 2, 1]
+```
+
+## Detailed Design
+
+One new method is added to sequences:
+
+```swift
+extension Sequence {
+ func scan(
+ _ initial: Result,
+ _ transform: @escaping (Result, Element) -> Result
+ ) -> Scan
+}
+```
+
+### Complexity
+
+Calling these methods is O(_1_).
+
+### Naming
+
+
+
+### Comparison with other langauges
+
+**C++:** As of C++17, the `` library includes an `inclusive_scan` function.
+
+**Haskell:** Haskell includes a `scan` function for its `Traversable` type,
+which is akin to Swift's `Sequence`.
+
+**Python:** Python’s `itertools` includes an `accumulate` method. In version
+3.3, a function paramenter was added. Version 3.8 added the optional initial
+parameter.
+
+**Rust:** Rust provides a `scan` function.
diff --git a/Sources/Algorithms/Scan.swift b/Sources/Algorithms/Scan.swift
new file mode 100644
index 00000000..55a36a70
--- /dev/null
+++ b/Sources/Algorithms/Scan.swift
@@ -0,0 +1,67 @@
+//===----------------------------------------------------------------------===//
+//
+// This source file is part of the Swift Algorithms open source project
+//
+// Copyright (c) 2020 Apple Inc. and the Swift project authors
+// Licensed under Apache License v2.0 with Runtime Library Exception
+//
+// See https://swift.org/LICENSE.txt for license information
+//
+//===----------------------------------------------------------------------===//
+
+/// A sequence of applying a transform to the element of a sequence and the
+/// previously transformed result.
+public struct Scan {
+ let base: Base
+ let initial: Result
+ let transform: (Result, Base.Element) -> Result
+}
+
+extension Scan: Sequence {
+ public struct Iterator: IteratorProtocol {
+ var iterator: Base.Iterator
+ var current: Result
+ let transform: (Result, Base.Element) -> Result
+
+ public mutating func next() -> Result? {
+ guard let element = iterator.next() else { return nil }
+ current = transform(current, element)
+ return current
+ }
+ }
+
+ public func makeIterator() -> Iterator {
+ Iterator(iterator: base.makeIterator(),
+ current: initial,
+ transform: transform)
+ }
+}
+
+extension Sequence {
+
+ /// Returns a sequence containing the results of combining the elements of
+ /// the sequence using the given transform.
+ ///
+ /// This can be seen as applying the reduce function to each element and
+ /// providing these results as a sequence.
+ ///
+ /// ```
+ /// let values = [1, 2, 3, 4]
+ /// let sequence = values.scan(0, +)
+ /// print(Array(sequence))
+ ///
+ /// // prints [1, 3, 6, 10]
+ /// ```
+ ///
+ /// - Parameters:
+ /// - initial: The value to use as the initial accumulating value.
+ /// - transform: A closure that combines an accumulating value and
+ /// an element of the sequence.
+ /// - Returns: A sequence of transformed elements.
+ public func scan(
+ _ initial: Result,
+ _ transform: @escaping (Result, Element) -> Result
+ ) -> Scan {
+ Scan(base: self, initial: initial, transform: transform)
+ }
+}
diff --git a/Tests/SwiftAlgorithmsTests/ScanTests.swift b/Tests/SwiftAlgorithmsTests/ScanTests.swift
new file mode 100644
index 00000000..42db08c2
--- /dev/null
+++ b/Tests/SwiftAlgorithmsTests/ScanTests.swift
@@ -0,0 +1,20 @@
+//===----------------------------------------------------------------------===//
+//
+// This source file is part of the Swift Algorithms open source project
+//
+// Copyright (c) 2020 Apple Inc. and the Swift project authors
+// Licensed under Apache License v2.0 with Runtime Library Exception
+//
+// See https://swift.org/LICENSE.txt for license information
+//
+//===----------------------------------------------------------------------===//
+
+import XCTest
+import Algorithms
+
+final class ScanTests: XCTestCase {
+ func testScan() {
+ XCTAssertEqualSequences((1...5).scan(0, +), [1, 3, 6, 10, 15])
+ XCTAssertEqualSequences([3, 4, 2, 3, 1].scan(.max, min), [3, 3, 2, 2, 1])
+ }
+}
From 548b1e7aed0a3b5e229a3b29ff37b1ea775cd3cc Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Tue, 24 Nov 2020 12:12:47 +0000
Subject: [PATCH 02/45] Add initial conditional conformances to Collection and
BidirectionalCollection
---
Sources/Algorithms/Scan.swift | 36 ++++++++++++++++++++++
Tests/SwiftAlgorithmsTests/ScanTests.swift | 29 +++++++++++++++--
2 files changed, 62 insertions(+), 3 deletions(-)
diff --git a/Sources/Algorithms/Scan.swift b/Sources/Algorithms/Scan.swift
index 55a36a70..a557f47b 100644
--- a/Sources/Algorithms/Scan.swift
+++ b/Sources/Algorithms/Scan.swift
@@ -37,6 +37,42 @@ extension Scan: Sequence {
}
}
+extension Scan: Collection where Base: Collection {
+ public var startIndex: Base.Index {
+ base.startIndex
+ }
+
+ public var endIndex: Base.Index {
+ base.endIndex
+ }
+
+ public subscript(position: Base.Index) -> Result {
+ base[...position].reduce(initial, transform)
+ }
+
+ public func index(after i: Base.Index) -> Base.Index {
+ base.index(after: i)
+ }
+
+ public func index(_ i: Base.Index, offsetBy distance: Int) -> Base.Index {
+ base.index(i, offsetBy: distance)
+ }
+
+ public func index(_ i: Base.Index, offsetBy distance: Int, limitedBy limit: Base.Index) -> Base.Index? {
+ base.index(i, offsetBy: distance, limitedBy: limit)
+ }
+
+ public func distance(from start: Base.Index, to end: Base.Index) -> Int {
+ base.distance(from: start, to: end)
+ }
+}
+
+extension Scan: BidirectionalCollection where Base: BidirectionalCollection {
+ public func index(before i: Base.Index) -> Base.Index {
+ base.index(before: i)
+ }
+}
+
extension Sequence {
/// Returns a sequence containing the results of combining the elements of
diff --git a/Tests/SwiftAlgorithmsTests/ScanTests.swift b/Tests/SwiftAlgorithmsTests/ScanTests.swift
index 42db08c2..800331a8 100644
--- a/Tests/SwiftAlgorithmsTests/ScanTests.swift
+++ b/Tests/SwiftAlgorithmsTests/ScanTests.swift
@@ -13,8 +13,31 @@ import XCTest
import Algorithms
final class ScanTests: XCTestCase {
- func testScan() {
- XCTAssertEqualSequences((1...5).scan(0, +), [1, 3, 6, 10, 15])
- XCTAssertEqualSequences([3, 4, 2, 3, 1].scan(.max, min), [3, 3, 2, 2, 1])
+ func testSequence() {
+ let scan = (1...).prefix(5).scan(0, +)
+ XCTAssertEqualSequences(scan, [1, 3, 6, 10, 15])
+ }
+
+ func testSequenceEmpty() {
+ let scan = (1...).prefix(0).scan(0, +)
+ XCTAssertEqualSequences(scan, [])
+ }
+
+ func testCollection() {
+ let scan = [3, 4, 2, 3, 1].scan(.max, min)
+ XCTAssertEqualSequences(scan, [3, 3, 2, 2, 1])
+ validateIndexTraversals(scan)
+ }
+
+ func testCollectionEmpty() {
+ let scan = EmptyCollection().scan(.max, min)
+ XCTAssertEqualSequences(scan, [])
+ validateIndexTraversals(scan)
+ }
+
+ func testBidirectionalCollection() {
+ let reversed = [1,2,3,4,5].scan(0, +).reversed()
+ XCTAssertEqualSequences(reversed, [15, 10, 6, 3, 1])
+ validateIndexTraversals(reversed)
}
}
From e9edde07a0b73d1a46ceae73ab0a1ac7867feba7 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Tue, 24 Nov 2020 12:15:33 +0000
Subject: [PATCH 03/45] Rename scan to reductions
---
Guides/{Scan.md => Reductions.md} | 14 +++++------
.../{Scan.swift => Reductions.swift} | 16 ++++++-------
...{ScanTests.swift => ReductionsTests.swift} | 24 +++++++++----------
3 files changed, 27 insertions(+), 27 deletions(-)
rename Guides/{Scan.md => Reductions.md} (81%)
rename Sources/Algorithms/{Scan.swift => Reductions.swift} (86%)
rename Tests/SwiftAlgorithmsTests/{ScanTests.swift => ReductionsTests.swift} (54%)
diff --git a/Guides/Scan.md b/Guides/Reductions.md
similarity index 81%
rename from Guides/Scan.md
rename to Guides/Reductions.md
index 97098ee8..3f8cfedb 100644
--- a/Guides/Scan.md
+++ b/Guides/Reductions.md
@@ -1,7 +1,7 @@
-# Scan
+# Reductions
-[[Source](https://github.com/apple/swift-algorithms/blob/main/Sources/Algorithms/Scan.swift) |
- [Tests](https://github.com/apple/swift-algorithms/blob/main/Tests/SwiftAlgorithmsTests/ScanTests.swift)]
+[[Source](https://github.com/apple/swift-algorithms/blob/main/Sources/Algorithms/Reductions.swift) |
+ [Tests](https://github.com/apple/swift-algorithms/blob/main/Tests/SwiftAlgorithmsTests/ReductionsTests.swift)]
Produces a sequence of values.
@@ -9,11 +9,11 @@ This has the behaviour of reduce, but instead of returning the final result
value, it returns the a sequence of the results returned from each element.
```swift
-let accumulation = (1...5).scan(0, +)
+let accumulation = (1...5).reductions(0, +)
print(Array(accumulation))
// prints [1, 3, 6, 10, 15]
-let runningMinimum = [3, 4, 2, 3, 1].scan(.max, min)
+let runningMinimum = [3, 4, 2, 3, 1].reductions(.max, min)
print(Array(runningMinimum))
// prints [3, 3, 2, 2, 1]
```
@@ -24,10 +24,10 @@ One new method is added to sequences:
```swift
extension Sequence {
- func scan(
+ func reductions(
_ initial: Result,
_ transform: @escaping (Result, Element) -> Result
- ) -> Scan
+ ) -> Reductions
}
```
diff --git a/Sources/Algorithms/Scan.swift b/Sources/Algorithms/Reductions.swift
similarity index 86%
rename from Sources/Algorithms/Scan.swift
rename to Sources/Algorithms/Reductions.swift
index a557f47b..bead9650 100644
--- a/Sources/Algorithms/Scan.swift
+++ b/Sources/Algorithms/Reductions.swift
@@ -11,13 +11,13 @@
/// A sequence of applying a transform to the element of a sequence and the
/// previously transformed result.
-public struct Scan {
+public struct Reductions {
let base: Base
let initial: Result
let transform: (Result, Base.Element) -> Result
}
-extension Scan: Sequence {
+extension Reductions: Sequence {
public struct Iterator: IteratorProtocol {
var iterator: Base.Iterator
var current: Result
@@ -37,7 +37,7 @@ extension Scan: Sequence {
}
}
-extension Scan: Collection where Base: Collection {
+extension Reductions: Collection where Base: Collection {
public var startIndex: Base.Index {
base.startIndex
}
@@ -67,7 +67,7 @@ extension Scan: Collection where Base: Collection {
}
}
-extension Scan: BidirectionalCollection where Base: BidirectionalCollection {
+extension Reductions: BidirectionalCollection where Base: BidirectionalCollection {
public func index(before i: Base.Index) -> Base.Index {
base.index(before: i)
}
@@ -83,7 +83,7 @@ extension Sequence {
///
/// ```
/// let values = [1, 2, 3, 4]
- /// let sequence = values.scan(0, +)
+ /// let sequence = values.reductions(0, +)
/// print(Array(sequence))
///
/// // prints [1, 3, 6, 10]
@@ -94,10 +94,10 @@ extension Sequence {
/// - transform: A closure that combines an accumulating value and
/// an element of the sequence.
/// - Returns: A sequence of transformed elements.
- public func scan(
+ public func reductions(
_ initial: Result,
_ transform: @escaping (Result, Element) -> Result
- ) -> Scan {
- Scan(base: self, initial: initial, transform: transform)
+ ) -> Reductions {
+ Reductions(base: self, initial: initial, transform: transform)
}
}
diff --git a/Tests/SwiftAlgorithmsTests/ScanTests.swift b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
similarity index 54%
rename from Tests/SwiftAlgorithmsTests/ScanTests.swift
rename to Tests/SwiftAlgorithmsTests/ReductionsTests.swift
index 800331a8..19bfe770 100644
--- a/Tests/SwiftAlgorithmsTests/ScanTests.swift
+++ b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
@@ -12,31 +12,31 @@
import XCTest
import Algorithms
-final class ScanTests: XCTestCase {
+final class ReductionsTests: XCTestCase {
func testSequence() {
- let scan = (1...).prefix(5).scan(0, +)
- XCTAssertEqualSequences(scan, [1, 3, 6, 10, 15])
+ let reductions = (1...).prefix(5).reductions(0, +)
+ XCTAssertEqualSequences(reductions, [1, 3, 6, 10, 15])
}
func testSequenceEmpty() {
- let scan = (1...).prefix(0).scan(0, +)
- XCTAssertEqualSequences(scan, [])
+ let reductions = (1...).prefix(0).reductions(0, +)
+ XCTAssertEqualSequences(reductions, [])
}
func testCollection() {
- let scan = [3, 4, 2, 3, 1].scan(.max, min)
- XCTAssertEqualSequences(scan, [3, 3, 2, 2, 1])
- validateIndexTraversals(scan)
+ let reductions = [3, 4, 2, 3, 1].reductions(.max, min)
+ XCTAssertEqualSequences(reductions, [3, 3, 2, 2, 1])
+ validateIndexTraversals(reductions)
}
func testCollectionEmpty() {
- let scan = EmptyCollection().scan(.max, min)
- XCTAssertEqualSequences(scan, [])
- validateIndexTraversals(scan)
+ let reductions = EmptyCollection().reductions(.max, min)
+ XCTAssertEqualSequences(reductions, [])
+ validateIndexTraversals(reductions)
}
func testBidirectionalCollection() {
- let reversed = [1,2,3,4,5].scan(0, +).reversed()
+ let reversed = [1,2,3,4,5].reductions(0, +).reversed()
XCTAssertEqualSequences(reversed, [15, 10, 6, 3, 1])
validateIndexTraversals(reversed)
}
From b41af13bc924e80614bbddae090b674aa60100f0 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Tue, 24 Nov 2020 13:09:04 +0000
Subject: [PATCH 04/45] Add information to the readme about the reductions name
---
Guides/Reductions.md | 47 ++++++++++++++++++++++++++++++++++++++------
1 file changed, 41 insertions(+), 6 deletions(-)
diff --git a/Guides/Reductions.md b/Guides/Reductions.md
index 3f8cfedb..71552e56 100644
--- a/Guides/Reductions.md
+++ b/Guides/Reductions.md
@@ -9,8 +9,8 @@ This has the behaviour of reduce, but instead of returning the final result
value, it returns the a sequence of the results returned from each element.
```swift
-let accumulation = (1...5).reductions(0, +)
-print(Array(accumulation))
+let runningTotal = (1...5).reductions(0, +)
+print(Array(runningTotal))
// prints [1, 3, 6, 10, 15]
let runningMinimum = [3, 4, 2, 3, 1].reductions(.max, min)
@@ -37,17 +37,52 @@ Calling these methods is O(_1_).
### Naming
+While the name `scan` is the term of art for this function, it has been
+discussed that `reductions` aligns better with the existing `reduce` function
+and will aid newcomers that might not know the existing `scan` term.
+Below are two quotes from the Swift forum [discussion about SE-0045][SE-0045]
+which proposed adding `scan` to the standard library and one from
+[issue #25][Issue 25] on the swift-algorithms GitHub project. These provide
+the reasoning to use the name `reductions`.
+
+[Brent Royal-Gordon][Brent_Royal-Gordon]:
+> I really like the `reduce`/`reductions` pairing instead of `reduce`/`scan`;
+it does a really good job of explaining the relationship between the two
+functions.
+
+[David Rönnqvist][David Rönnqvist]:
+> As other have already pointed out, I also feel that `scan` is the least
+intuitive name among these and that the `reduce`/`reductions` pairing would do
+a good job at explaining the relation between the two.
+
+[Kyle Macomber][Kyle Macomber]:
+> As someone unfamiliar with the prior art, `reductions` strikes me as very
+approachable—I feel like I can extrapolate the expected behavior purely from my
+familiarity with `reduce`.
+
+[SE-0045]: https://forums.swift.org/t/review-se-0045-add-scan-prefix-while-drop-while-and-iterate-to-the-stdlib/2382
+[Issue 25]: https://github.com/apple/swift-algorithms/issues/25
+[Brent_Royal-Gordon]: https://forums.swift.org/t/review-se-0045-add-scan-prefix-while-drop-while-and-iterate-to-the-stdlib/2382/6
+[David Rönnqvist]: https://forums.swift.org/t/review-se-0045-add-scan-prefix-while-drop-while-and-iterate-to-the-stdlib/2382/8
+[Kyle Macomber]: https://github.com/apple/swift-algorithms/issues/25#issuecomment-709317894
### Comparison with other langauges
-**C++:** As of C++17, the `` library includes an `inclusive_scan` function.
+**C++:** As of C++17, the `` library includes an `inclusive_scan`
+function.
+
+**[Clojure][Clojure]:** Clojure 1.2 added a `reductions` function.
-**Haskell:** Haskell includes a `scan` function for its `Traversable` type,
-which is akin to Swift's `Sequence`.
+**[Haskell][Haskell]:** Haskell includes a `scan` function for its
+`Traversable` type, which is akin to Swift's `Sequence`.
**Python:** Python’s `itertools` includes an `accumulate` method. In version
3.3, a function paramenter was added. Version 3.8 added the optional initial
parameter.
-**Rust:** Rust provides a `scan` function.
+**[Rust][Rust]:** Rust provides a `scan` function.
+
+[Clojure]: http://clojure.github.io/clojure/clojure.core-api.html#clojure.core/reductions
+[Haskell]: http://hackage.haskell.org/package/base-4.8.2.0/docs/Prelude.html#v:scanl
+[Rust]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.scan
From 5116e2500231e25bcfed06bd09cbd8f89d2d9059 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Wed, 25 Nov 2020 08:46:05 +0000
Subject: [PATCH 05/45] Add eager API
---
Sources/Algorithms/Reductions.swift | 15 +++++++++++++++
Tests/SwiftAlgorithmsTests/ReductionsTests.swift | 12 ++++++++++++
2 files changed, 27 insertions(+)
diff --git a/Sources/Algorithms/Reductions.swift b/Sources/Algorithms/Reductions.swift
index bead9650..344b986a 100644
--- a/Sources/Algorithms/Reductions.swift
+++ b/Sources/Algorithms/Reductions.swift
@@ -75,6 +75,21 @@ extension Reductions: BidirectionalCollection where Base: BidirectionalCollectio
extension Sequence {
+ public func reductions(
+ _ initial: Result,
+ _ transform: (Result, Element) throws -> Result
+ ) rethrows -> [Result] {
+
+ var result = initial
+ return try map { element in
+ result = try transform(result, element)
+ return result
+ }
+ }
+}
+
+extension LazySequenceProtocol {
+
/// Returns a sequence containing the results of combining the elements of
/// the sequence using the given transform.
///
diff --git a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
index 19bfe770..aae38616 100644
--- a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
+++ b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
@@ -23,6 +23,18 @@ final class ReductionsTests: XCTestCase {
XCTAssertEqualSequences(reductions, [])
}
+ func testEager() {
+ let reductions: [Int] = [3, 4, 2, 3, 1].reductions(.max, min)
+ XCTAssertEqualSequences(reductions, [3, 3, 2, 2, 1])
+ validateIndexTraversals(reductions)
+ }
+
+ func testEagerThrows() {
+ struct E: Error {}
+ XCTAssertNoThrow(try [].reductions(0, { _, _ in throw E() }))
+ XCTAssertThrowsError(try [1].reductions(0, { _, _ in throw E() }))
+ }
+
func testCollection() {
let reductions = [3, 4, 2, 3, 1].reductions(.max, min)
XCTAssertEqualSequences(reductions, [3, 3, 2, 2, 1])
From e9d35f60758fa288e8c56e6dc76ba5cc5f9cb236 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Thu, 26 Nov 2020 10:42:55 +0000
Subject: [PATCH 06/45] Remove Reductions' conformance to
BidirectionalCollection
---
Sources/Algorithms/Reductions.swift | 6 ------
Tests/SwiftAlgorithmsTests/ReductionsTests.swift | 6 ------
2 files changed, 12 deletions(-)
diff --git a/Sources/Algorithms/Reductions.swift b/Sources/Algorithms/Reductions.swift
index 344b986a..467db8e3 100644
--- a/Sources/Algorithms/Reductions.swift
+++ b/Sources/Algorithms/Reductions.swift
@@ -67,12 +67,6 @@ extension Reductions: Collection where Base: Collection {
}
}
-extension Reductions: BidirectionalCollection where Base: BidirectionalCollection {
- public func index(before i: Base.Index) -> Base.Index {
- base.index(before: i)
- }
-}
-
extension Sequence {
public func reductions(
diff --git a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
index aae38616..c4f20bb5 100644
--- a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
+++ b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
@@ -46,10 +46,4 @@ final class ReductionsTests: XCTestCase {
XCTAssertEqualSequences(reductions, [])
validateIndexTraversals(reductions)
}
-
- func testBidirectionalCollection() {
- let reversed = [1,2,3,4,5].reductions(0, +).reversed()
- XCTAssertEqualSequences(reversed, [15, 10, 6, 3, 1])
- validateIndexTraversals(reversed)
- }
}
From caf0b7175c31e097d191c3ed584e2a8f0aadf0fd Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Thu, 26 Nov 2020 10:43:26 +0000
Subject: [PATCH 07/45] Implement Reductions subscript such that it occurs in
constant time
---
Sources/Algorithms/Reductions.swift | 41 ++++++++++++++++++-----------
1 file changed, 26 insertions(+), 15 deletions(-)
diff --git a/Sources/Algorithms/Reductions.swift b/Sources/Algorithms/Reductions.swift
index 467db8e3..5dc1e256 100644
--- a/Sources/Algorithms/Reductions.swift
+++ b/Sources/Algorithms/Reductions.swift
@@ -38,32 +38,43 @@ extension Reductions: Sequence {
}
extension Reductions: Collection where Base: Collection {
- public var startIndex: Base.Index {
- base.startIndex
- }
+ public struct Index: Comparable {
+ let index: Base.Index
+ let result: Result
+
+ public static func < (lhs: Index, rhs: Index) -> Bool {
+ lhs.index < rhs.index
+ }
- public var endIndex: Base.Index {
- base.endIndex
+ public static func == (lhs: Index, rhs: Index) -> Bool {
+ lhs.index == rhs.index
+ }
}
- public subscript(position: Base.Index) -> Result {
- base[...position].reduce(initial, transform)
+ public var startIndex: Index {
+ let start = base.startIndex
+ let result = transform(initial, base[start])
+ return Index(index: start, result: result)
}
- public func index(after i: Base.Index) -> Base.Index {
- base.index(after: i)
+ public var endIndex: Index {
+ let end = base.endIndex
+ let result = base.reduce(initial, transform)
+ return Index(index: end, result: result)
}
- public func index(_ i: Base.Index, offsetBy distance: Int) -> Base.Index {
- base.index(i, offsetBy: distance)
+ public subscript(position: Index) -> Result {
+ position.result
}
- public func index(_ i: Base.Index, offsetBy distance: Int, limitedBy limit: Base.Index) -> Base.Index? {
- base.index(i, offsetBy: distance, limitedBy: limit)
+ public func index(after i: Index) -> Index {
+ let index = base.index(after: i.index)
+ let result = transform(i.result, base[i.index])
+ return Index(index: index, result: result)
}
- public func distance(from start: Base.Index, to end: Base.Index) -> Int {
- base.distance(from: start, to: end)
+ public func distance(from start: Index, to end: Index) -> Int {
+ base.distance(from: start.index, to: end.index)
}
}
From 531b6f91878eb80b0a4ded1e4cd51831e88e5d80 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Thu, 26 Nov 2020 12:06:29 +0000
Subject: [PATCH 08/45] Add a variant which includes the given initial result
---
Sources/Algorithms/Reductions.swift | 18 ++++++++++++++++++
.../SwiftAlgorithmsTests/ReductionsTests.swift | 7 +++++++
2 files changed, 25 insertions(+)
diff --git a/Sources/Algorithms/Reductions.swift b/Sources/Algorithms/Reductions.swift
index 5dc1e256..8fe6a483 100644
--- a/Sources/Algorithms/Reductions.swift
+++ b/Sources/Algorithms/Reductions.swift
@@ -80,6 +80,24 @@ extension Reductions: Collection where Base: Collection {
extension Sequence {
+ public func reductions(
+ including initial: Result,
+ _ transform: (Result, Element) throws -> Result
+ ) rethrows -> [Result] {
+
+ var output = [Result]()
+ output.reserveCapacity(underestimatedCount + 1)
+ output.append(initial)
+
+ var result = initial
+ for element in self {
+ result = try transform(result, element)
+ output.append(result)
+ }
+
+ return output
+ }
+
public func reductions(
_ initial: Result,
_ transform: (Result, Element) throws -> Result
diff --git a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
index c4f20bb5..74b0e271 100644
--- a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
+++ b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
@@ -27,12 +27,19 @@ final class ReductionsTests: XCTestCase {
let reductions: [Int] = [3, 4, 2, 3, 1].reductions(.max, min)
XCTAssertEqualSequences(reductions, [3, 3, 2, 2, 1])
validateIndexTraversals(reductions)
+
+ let including: [Int] = [6, 3, 2, 4, 1].reductions(including: 10, +)
+ XCTAssertEqualSequences(including, [10, 16, 19, 21, 25, 26])
+ validateIndexTraversals(including)
}
func testEagerThrows() {
struct E: Error {}
XCTAssertNoThrow(try [].reductions(0, { _, _ in throw E() }))
XCTAssertThrowsError(try [1].reductions(0, { _, _ in throw E() }))
+
+ XCTAssertNoThrow(try [].reductions(including: 0, { _, _ in throw E() }))
+ XCTAssertThrowsError(try [1].reductions(including: 0, { _, _ in throw E() }))
}
func testCollection() {
From c0c237a944ac635787d3c2177ee1b62816e914d4 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Thu, 26 Nov 2020 12:07:40 +0000
Subject: [PATCH 09/45] Add a variant which takes no initial result value
---
Sources/Algorithms/Reductions.swift | 10 ++++++++++
Tests/SwiftAlgorithmsTests/ReductionsTests.swift | 8 ++++++++
2 files changed, 18 insertions(+)
diff --git a/Sources/Algorithms/Reductions.swift b/Sources/Algorithms/Reductions.swift
index 8fe6a483..9806c50e 100644
--- a/Sources/Algorithms/Reductions.swift
+++ b/Sources/Algorithms/Reductions.swift
@@ -78,6 +78,16 @@ extension Reductions: Collection where Base: Collection {
}
}
+extension Collection {
+ public func reductions(
+ _ transform: (Element, Element) throws -> Element
+ ) rethrows -> [Element] {
+
+ guard let first = first else { return [] }
+ return try dropFirst().reductions(including: first, transform)
+ }
+}
+
extension Sequence {
public func reductions(
diff --git a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
index 74b0e271..2cc320ea 100644
--- a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
+++ b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
@@ -31,6 +31,10 @@ final class ReductionsTests: XCTestCase {
let including: [Int] = [6, 3, 2, 4, 1].reductions(including: 10, +)
XCTAssertEqualSequences(including, [10, 16, 19, 21, 25, 26])
validateIndexTraversals(including)
+
+ let noInitial: [Int] = [3, 4, 2, 3, 1].reductions(+)
+ XCTAssertEqualSequences(noInitial, [3, 7, 9, 12, 13])
+ validateIndexTraversals(noInitial)
}
func testEagerThrows() {
@@ -40,6 +44,10 @@ final class ReductionsTests: XCTestCase {
XCTAssertNoThrow(try [].reductions(including: 0, { _, _ in throw E() }))
XCTAssertThrowsError(try [1].reductions(including: 0, { _, _ in throw E() }))
+
+ XCTAssertNoThrow(try [].reductions({ _, _ in throw E() }))
+ XCTAssertNoThrow(try [1].reductions({ _, _ in throw E() }))
+ XCTAssertThrowsError(try [1, 1].reductions({ _, _ in throw E() }))
}
func testCollection() {
From 7bb96ead8958a5bb602f7d3a9d308bfdb53c2103 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Thu, 26 Nov 2020 12:19:05 +0000
Subject: [PATCH 10/45] Add excluding label to show the initial result is not
included
---
Sources/Algorithms/Reductions.swift | 2 +-
.../SwiftAlgorithmsTests/ReductionsTests.swift | 18 +++++++++---------
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/Sources/Algorithms/Reductions.swift b/Sources/Algorithms/Reductions.swift
index 9806c50e..0d41279b 100644
--- a/Sources/Algorithms/Reductions.swift
+++ b/Sources/Algorithms/Reductions.swift
@@ -109,7 +109,7 @@ extension Sequence {
}
public func reductions(
- _ initial: Result,
+ excluding initial: Result,
_ transform: (Result, Element) throws -> Result
) rethrows -> [Result] {
diff --git a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
index 2cc320ea..af185ea6 100644
--- a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
+++ b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
@@ -14,19 +14,19 @@ import Algorithms
final class ReductionsTests: XCTestCase {
func testSequence() {
- let reductions = (1...).prefix(5).reductions(0, +)
+ let reductions = (1...).prefix(5).reductions(excluding: 0, +)
XCTAssertEqualSequences(reductions, [1, 3, 6, 10, 15])
}
func testSequenceEmpty() {
- let reductions = (1...).prefix(0).reductions(0, +)
+ let reductions = (1...).prefix(0).reductions(excluding: 0, +)
XCTAssertEqualSequences(reductions, [])
}
func testEager() {
- let reductions: [Int] = [3, 4, 2, 3, 1].reductions(.max, min)
- XCTAssertEqualSequences(reductions, [3, 3, 2, 2, 1])
- validateIndexTraversals(reductions)
+ let excluding: [Int] = [3, 4, 2, 3, 1].reductions(excluding: .max, min)
+ XCTAssertEqualSequences(excluding, [3, 3, 2, 2, 1])
+ validateIndexTraversals(excluding)
let including: [Int] = [6, 3, 2, 4, 1].reductions(including: 10, +)
XCTAssertEqualSequences(including, [10, 16, 19, 21, 25, 26])
@@ -39,8 +39,8 @@ final class ReductionsTests: XCTestCase {
func testEagerThrows() {
struct E: Error {}
- XCTAssertNoThrow(try [].reductions(0, { _, _ in throw E() }))
- XCTAssertThrowsError(try [1].reductions(0, { _, _ in throw E() }))
+ XCTAssertNoThrow(try [].reductions(excluding: 0, { _, _ in throw E() }))
+ XCTAssertThrowsError(try [1].reductions(excluding: 0, { _, _ in throw E() }))
XCTAssertNoThrow(try [].reductions(including: 0, { _, _ in throw E() }))
XCTAssertThrowsError(try [1].reductions(including: 0, { _, _ in throw E() }))
@@ -51,13 +51,13 @@ final class ReductionsTests: XCTestCase {
}
func testCollection() {
- let reductions = [3, 4, 2, 3, 1].reductions(.max, min)
+ let reductions = [3, 4, 2, 3, 1].reductions(excluding: .max, min)
XCTAssertEqualSequences(reductions, [3, 3, 2, 2, 1])
validateIndexTraversals(reductions)
}
func testCollectionEmpty() {
- let reductions = EmptyCollection().reductions(.max, min)
+ let reductions = EmptyCollection().reductions(excluding: .max, min)
XCTAssertEqualSequences(reductions, [])
validateIndexTraversals(reductions)
}
From 5b4d984a08afb5904f3cb603e5d291aa87a5a411 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Thu, 26 Nov 2020 17:05:36 +0000
Subject: [PATCH 11/45] Test lazy implementations
---
.../ReductionsTests.swift | 32 +++++++++----------
1 file changed, 16 insertions(+), 16 deletions(-)
diff --git a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
index af185ea6..8c311dba 100644
--- a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
+++ b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
@@ -13,16 +13,28 @@ import XCTest
import Algorithms
final class ReductionsTests: XCTestCase {
- func testSequence() {
- let reductions = (1...).prefix(5).reductions(excluding: 0, +)
+ func testLazySequence() {
+ let reductions = (1...).prefix(5).lazy.reductions(0, +)
XCTAssertEqualSequences(reductions, [1, 3, 6, 10, 15])
}
- func testSequenceEmpty() {
- let reductions = (1...).prefix(0).reductions(excluding: 0, +)
+ func testLazySequenceEmpty() {
+ let reductions = (1...).prefix(0).lazy.reductions(0, +)
XCTAssertEqualSequences(reductions, [])
}
+ func testLazyCollection() {
+ let reductions = [3, 4, 2, 3, 1].lazy.reductions(.max, min)
+ XCTAssertEqualSequences(reductions, [3, 3, 2, 2, 1])
+// validateIndexTraversals(reductions)
+ }
+
+ func testLazyCollectionEmpty() {
+ let reductions = EmptyCollection().lazy.reductions(.max, min)
+ XCTAssertEqualSequences(reductions, [])
+// validateIndexTraversals(reductions)
+ }
+
func testEager() {
let excluding: [Int] = [3, 4, 2, 3, 1].reductions(excluding: .max, min)
XCTAssertEqualSequences(excluding, [3, 3, 2, 2, 1])
@@ -49,16 +61,4 @@ final class ReductionsTests: XCTestCase {
XCTAssertNoThrow(try [1].reductions({ _, _ in throw E() }))
XCTAssertThrowsError(try [1, 1].reductions({ _, _ in throw E() }))
}
-
- func testCollection() {
- let reductions = [3, 4, 2, 3, 1].reductions(excluding: .max, min)
- XCTAssertEqualSequences(reductions, [3, 3, 2, 2, 1])
- validateIndexTraversals(reductions)
- }
-
- func testCollectionEmpty() {
- let reductions = EmptyCollection().reductions(excluding: .max, min)
- XCTAssertEqualSequences(reductions, [])
- validateIndexTraversals(reductions)
- }
}
From e0ee01d6534e1d56f6f609892671940c92e5df34 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Thu, 26 Nov 2020 17:12:25 +0000
Subject: [PATCH 12/45] Remove reductions(including:_:)
---
Sources/Algorithms/Reductions.swift | 16 ++--------------
.../SwiftAlgorithmsTests/ReductionsTests.swift | 17 +++++------------
2 files changed, 7 insertions(+), 26 deletions(-)
diff --git a/Sources/Algorithms/Reductions.swift b/Sources/Algorithms/Reductions.swift
index 0d41279b..5d567e3c 100644
--- a/Sources/Algorithms/Reductions.swift
+++ b/Sources/Algorithms/Reductions.swift
@@ -84,14 +84,14 @@ extension Collection {
) rethrows -> [Element] {
guard let first = first else { return [] }
- return try dropFirst().reductions(including: first, transform)
+ return try dropFirst().reductions(first, transform)
}
}
extension Sequence {
public func reductions(
- including initial: Result,
+ _ initial: Result,
_ transform: (Result, Element) throws -> Result
) rethrows -> [Result] {
@@ -107,18 +107,6 @@ extension Sequence {
return output
}
-
- public func reductions(
- excluding initial: Result,
- _ transform: (Result, Element) throws -> Result
- ) rethrows -> [Result] {
-
- var result = initial
- return try map { element in
- result = try transform(result, element)
- return result
- }
- }
}
extension LazySequenceProtocol {
diff --git a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
index 8c311dba..b98257cb 100644
--- a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
+++ b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
@@ -36,13 +36,9 @@ final class ReductionsTests: XCTestCase {
}
func testEager() {
- let excluding: [Int] = [3, 4, 2, 3, 1].reductions(excluding: .max, min)
- XCTAssertEqualSequences(excluding, [3, 3, 2, 2, 1])
- validateIndexTraversals(excluding)
-
- let including: [Int] = [6, 3, 2, 4, 1].reductions(including: 10, +)
- XCTAssertEqualSequences(including, [10, 16, 19, 21, 25, 26])
- validateIndexTraversals(including)
+ let initial: [Int] = [6, 3, 2, 4, 1].reductions(10, +)
+ XCTAssertEqualSequences(initial, [10, 16, 19, 21, 25, 26])
+ validateIndexTraversals(initial)
let noInitial: [Int] = [3, 4, 2, 3, 1].reductions(+)
XCTAssertEqualSequences(noInitial, [3, 7, 9, 12, 13])
@@ -51,11 +47,8 @@ final class ReductionsTests: XCTestCase {
func testEagerThrows() {
struct E: Error {}
- XCTAssertNoThrow(try [].reductions(excluding: 0, { _, _ in throw E() }))
- XCTAssertThrowsError(try [1].reductions(excluding: 0, { _, _ in throw E() }))
-
- XCTAssertNoThrow(try [].reductions(including: 0, { _, _ in throw E() }))
- XCTAssertThrowsError(try [1].reductions(including: 0, { _, _ in throw E() }))
+ XCTAssertNoThrow(try [].reductions(0, { _, _ in throw E() }))
+ XCTAssertThrowsError(try [1].reductions(0, { _, _ in throw E() }))
XCTAssertNoThrow(try [].reductions({ _, _ in throw E() }))
XCTAssertNoThrow(try [1].reductions({ _, _ in throw E() }))
From 1ce31ed773ac0505f7842b9498eda5dc55d6599a Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Thu, 26 Nov 2020 20:24:38 +0000
Subject: [PATCH 13/45] Add conformance to LazySequenceProtocol and
LazyCollectionProtocol when the base sequence conforms
---
Sources/Algorithms/Reductions.swift | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/Sources/Algorithms/Reductions.swift b/Sources/Algorithms/Reductions.swift
index 5d567e3c..c29d21f0 100644
--- a/Sources/Algorithms/Reductions.swift
+++ b/Sources/Algorithms/Reductions.swift
@@ -78,6 +78,12 @@ extension Reductions: Collection where Base: Collection {
}
}
+extension Reductions: LazyCollectionProtocol
+where Base: LazyCollectionProtocol {}
+
+extension Reductions: LazySequenceProtocol
+ where Base: LazySequenceProtocol {}
+
extension Collection {
public func reductions(
_ transform: (Element, Element) throws -> Element
From fe912e5b67a90c3cf12f2912ff8592a98c4d45bc Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Fri, 27 Nov 2020 07:56:44 +0000
Subject: [PATCH 14/45] Fix implementation of lazy reductions
---
Guides/Reductions.md | 19 ++++++-
Sources/Algorithms/Reductions.swift | 18 +++----
.../ReductionsTests.swift | 50 ++++++++++---------
3 files changed, 53 insertions(+), 34 deletions(-)
diff --git a/Guides/Reductions.md b/Guides/Reductions.md
index 71552e56..dfd9405f 100644
--- a/Guides/Reductions.md
+++ b/Guides/Reductions.md
@@ -11,7 +11,7 @@ value, it returns the a sequence of the results returned from each element.
```swift
let runningTotal = (1...5).reductions(0, +)
print(Array(runningTotal))
-// prints [1, 3, 6, 10, 15]
+// prints [0, 1, 3, 6, 10, 15]
let runningMinimum = [3, 4, 2, 3, 1].reductions(.max, min)
print(Array(runningMinimum))
@@ -23,12 +23,27 @@ print(Array(runningMinimum))
One new method is added to sequences:
```swift
-extension Sequence {
+extension LazySequenceProtocol {
func reductions(
_ initial: Result,
_ transform: @escaping (Result, Element) -> Result
) -> Reductions
}
+
+extension Sequence {
+ public func reductions(
+ _ initial: Result,
+ _ transform: (Result, Element) throws -> Result
+ ) rethrows -> [Result]
+}
+```
+
+```swift
+extension Collection {
+ public func reductions(
+ _ transform: (Element, Element) throws -> Element
+ ) rethrows -> [Element]
+}
```
### Complexity
diff --git a/Sources/Algorithms/Reductions.swift b/Sources/Algorithms/Reductions.swift
index c29d21f0..a09a63ee 100644
--- a/Sources/Algorithms/Reductions.swift
+++ b/Sources/Algorithms/Reductions.swift
@@ -20,13 +20,13 @@ public struct Reductions {
extension Reductions: Sequence {
public struct Iterator: IteratorProtocol {
var iterator: Base.Iterator
- var current: Result
+ var current: Result?
let transform: (Result, Base.Element) -> Result
public mutating func next() -> Result? {
- guard let element = iterator.next() else { return nil }
- current = transform(current, element)
- return current
+ guard let result = current else { return nil }
+ current = iterator.next().map { transform(result, $0) }
+ return result
}
}
@@ -121,20 +121,20 @@ extension LazySequenceProtocol {
/// the sequence using the given transform.
///
/// This can be seen as applying the reduce function to each element and
- /// providing these results as a sequence.
+ /// providing the initial value followed by these results as a sequence.
///
/// ```
/// let values = [1, 2, 3, 4]
/// let sequence = values.reductions(0, +)
/// print(Array(sequence))
///
- /// // prints [1, 3, 6, 10]
+ /// // prints [0, 1, 3, 6, 10]
/// ```
///
/// - Parameters:
- /// - initial: The value to use as the initial accumulating value.
- /// - transform: A closure that combines an accumulating value and
- /// an element of the sequence.
+ /// - initial: The value to use as the initial value.
+ /// - transform: A closure that combines the previously reduced result and
+ /// the next element in the receiving sequence.
/// - Returns: A sequence of transformed elements.
public func reductions(
_ initial: Result,
diff --git a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
index b98257cb..ce4ffa8d 100644
--- a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
+++ b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
@@ -13,45 +13,49 @@ import XCTest
import Algorithms
final class ReductionsTests: XCTestCase {
+
func testLazySequence() {
let reductions = (1...).prefix(5).lazy.reductions(0, +)
- XCTAssertEqualSequences(reductions, [1, 3, 6, 10, 15])
- }
+ XCTAssertEqualSequences(reductions, [0, 1, 3, 6, 10, 15])
- func testLazySequenceEmpty() {
- let reductions = (1...).prefix(0).lazy.reductions(0, +)
- XCTAssertEqualSequences(reductions, [])
+ let empty = (1...).prefix(0).lazy.reductions(0, +)
+ XCTAssertEqualSequences(empty, [0])
}
func testLazyCollection() {
- let reductions = [3, 4, 2, 3, 1].lazy.reductions(.max, min)
- XCTAssertEqualSequences(reductions, [3, 3, 2, 2, 1])
+ let reductions = [1, 2, 3, 4, 5].lazy.reductions(0, +)
+ XCTAssertEqualSequences(reductions, [0, 1, 3, 6, 10, 15])
// validateIndexTraversals(reductions)
- }
- func testLazyCollectionEmpty() {
- let reductions = EmptyCollection().lazy.reductions(.max, min)
- XCTAssertEqualSequences(reductions, [])
+ let empty = EmptyCollection().lazy.reductions(0, +)
+ XCTAssertEqualSequences(empty, [0])
// validateIndexTraversals(reductions)
}
- func testEager() {
- let initial: [Int] = [6, 3, 2, 4, 1].reductions(10, +)
- XCTAssertEqualSequences(initial, [10, 16, 19, 21, 25, 26])
- validateIndexTraversals(initial)
+ func testEagerInitial() {
+ let reductions: [Int] = [1, 2, 3, 4, 5].reductions(0, +)
+ XCTAssertEqualSequences(reductions, [0, 1, 3, 6, 10, 15])
- let noInitial: [Int] = [3, 4, 2, 3, 1].reductions(+)
- XCTAssertEqualSequences(noInitial, [3, 7, 9, 12, 13])
- validateIndexTraversals(noInitial)
+ let empty: [Int] = EmptyCollection().reductions(0, +)
+ XCTAssertEqualSequences(empty, [0])
+ }
+
+ func testEagerNoInitial() {
+ let reductions: [Int] = [1, 2, 3, 4, 5].reductions(+)
+ XCTAssertEqualSequences(reductions, [1, 3, 6, 10, 15])
+
+ let empty: [Int] = EmptyCollection().reductions(+)
+ XCTAssertEqualSequences(empty, [])
}
func testEagerThrows() {
struct E: Error {}
- XCTAssertNoThrow(try [].reductions(0, { _, _ in throw E() }))
- XCTAssertThrowsError(try [1].reductions(0, { _, _ in throw E() }))
- XCTAssertNoThrow(try [].reductions({ _, _ in throw E() }))
- XCTAssertNoThrow(try [1].reductions({ _, _ in throw E() }))
- XCTAssertThrowsError(try [1, 1].reductions({ _, _ in throw E() }))
+ XCTAssertNoThrow(try [].reductions(0) { _, _ in throw E() })
+ XCTAssertThrowsError(try [1].reductions(0) { _, _ in throw E() })
+
+ XCTAssertNoThrow(try [].reductions { _, _ in throw E() })
+ XCTAssertNoThrow(try [1].reductions { _, _ in throw E() })
+ XCTAssertThrowsError(try [1, 1].reductions { _, _ in throw E() })
}
}
From c3e918d61bd5e410db60199c192fdc13637a7d77 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Fri, 27 Nov 2020 08:05:22 +0000
Subject: [PATCH 15/45] Add test cases for sequences with one element
---
Tests/SwiftAlgorithmsTests/ReductionsTests.swift | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
index ce4ffa8d..424614a0 100644
--- a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
+++ b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
@@ -18,6 +18,9 @@ final class ReductionsTests: XCTestCase {
let reductions = (1...).prefix(5).lazy.reductions(0, +)
XCTAssertEqualSequences(reductions, [0, 1, 3, 6, 10, 15])
+ let one = (1...).prefix(1).lazy.reductions(0, +)
+ XCTAssertEqualSequences(one, [0, 1])
+
let empty = (1...).prefix(0).lazy.reductions(0, +)
XCTAssertEqualSequences(empty, [0])
}
@@ -27,8 +30,13 @@ final class ReductionsTests: XCTestCase {
XCTAssertEqualSequences(reductions, [0, 1, 3, 6, 10, 15])
// validateIndexTraversals(reductions)
+ let one = [1].lazy.reductions(0, +)
+ XCTAssertEqualSequences(one, [0, 1])
+// validateIndexTraversals(one)
+
let empty = EmptyCollection().lazy.reductions(0, +)
XCTAssertEqualSequences(empty, [0])
+// validateIndexTraversals(empty)
// validateIndexTraversals(reductions)
}
@@ -36,6 +44,9 @@ final class ReductionsTests: XCTestCase {
let reductions: [Int] = [1, 2, 3, 4, 5].reductions(0, +)
XCTAssertEqualSequences(reductions, [0, 1, 3, 6, 10, 15])
+ let one: [Int] = CollectionOfOne(1).reductions(0, +)
+ XCTAssertEqualSequences(one, [0, 1])
+
let empty: [Int] = EmptyCollection().reductions(0, +)
XCTAssertEqualSequences(empty, [0])
}
@@ -44,6 +55,9 @@ final class ReductionsTests: XCTestCase {
let reductions: [Int] = [1, 2, 3, 4, 5].reductions(+)
XCTAssertEqualSequences(reductions, [1, 3, 6, 10, 15])
+ let one: [Int] = CollectionOfOne(1).reductions(+)
+ XCTAssertEqualSequences(one, [1])
+
let empty: [Int] = EmptyCollection().reductions(+)
XCTAssertEqualSequences(empty, [])
}
From c69357931ab19f5245fc843f69c530f025cf7aab Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Fri, 27 Nov 2020 08:10:14 +0000
Subject: [PATCH 16/45] Tidy up tests
---
.../ReductionsTests.swift | 72 ++++++++++---------
1 file changed, 40 insertions(+), 32 deletions(-)
diff --git a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
index 424614a0..d2e3ba2e 100644
--- a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
+++ b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
@@ -14,52 +14,60 @@ import Algorithms
final class ReductionsTests: XCTestCase {
- func testLazySequence() {
- let reductions = (1...).prefix(5).lazy.reductions(0, +)
- XCTAssertEqualSequences(reductions, [0, 1, 3, 6, 10, 15])
-
- let one = (1...).prefix(1).lazy.reductions(0, +)
- XCTAssertEqualSequences(one, [0, 1])
-
- let empty = (1...).prefix(0).lazy.reductions(0, +)
- XCTAssertEqualSequences(empty, [0])
+ func testLazySequenceInitial() {
+ XCTAssertEqualSequences(
+ (1...).prefix(5).lazy.reductions(0, +),
+ [0, 1, 3, 6, 10, 15])
+
+ XCTAssertEqualSequences(
+ (1...).prefix(1).lazy.reductions(0, +),
+ [0, 1])
+
+ XCTAssertEqualSequences(
+ (1...).prefix(0).lazy.reductions(0, +),
+ [0])
}
- func testLazyCollection() {
- let reductions = [1, 2, 3, 4, 5].lazy.reductions(0, +)
- XCTAssertEqualSequences(reductions, [0, 1, 3, 6, 10, 15])
-// validateIndexTraversals(reductions)
+ func testLazyCollectionInitial() {
+ XCTAssertEqualSequences(
+ [1, 2, 3, 4, 5].lazy.reductions(0, +),
+ [0, 1, 3, 6, 10, 15])
- let one = [1].lazy.reductions(0, +)
- XCTAssertEqualSequences(one, [0, 1])
-// validateIndexTraversals(one)
+ XCTAssertEqualSequences(
+ [1].lazy.reductions(0, +),
+ [0, 1])
- let empty = EmptyCollection().lazy.reductions(0, +)
- XCTAssertEqualSequences(empty, [0])
-// validateIndexTraversals(empty)
-// validateIndexTraversals(reductions)
+ XCTAssertEqualSequences(
+ EmptyCollection().lazy.reductions(0, +),
+ [0])
}
func testEagerInitial() {
- let reductions: [Int] = [1, 2, 3, 4, 5].reductions(0, +)
- XCTAssertEqualSequences(reductions, [0, 1, 3, 6, 10, 15])
+ XCTAssertEqual(
+ [1, 2, 3, 4, 5].reductions(0, +),
+ [0, 1, 3, 6, 10, 15])
- let one: [Int] = CollectionOfOne(1).reductions(0, +)
- XCTAssertEqualSequences(one, [0, 1])
+ XCTAssertEqual(
+ CollectionOfOne(1).reductions(0, +),
+ [0, 1])
- let empty: [Int] = EmptyCollection().reductions(0, +)
- XCTAssertEqualSequences(empty, [0])
+ XCTAssertEqualSequences(
+ EmptyCollection().reductions(0, +),
+ [0])
}
func testEagerNoInitial() {
- let reductions: [Int] = [1, 2, 3, 4, 5].reductions(+)
- XCTAssertEqualSequences(reductions, [1, 3, 6, 10, 15])
+ XCTAssertEqualSequences(
+ [1, 2, 3, 4, 5].reductions(+),
+ [1, 3, 6, 10, 15])
- let one: [Int] = CollectionOfOne(1).reductions(+)
- XCTAssertEqualSequences(one, [1])
+ XCTAssertEqualSequences(
+ CollectionOfOne(1).reductions(+),
+ [1])
- let empty: [Int] = EmptyCollection().reductions(+)
- XCTAssertEqualSequences(empty, [])
+ XCTAssertEqualSequences(
+ EmptyCollection().reductions(+),
+ [])
}
func testEagerThrows() {
From 55e87eb3d83c823c286e1d66bd172cf087a536ac Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Fri, 27 Nov 2020 08:21:08 +0000
Subject: [PATCH 17/45] Improve ergonomics of no initial value eager reductions
call and provide it as an extension on Sequence
---
Sources/Algorithms/Reductions.swift | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/Sources/Algorithms/Reductions.swift b/Sources/Algorithms/Reductions.swift
index a09a63ee..7b3c2de6 100644
--- a/Sources/Algorithms/Reductions.swift
+++ b/Sources/Algorithms/Reductions.swift
@@ -84,17 +84,15 @@ where Base: LazyCollectionProtocol {}
extension Reductions: LazySequenceProtocol
where Base: LazySequenceProtocol {}
-extension Collection {
+extension Sequence {
public func reductions(
_ transform: (Element, Element) throws -> Element
) rethrows -> [Element] {
- guard let first = first else { return [] }
- return try dropFirst().reductions(first, transform)
+ var iterator = makeIterator()
+ guard let initial = iterator.next() else { return [] }
+ return try IteratorSequence(iterator).reductions(initial, transform)
}
-}
-
-extension Sequence {
public func reductions(
_ initial: Result,
From 4654ae758e3216516499b144e075f6d9ad544ac3 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Fri, 27 Nov 2020 15:26:07 +0000
Subject: [PATCH 18/45] Add lazy InclusiveReductions sequence
---
Sources/Algorithms/Reductions.swift | 62 ++++++++++++++++---
.../ReductionsTests.swift | 38 ++++++------
2 files changed, 74 insertions(+), 26 deletions(-)
diff --git a/Sources/Algorithms/Reductions.swift b/Sources/Algorithms/Reductions.swift
index 7b3c2de6..94242eac 100644
--- a/Sources/Algorithms/Reductions.swift
+++ b/Sources/Algorithms/Reductions.swift
@@ -9,6 +9,8 @@
//
//===----------------------------------------------------------------------===//
+// MARK: - Exclusive Reductions
+
/// A sequence of applying a transform to the element of a sequence and the
/// previously transformed result.
public struct Reductions {
@@ -85,14 +87,6 @@ extension Reductions: LazySequenceProtocol
where Base: LazySequenceProtocol {}
extension Sequence {
- public func reductions(
- _ transform: (Element, Element) throws -> Element
- ) rethrows -> [Element] {
-
- var iterator = makeIterator()
- guard let initial = iterator.next() else { return [] }
- return try IteratorSequence(iterator).reductions(initial, transform)
- }
public func reductions(
_ initial: Result,
@@ -141,3 +135,55 @@ extension LazySequenceProtocol {
Reductions(base: self, initial: initial, transform: transform)
}
}
+
+// MARK: - Inclusive Reductions
+
+extension LazySequenceProtocol {
+
+ public func reductions(
+ _ transform: @escaping (Element, Element) -> Element
+ ) -> InclusiveReductions {
+ InclusiveReductions(base: self, transform: transform)
+ }
+}
+
+extension Sequence {
+ public func reductions(
+ _ transform: (Element, Element) throws -> Element
+ ) rethrows -> [Element] {
+ var iterator = makeIterator()
+ guard let initial = iterator.next() else { return [] }
+ return try IteratorSequence(iterator).reductions(initial, transform)
+ }
+}
+
+public struct InclusiveReductions {
+ let base: Base
+ let transform: (Base.Element, Base.Element) -> Base.Element
+}
+
+extension InclusiveReductions: Sequence {
+ public struct Iterator: IteratorProtocol {
+ var iterator: Base.Iterator
+ var element: Base.Element?
+ let transform: (Base.Element, Base.Element) -> Base.Element
+
+ public mutating func next() -> Base.Element? {
+ guard let previous = element else {
+ element = iterator.next()
+ return element
+ }
+ guard let next = iterator.next() else { return nil }
+ element = transform(previous, next)
+ return element
+ }
+ }
+
+ public func makeIterator() -> Iterator {
+ Iterator(iterator: base.makeIterator(),
+ transform: transform)
+ }
+}
+
+extension InclusiveReductions: LazySequenceProtocol
+ where Base: LazySequenceProtocol {}
diff --git a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
index d2e3ba2e..699c3f61 100644
--- a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
+++ b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
@@ -14,6 +14,10 @@ import Algorithms
final class ReductionsTests: XCTestCase {
+ struct TestError: Error {}
+
+ // MARK: - Exclusive Reductions
+
func testLazySequenceInitial() {
XCTAssertEqualSequences(
(1...).prefix(5).lazy.reductions(0, +),
@@ -56,28 +60,26 @@ final class ReductionsTests: XCTestCase {
[0])
}
- func testEagerNoInitial() {
- XCTAssertEqualSequences(
- [1, 2, 3, 4, 5].reductions(+),
- [1, 3, 6, 10, 15])
+ func testEagerThrows() {
+ XCTAssertNoThrow(try [].reductions(0) { _, _ in throw TestError() })
+ XCTAssertThrowsError(try [1].reductions(0) { _, _ in throw TestError() })
+ }
- XCTAssertEqualSequences(
- CollectionOfOne(1).reductions(+),
- [1])
+ // MARK: - Inclusive Reductions
- XCTAssertEqualSequences(
- EmptyCollection().reductions(+),
- [])
+ func testInclusiveLazySequence() {
+ XCTAssertEqualSequences((1...).prefix(4).lazy.reductions(+), [1, 3, 6, 10])
+ XCTAssertEqualSequences((1...).prefix(1).lazy.reductions(+), [1])
+ XCTAssertEqualSequences((1...).prefix(0).lazy.reductions(+), [])
}
- func testEagerThrows() {
- struct E: Error {}
-
- XCTAssertNoThrow(try [].reductions(0) { _, _ in throw E() })
- XCTAssertThrowsError(try [1].reductions(0) { _, _ in throw E() })
+ func testInclusiveEager() {
+ XCTAssertEqual([1, 2, 3, 4].reductions(+), [1, 3, 6, 10])
+ XCTAssertEqual([1].reductions(+), [1])
+ XCTAssertEqual(EmptyCollection().reductions(+), [])
- XCTAssertNoThrow(try [].reductions { _, _ in throw E() })
- XCTAssertNoThrow(try [1].reductions { _, _ in throw E() })
- XCTAssertThrowsError(try [1, 1].reductions { _, _ in throw E() })
+ XCTAssertNoThrow(try [].reductions { _, _ in throw TestError() })
+ XCTAssertNoThrow(try [1].reductions { _, _ in throw TestError() })
+ XCTAssertThrowsError(try [1, 1].reductions { _, _ in throw TestError() })
}
}
From 154fa4066ed5fa14453480e2280b55bdbfeac16e Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Fri, 27 Nov 2020 15:30:40 +0000
Subject: [PATCH 19/45] Rename Reductions to ExclusiveReductions
---
Sources/Algorithms/Reductions.swift | 148 ++++++------------
.../ReductionsTests.swift | 48 +-----
2 files changed, 60 insertions(+), 136 deletions(-)
diff --git a/Sources/Algorithms/Reductions.swift b/Sources/Algorithms/Reductions.swift
index 94242eac..e3d39662 100644
--- a/Sources/Algorithms/Reductions.swift
+++ b/Sources/Algorithms/Reductions.swift
@@ -11,81 +11,35 @@
// MARK: - Exclusive Reductions
-/// A sequence of applying a transform to the element of a sequence and the
-/// previously transformed result.
-public struct Reductions {
- let base: Base
- let initial: Result
- let transform: (Result, Base.Element) -> Result
-}
-
-extension Reductions: Sequence {
- public struct Iterator: IteratorProtocol {
- var iterator: Base.Iterator
- var current: Result?
- let transform: (Result, Base.Element) -> Result
-
- public mutating func next() -> Result? {
- guard let result = current else { return nil }
- current = iterator.next().map { transform(result, $0) }
- return result
- }
- }
-
- public func makeIterator() -> Iterator {
- Iterator(iterator: base.makeIterator(),
- current: initial,
- transform: transform)
- }
-}
-
-extension Reductions: Collection where Base: Collection {
- public struct Index: Comparable {
- let index: Base.Index
- let result: Result
-
- public static func < (lhs: Index, rhs: Index) -> Bool {
- lhs.index < rhs.index
- }
-
- public static func == (lhs: Index, rhs: Index) -> Bool {
- lhs.index == rhs.index
- }
- }
-
- public var startIndex: Index {
- let start = base.startIndex
- let result = transform(initial, base[start])
- return Index(index: start, result: result)
- }
-
- public var endIndex: Index {
- let end = base.endIndex
- let result = base.reduce(initial, transform)
- return Index(index: end, result: result)
- }
-
- public subscript(position: Index) -> Result {
- position.result
- }
-
- public func index(after i: Index) -> Index {
- let index = base.index(after: i.index)
- let result = transform(i.result, base[i.index])
- return Index(index: index, result: result)
- }
+extension LazySequenceProtocol {
- public func distance(from start: Index, to end: Index) -> Int {
- base.distance(from: start.index, to: end.index)
+ /// Returns a sequence containing the results of combining the elements of
+ /// the sequence using the given transform.
+ ///
+ /// This can be seen as applying the reduce function to each element and
+ /// providing the initial value followed by these results as a sequence.
+ ///
+ /// ```
+ /// let values = [1, 2, 3, 4]
+ /// let sequence = values.reductions(0, +)
+ /// print(Array(sequence))
+ ///
+ /// // prints [0, 1, 3, 6, 10]
+ /// ```
+ ///
+ /// - Parameters:
+ /// - initial: The value to use as the initial value.
+ /// - transform: A closure that combines the previously reduced result and
+ /// the next element in the receiving sequence.
+ /// - Returns: A sequence of transformed elements.
+ public func reductions(
+ _ initial: Result,
+ _ transform: @escaping (Result, Element) -> Result
+ ) -> ExclusiveReductions {
+ ExclusiveReductions(base: self, initial: initial, transform: transform)
}
}
-extension Reductions: LazyCollectionProtocol
-where Base: LazyCollectionProtocol {}
-
-extension Reductions: LazySequenceProtocol
- where Base: LazySequenceProtocol {}
-
extension Sequence {
public func reductions(
@@ -107,35 +61,37 @@ extension Sequence {
}
}
-extension LazySequenceProtocol {
+/// A sequence of applying a transform to the element of a sequence and the
+/// previously transformed result.
+public struct ExclusiveReductions {
+ let base: Base
+ let initial: Result
+ let transform: (Result, Base.Element) -> Result
+}
- /// Returns a sequence containing the results of combining the elements of
- /// the sequence using the given transform.
- ///
- /// This can be seen as applying the reduce function to each element and
- /// providing the initial value followed by these results as a sequence.
- ///
- /// ```
- /// let values = [1, 2, 3, 4]
- /// let sequence = values.reductions(0, +)
- /// print(Array(sequence))
- ///
- /// // prints [0, 1, 3, 6, 10]
- /// ```
- ///
- /// - Parameters:
- /// - initial: The value to use as the initial value.
- /// - transform: A closure that combines the previously reduced result and
- /// the next element in the receiving sequence.
- /// - Returns: A sequence of transformed elements.
- public func reductions(
- _ initial: Result,
- _ transform: @escaping (Result, Element) -> Result
- ) -> Reductions {
- Reductions(base: self, initial: initial, transform: transform)
+extension ExclusiveReductions: Sequence {
+ public struct Iterator: IteratorProtocol {
+ var iterator: Base.Iterator
+ var current: Result?
+ let transform: (Result, Base.Element) -> Result
+
+ public mutating func next() -> Result? {
+ guard let result = current else { return nil }
+ current = iterator.next().map { transform(result, $0) }
+ return result
+ }
+ }
+
+ public func makeIterator() -> Iterator {
+ Iterator(iterator: base.makeIterator(),
+ current: initial,
+ transform: transform)
}
}
+extension ExclusiveReductions: LazySequenceProtocol
+ where Base: LazySequenceProtocol {}
+
// MARK: - Inclusive Reductions
extension LazySequenceProtocol {
diff --git a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
index 699c3f61..fe258619 100644
--- a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
+++ b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
@@ -18,49 +18,17 @@ final class ReductionsTests: XCTestCase {
// MARK: - Exclusive Reductions
- func testLazySequenceInitial() {
- XCTAssertEqualSequences(
- (1...).prefix(5).lazy.reductions(0, +),
- [0, 1, 3, 6, 10, 15])
-
- XCTAssertEqualSequences(
- (1...).prefix(1).lazy.reductions(0, +),
- [0, 1])
-
- XCTAssertEqualSequences(
- (1...).prefix(0).lazy.reductions(0, +),
- [0])
+ func testExclusiveLazySequence() {
+ XCTAssertEqualSequences((1...).prefix(4).lazy.reductions(0, +), [0, 1, 3, 6, 10])
+ XCTAssertEqualSequences((1...).prefix(1).lazy.reductions(0, +), [0, 1])
+ XCTAssertEqualSequences((1...).prefix(0).lazy.reductions(0, +), [0])
}
- func testLazyCollectionInitial() {
- XCTAssertEqualSequences(
- [1, 2, 3, 4, 5].lazy.reductions(0, +),
- [0, 1, 3, 6, 10, 15])
-
- XCTAssertEqualSequences(
- [1].lazy.reductions(0, +),
- [0, 1])
-
- XCTAssertEqualSequences(
- EmptyCollection().lazy.reductions(0, +),
- [0])
- }
-
- func testEagerInitial() {
- XCTAssertEqual(
- [1, 2, 3, 4, 5].reductions(0, +),
- [0, 1, 3, 6, 10, 15])
-
- XCTAssertEqual(
- CollectionOfOne(1).reductions(0, +),
- [0, 1])
-
- XCTAssertEqualSequences(
- EmptyCollection().reductions(0, +),
- [0])
- }
+ func testExclusiveEager() {
+ XCTAssertEqual([1, 2, 3, 4].reductions(0, +), [0, 1, 3, 6, 10])
+ XCTAssertEqual([1].reductions(0, +), [0, 1])
+ XCTAssertEqual(EmptyCollection().reductions(0, +), [0])
- func testEagerThrows() {
XCTAssertNoThrow(try [].reductions(0) { _, _ in throw TestError() })
XCTAssertThrowsError(try [1].reductions(0) { _, _ in throw TestError() })
}
From 959e963a167f8300fc99b5d3a9fc5363b0a6fff9 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Fri, 27 Nov 2020 16:12:32 +0000
Subject: [PATCH 20/45] Add Collection implementation for InclusiveReductions
---
Sources/Algorithms/Reductions.swift | 45 +++++++++++++++++++
.../ReductionsTests.swift | 12 ++++-
.../SwiftAlgorithmsTests/TestUtilities.swift | 15 +++++++
3 files changed, 70 insertions(+), 2 deletions(-)
diff --git a/Sources/Algorithms/Reductions.swift b/Sources/Algorithms/Reductions.swift
index e3d39662..95335463 100644
--- a/Sources/Algorithms/Reductions.swift
+++ b/Sources/Algorithms/Reductions.swift
@@ -141,5 +141,50 @@ extension InclusiveReductions: Sequence {
}
}
+extension InclusiveReductions: Collection where Base: Collection {
+ public struct Index: Comparable {
+ let index: Base.Index
+ let previous: Element?
+
+ public static func < (lhs: Index, rhs: Index) -> Bool {
+ lhs.index < rhs.index
+ }
+
+ public static func == (lhs: Index, rhs: Index) -> Bool {
+ lhs.index == rhs.index
+ }
+ }
+
+ public var startIndex: Index {
+ Index(index: base.startIndex, previous: nil)
+ }
+
+ public var endIndex: Index {
+ var iterator = makeIterator()
+ guard let initial = iterator.next() else { return startIndex }
+ let previous = IteratorSequence(iterator).dropLast().reduce(initial, transform)
+ return Index(index: base.endIndex, previous: previous)
+ }
+
+ public subscript(position: Index) -> Base.Element {
+ let element = base[position.index]
+ switch position.previous {
+ case .none: return element
+ case .some(let previous): return transform(previous, element)
+ }
+ }
+
+ public func index(after i: Index) -> Index {
+ Index(index: base.index(after: i.index), previous: self[i])
+ }
+
+ public func distance(from start: Index, to end: Index) -> Int {
+ base.distance(from: start.index, to: end.index)
+ }
+}
+
extension InclusiveReductions: LazySequenceProtocol
where Base: LazySequenceProtocol {}
+
+extension InclusiveReductions: LazyCollectionProtocol
+ where Base: LazyCollectionProtocol {}
diff --git a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
index fe258619..94ce9291 100644
--- a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
+++ b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
@@ -18,10 +18,12 @@ final class ReductionsTests: XCTestCase {
// MARK: - Exclusive Reductions
- func testExclusiveLazySequence() {
+ func testExclusiveLazy() {
XCTAssertEqualSequences((1...).prefix(4).lazy.reductions(0, +), [0, 1, 3, 6, 10])
XCTAssertEqualSequences((1...).prefix(1).lazy.reductions(0, +), [0, 1])
XCTAssertEqualSequences((1...).prefix(0).lazy.reductions(0, +), [0])
+
+ XCTAssertLazy([1].lazy.reductions(0, +))
}
func testExclusiveEager() {
@@ -35,10 +37,16 @@ final class ReductionsTests: XCTestCase {
// MARK: - Inclusive Reductions
- func testInclusiveLazySequence() {
+ func testInclusiveLazy() {
XCTAssertEqualSequences((1...).prefix(4).lazy.reductions(+), [1, 3, 6, 10])
XCTAssertEqualSequences((1...).prefix(1).lazy.reductions(+), [1])
XCTAssertEqualSequences((1...).prefix(0).lazy.reductions(+), [])
+
+ XCTAssertEqualCollections([1, 2, 3, 4].lazy.reductions(+), [1, 3, 6, 10])
+ XCTAssertEqualCollections([1].lazy.reductions(+), [1])
+ XCTAssertEqualCollections(EmptyCollection().lazy.reductions(+), [])
+
+ XCTAssertLazy([1].lazy.reductions(+))
}
func testInclusiveEager() {
diff --git a/Tests/SwiftAlgorithmsTests/TestUtilities.swift b/Tests/SwiftAlgorithmsTests/TestUtilities.swift
index 11520b93..b160caa6 100644
--- a/Tests/SwiftAlgorithmsTests/TestUtilities.swift
+++ b/Tests/SwiftAlgorithmsTests/TestUtilities.swift
@@ -68,6 +68,21 @@ func XCTAssertEqualSequences(
func XCTAssertLazySequence(_: S) {}
func XCTAssertLazyCollection(_: S) {}
+/// Asserts two collections are equal by using their indices to access elements.
+func XCTAssertEqualCollections(
+ _ expression1: @autoclosure () throws -> C1,
+ _ expression2: @autoclosure () throws -> C2,
+ _ message: @autoclosure () -> String = "",
+ file: StaticString = #file, line: UInt = #line
+) rethrows where C1.Element: Equatable, C1.Element == C2.Element {
+ let c1 = try expression1()
+ let c2 = try expression2()
+ XCTAssertEqual(c1.indices.count, c2.indices.count, message(), file: file, line: line)
+ for index in zip(c1.indices, c2.indices) {
+ XCTAssertEqual(c1[index.0], c2[index.1], message(), file: file, line: line)
+ }
+}
+
/// Tests that all index traversal methods behave as expected.
///
/// Verifies the correctness of the implementations of `startIndex`, `endIndex`,
From 0c60ad759e3488ee6cd5ef3366ade39d34c8574d Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Fri, 27 Nov 2020 16:49:53 +0000
Subject: [PATCH 21/45] Update guide
---
Guides/Reductions.md | 38 +++++++++++++++++++++++++++-----------
1 file changed, 27 insertions(+), 11 deletions(-)
diff --git a/Guides/Reductions.md b/Guides/Reductions.md
index dfd9405f..a86c5582 100644
--- a/Guides/Reductions.md
+++ b/Guides/Reductions.md
@@ -13,33 +13,42 @@ let runningTotal = (1...5).reductions(0, +)
print(Array(runningTotal))
// prints [0, 1, 3, 6, 10, 15]
-let runningMinimum = [3, 4, 2, 3, 1].reductions(.max, min)
+let runningTotal = (1...5).reductions(+)
+print(Array(runningTotal))
+// prints [1, 3, 6, 10, 15]
+
+let runningMinimum = [3, 4, 2, 3, 1].reductions(min)
print(Array(runningMinimum))
// prints [3, 3, 2, 2, 1]
```
## Detailed Design
-One new method is added to sequences:
+One pair of methods are added to `LazySequenceProtocol` for a lazily evaluated
+sequence and another pair are added to `Sequence` which are eagerly evaluated.
```swift
extension LazySequenceProtocol {
- func reductions(
- _ initial: Result,
- _ transform: @escaping (Result, Element) -> Result
- ) -> Reductions
-}
-extension Sequence {
public func reductions(
_ initial: Result,
- _ transform: (Result, Element) throws -> Result
- ) rethrows -> [Result]
+ _ transform: @escaping (Result, Element) -> Result
+ ) -> ExclusiveReductions
+
+ public func reductions(
+ _ transform: @escaping (Element, Element) -> Element
+ ) -> InclusiveReductions
}
```
```swift
-extension Collection {
+extension Sequence {
+
+ public func reductions(
+ _ initial: Result,
+ _ transform: (Result, Element) throws -> Result
+ ) rethrows -> [Result]
+
public func reductions(
_ transform: (Element, Element) throws -> Element
) rethrows -> [Element]
@@ -76,6 +85,13 @@ a good job at explaining the relation between the two.
approachable—I feel like I can extrapolate the expected behavior purely from my
familiarity with `reduce`.
+As part of early discussions, it was decided to have two variants, one which
+takes an initial value to use for the first element in the returned sequence,
+and another which uses the first value of the base sequence as the initial
+value. C++ calls these variants exclusive and inclusive respectively and so
+these terms carry through as the name for the lazy sequences;
+`ExclusiveReductions` and `InclusiveReductions`.
+
[SE-0045]: https://forums.swift.org/t/review-se-0045-add-scan-prefix-while-drop-while-and-iterate-to-the-stdlib/2382
[Issue 25]: https://github.com/apple/swift-algorithms/issues/25
[Brent_Royal-Gordon]: https://forums.swift.org/t/review-se-0045-add-scan-prefix-while-drop-while-and-iterate-to-the-stdlib/2382/6
From 2a41ecbb12e28e00b74fc8c2b0e668b87452e9da Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Fri, 27 Nov 2020 16:53:26 +0000
Subject: [PATCH 22/45] Add links for C++ implementations
---
Guides/Reductions.md | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/Guides/Reductions.md b/Guides/Reductions.md
index a86c5582..00280dec 100644
--- a/Guides/Reductions.md
+++ b/Guides/Reductions.md
@@ -100,8 +100,9 @@ these terms carry through as the name for the lazy sequences;
### Comparison with other langauges
-**C++:** As of C++17, the `` library includes an `inclusive_scan`
-function.
+**C++:** As of C++17, the `` library includes both
+[`exclusive_scan`][C++ Exclusive] and [`inclusive_scan`][C++ Inclusive]
+functions.
**[Clojure][Clojure]:** Clojure 1.2 added a `reductions` function.
@@ -114,6 +115,8 @@ parameter.
**[Rust][Rust]:** Rust provides a `scan` function.
+[C++ Exclusive]: https://en.cppreference.com/w/cpp/algorithm/exclusive_scan
+[C++ Inclusive]: https://en.cppreference.com/w/cpp/algorithm/inclusive_scan
[Clojure]: http://clojure.github.io/clojure/clojure.core-api.html#clojure.core/reductions
[Haskell]: http://hackage.haskell.org/package/base-4.8.2.0/docs/Prelude.html#v:scanl
[Rust]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.scan
From aac96c348c2bcf48f149665e17229e0180095d68 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Sat, 28 Nov 2020 21:56:34 +0000
Subject: [PATCH 23/45] Add conformance to Collection for ExclusiveReductions
---
Sources/Algorithms/Reductions.swift | 72 +++++++++++++++++++
.../ReductionsTests.swift | 4 ++
2 files changed, 76 insertions(+)
diff --git a/Sources/Algorithms/Reductions.swift b/Sources/Algorithms/Reductions.swift
index 95335463..33ef533d 100644
--- a/Sources/Algorithms/Reductions.swift
+++ b/Sources/Algorithms/Reductions.swift
@@ -89,9 +89,81 @@ extension ExclusiveReductions: Sequence {
}
}
+extension ExclusiveReductions: Collection where Base: Collection {
+ public struct Index: Comparable {
+ enum Representation {
+ case start
+ case base(index: Base.Index, previous: Result)
+ }
+ let representation: Representation
+
+ public static func < (lhs: Index, rhs: Index) -> Bool {
+ switch (lhs.representation, rhs.representation) {
+ case (_, .start): return false
+ case (.start, _): return true
+ case let (.base(lhs, _), .base(rhs, _)): return lhs < rhs
+ }
+ }
+
+ public static func == (lhs: Index, rhs: Index) -> Bool {
+ switch (lhs.representation, rhs.representation) {
+ case (.start, .start): return true
+ case let (.base(lhs, _), .base(rhs, _)): return lhs == rhs
+ default: return false
+ }
+ }
+
+ static func base(index: Base.Index, previous: Result) -> Self {
+ Self(representation: .base(index: index, previous: previous))
+ }
+ }
+
+ public var startIndex: Index {
+ Index(representation: .start)
+ }
+
+ public var endIndex: Index {
+ let previous = base.reduce(initial, transform)
+ return .base(index: base.endIndex, previous: previous)
+ }
+
+ public subscript(position: Index) -> Result {
+ switch position.representation {
+ case .start: return initial
+ case let .base(index, previous): return transform(previous, base[index])
+ }
+ }
+
+ public func index(after i: Index) -> Index {
+ switch i.representation {
+ case .start:
+ return .base(index: base.startIndex, previous: initial)
+ case let .base(index, previous):
+ return .base(index: base.index(after: index),
+ previous: transform(previous, base[index]))
+ }
+ }
+
+ public func distance(from start: Index, to end: Index) -> Int {
+ switch (start.representation, end.representation) {
+ case (.start, .start):
+ return 0
+ case let (.start, .base(end, _)):
+ return base.distance(from: base.startIndex, to: end) + 1
+ case let (.base(start, _), .base(end, _)):
+ return base.distance(from: start, to: end)
+ case let (.base(start, _), .start):
+ return base.distance(from: start, to: base.startIndex) - 1
+ }
+ }
+}
+
extension ExclusiveReductions: LazySequenceProtocol
where Base: LazySequenceProtocol {}
+extension ExclusiveReductions: LazyCollectionProtocol
+ where Base: LazyCollectionProtocol {}
+
// MARK: - Inclusive Reductions
extension LazySequenceProtocol {
diff --git a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
index 94ce9291..88c59faf 100644
--- a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
+++ b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
@@ -23,6 +23,10 @@ final class ReductionsTests: XCTestCase {
XCTAssertEqualSequences((1...).prefix(1).lazy.reductions(0, +), [0, 1])
XCTAssertEqualSequences((1...).prefix(0).lazy.reductions(0, +), [0])
+ XCTAssertEqualCollections([1, 2, 3, 4].lazy.reductions(0, +), [0, 1, 3, 6, 10])
+ XCTAssertEqualCollections([1].lazy.reductions(0, +), [0, 1])
+ XCTAssertEqualCollections(EmptyCollection().lazy.reductions(0, +), [0])
+
XCTAssertLazy([1].lazy.reductions(0, +))
}
From a61878f508df8099a525b2fd339d17b3e2350299 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Sat, 28 Nov 2020 23:42:35 +0000
Subject: [PATCH 24/45] Improve ergonomics of ExclusiveReductions' index
representation
---
Sources/Algorithms/Reductions.swift | 54 ++++++++++++++++++-----------
1 file changed, 34 insertions(+), 20 deletions(-)
diff --git a/Sources/Algorithms/Reductions.swift b/Sources/Algorithms/Reductions.swift
index 33ef533d..8acd3c04 100644
--- a/Sources/Algorithms/Reductions.swift
+++ b/Sources/Algorithms/Reductions.swift
@@ -93,7 +93,8 @@ extension ExclusiveReductions: Collection where Base: Collection {
public struct Index: Comparable {
enum Representation {
case start
- case base(index: Base.Index, previous: Result)
+ case base(index: Base.Index, result: Result)
+ case end
}
let representation: Representation
@@ -101,6 +102,8 @@ extension ExclusiveReductions: Collection where Base: Collection {
switch (lhs.representation, rhs.representation) {
case (_, .start): return false
case (.start, _): return true
+ case (.end, _): return false
+ case (_, .end): return true
case let (.base(lhs, _), .base(rhs, _)): return lhs < rhs
}
}
@@ -108,39 +111,40 @@ extension ExclusiveReductions: Collection where Base: Collection {
public static func == (lhs: Index, rhs: Index) -> Bool {
switch (lhs.representation, rhs.representation) {
case (.start, .start): return true
+ case (.end, .end): return true
case let (.base(lhs, _), .base(rhs, _)): return lhs == rhs
default: return false
}
}
- static func base(index: Base.Index, previous: Result) -> Self {
- Self(representation: .base(index: index, previous: previous))
+ static func base(index: Base.Index, result: Result) -> Self {
+ Self(representation: .base(index: index, result: result))
}
}
- public var startIndex: Index {
- Index(representation: .start)
- }
-
- public var endIndex: Index {
- let previous = base.reduce(initial, transform)
- return .base(index: base.endIndex, previous: previous)
- }
+ public var startIndex: Index { Index(representation: .start) }
+ public var endIndex: Index { Index(representation: .end) }
public subscript(position: Index) -> Result {
switch position.representation {
case .start: return initial
- case let .base(index, previous): return transform(previous, base[index])
+ case .base(_, let result): return result
+ case .end: fatalError("Cannot get element of end index.")
}
}
public func index(after i: Index) -> Index {
+ func index(base index: Base.Index, previous: Result) -> Index {
+ guard index != base.endIndex else { return endIndex }
+ return .base(index: index, result: transform(previous, base[index]))
+ }
switch i.representation {
case .start:
- return .base(index: base.startIndex, previous: initial)
- case let .base(index, previous):
- return .base(index: base.index(after: index),
- previous: transform(previous, base[index]))
+ return index(base: base.startIndex, previous: initial)
+ case let .base(i, result):
+ return index(base: base.index(after: i), previous: result)
+ case .end:
+ fatalError("Cannot get index after end index.")
}
}
@@ -148,12 +152,22 @@ extension ExclusiveReductions: Collection where Base: Collection {
switch (start.representation, end.representation) {
case (.start, .start):
return 0
- case let (.start, .base(end, _)):
- return base.distance(from: base.startIndex, to: end) + 1
+ case let (.start, .base(index, _)):
+ return base.distance(from: base.startIndex, to: index) + 1
+ case (.start, .end):
+ return base.distance(from: base.startIndex, to: base.endIndex) + 1
+ case let (.base(index, _), .start):
+ return base.distance(from: index, to: base.startIndex) - 1
case let (.base(start, _), .base(end, _)):
return base.distance(from: start, to: end)
- case let (.base(start, _), .start):
- return base.distance(from: start, to: base.startIndex) - 1
+ case let (.base(index, _), .end):
+ return base.distance(from: index, to: base.endIndex)
+ case (.end, .start):
+ return base.distance(from: base.endIndex, to: base.startIndex) - 1
+ case let (.end, .base(index, _)):
+ return base.distance(from: base.endIndex, to: index)
+ case (.end, .end):
+ return 0
}
}
}
From 058edc8fe56d47673176a5cb6d131e4075e76f29 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Sun, 29 Nov 2020 00:05:12 +0000
Subject: [PATCH 25/45] Improve ergonomics of InclusiveReductions' index
representation by sharing the Index implementation
---
Sources/Algorithms/Reductions.swift | 130 ++++++++++++++++------------
1 file changed, 76 insertions(+), 54 deletions(-)
diff --git a/Sources/Algorithms/Reductions.swift b/Sources/Algorithms/Reductions.swift
index 8acd3c04..3d97e1f2 100644
--- a/Sources/Algorithms/Reductions.swift
+++ b/Sources/Algorithms/Reductions.swift
@@ -90,37 +90,7 @@ extension ExclusiveReductions: Sequence {
}
extension ExclusiveReductions: Collection where Base: Collection {
- public struct Index: Comparable {
- enum Representation {
- case start
- case base(index: Base.Index, result: Result)
- case end
- }
- let representation: Representation
-
- public static func < (lhs: Index, rhs: Index) -> Bool {
- switch (lhs.representation, rhs.representation) {
- case (_, .start): return false
- case (.start, _): return true
- case (.end, _): return false
- case (_, .end): return true
- case let (.base(lhs, _), .base(rhs, _)): return lhs < rhs
- }
- }
-
- public static func == (lhs: Index, rhs: Index) -> Bool {
- switch (lhs.representation, rhs.representation) {
- case (.start, .start): return true
- case (.end, .end): return true
- case let (.base(lhs, _), .base(rhs, _)): return lhs == rhs
- default: return false
- }
- }
-
- static func base(index: Base.Index, result: Result) -> Self {
- Self(representation: .base(index: index, result: result))
- }
- }
+ public typealias Index = ReductionsIndex
public var startIndex: Index { Index(representation: .start) }
public var endIndex: Index { Index(representation: .end) }
@@ -228,44 +198,62 @@ extension InclusiveReductions: Sequence {
}
extension InclusiveReductions: Collection where Base: Collection {
- public struct Index: Comparable {
- let index: Base.Index
- let previous: Element?
-
- public static func < (lhs: Index, rhs: Index) -> Bool {
- lhs.index < rhs.index
- }
-
- public static func == (lhs: Index, rhs: Index) -> Bool {
- lhs.index == rhs.index
- }
- }
+ public typealias Index = ReductionsIndex
public var startIndex: Index {
- Index(index: base.startIndex, previous: nil)
+ guard base.startIndex != base.endIndex else { return endIndex }
+ return Index(representation: .start)
}
public var endIndex: Index {
- var iterator = makeIterator()
- guard let initial = iterator.next() else { return startIndex }
- let previous = IteratorSequence(iterator).dropLast().reduce(initial, transform)
- return Index(index: base.endIndex, previous: previous)
+ Index(representation: .end)
}
public subscript(position: Index) -> Base.Element {
- let element = base[position.index]
- switch position.previous {
- case .none: return element
- case .some(let previous): return transform(previous, element)
+ switch position.representation {
+ case .start: return base[base.startIndex]
+ case .base(_, let result): return result
+ case .end: fatalError("Cannot get element of end index.")
}
}
public func index(after i: Index) -> Index {
- Index(index: base.index(after: i.index), previous: self[i])
+ func index(base index: Base.Index, previous: Base.Element) -> Index {
+ guard index != base.endIndex else { return endIndex }
+ return .base(index: index, result: transform(previous, base[index]))
+ }
+ switch i.representation {
+ case .start:
+ let i = base.startIndex
+ return index(base: base.index(after: i), previous: base[i])
+ case let .base(i, element):
+ return index(base: base.index(after: i), previous: element)
+ case .end:
+ fatalError("Cannot get index after end index.")
+ }
}
public func distance(from start: Index, to end: Index) -> Int {
- base.distance(from: start.index, to: end.index)
+ switch (start.representation, end.representation) {
+ case (.start, .start):
+ return 0
+ case let (.start, .base(index, _)):
+ return base.distance(from: base.startIndex, to: index)
+ case (.start, .end):
+ return base.distance(from: base.startIndex, to: base.endIndex)
+ case let (.base(index, _), .start):
+ return base.distance(from: index, to: base.startIndex)
+ case let (.base(start, _), .base(end, _)):
+ return base.distance(from: start, to: end)
+ case let (.base(index, _), .end):
+ return base.distance(from: index, to: base.endIndex)
+ case (.end, .start):
+ return base.distance(from: base.endIndex, to: base.startIndex)
+ case let (.end, .base(index, _)):
+ return base.distance(from: base.endIndex, to: index)
+ case (.end, .end):
+ return 0
+ }
}
}
@@ -274,3 +262,37 @@ extension InclusiveReductions: LazySequenceProtocol
extension InclusiveReductions: LazyCollectionProtocol
where Base: LazyCollectionProtocol {}
+
+// MARK: - Shared ReductionsIndex
+
+public struct ReductionsIndex: Comparable {
+ enum Representation {
+ case start
+ case base(index: BaseIndex, result: Result)
+ case end
+ }
+ let representation: Representation
+
+ public static func < (lhs: Self, rhs: Self) -> Bool {
+ switch (lhs.representation, rhs.representation) {
+ case (_, .start): return false
+ case (.start, _): return true
+ case (.end, _): return false
+ case (_, .end): return true
+ case let (.base(lhs, _), .base(rhs, _)): return lhs < rhs
+ }
+ }
+
+ public static func == (lhs: Self, rhs: Self) -> Bool {
+ switch (lhs.representation, rhs.representation) {
+ case (.start, .start): return true
+ case (.end, .end): return true
+ case let (.base(lhs, _), .base(rhs, _)): return lhs == rhs
+ default: return false
+ }
+ }
+
+ static func base(index: BaseIndex, result: Result) -> Self {
+ Self(representation: .base(index: index, result: result))
+ }
+}
From a2219970111821204ed4615abca09d5477eae4c7 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Sun, 29 Nov 2020 00:12:17 +0000
Subject: [PATCH 26/45] Tidy up internal function
---
Sources/Algorithms/Reductions.swift | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/Sources/Algorithms/Reductions.swift b/Sources/Algorithms/Reductions.swift
index 3d97e1f2..749aaa90 100644
--- a/Sources/Algorithms/Reductions.swift
+++ b/Sources/Algorithms/Reductions.swift
@@ -218,16 +218,16 @@ extension InclusiveReductions: Collection where Base: Collection {
}
public func index(after i: Index) -> Index {
- func index(base index: Base.Index, previous: Base.Element) -> Index {
+ func index(after i: Base.Index, previous: Base.Element) -> Index {
+ let index = base.index(after: i)
guard index != base.endIndex else { return endIndex }
return .base(index: index, result: transform(previous, base[index]))
}
switch i.representation {
case .start:
- let i = base.startIndex
- return index(base: base.index(after: i), previous: base[i])
+ return index(after: base.startIndex, previous: base[base.startIndex])
case let .base(i, element):
- return index(base: base.index(after: i), previous: element)
+ return index(after: i, previous: element)
case .end:
fatalError("Cannot get index after end index.")
}
From ab40e33a68aadc49254af3dc80f9a7b1d3702ad9 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Sun, 29 Nov 2020 10:44:53 +0000
Subject: [PATCH 27/45] Add exclusive eager version of reductions(into:_:)
---
Sources/Algorithms/Reductions.swift | 16 +++++++++++++---
Tests/SwiftAlgorithmsTests/ReductionsTests.swift | 8 ++++++++
2 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/Sources/Algorithms/Reductions.swift b/Sources/Algorithms/Reductions.swift
index 749aaa90..f707ff2a 100644
--- a/Sources/Algorithms/Reductions.swift
+++ b/Sources/Algorithms/Reductions.swift
@@ -47,14 +47,24 @@ extension Sequence {
_ transform: (Result, Element) throws -> Result
) rethrows -> [Result] {
+ var result = initial
+ return try reductions(into: &result) { result, element in
+ result = try transform(result, element)
+ }
+ }
+
+ public func reductions(
+ into initial: inout Result,
+ _ transform: (inout Result, Element) throws -> Void
+ ) rethrows -> [Result] {
+
var output = [Result]()
output.reserveCapacity(underestimatedCount + 1)
output.append(initial)
- var result = initial
for element in self {
- result = try transform(result, element)
- output.append(result)
+ try transform(&initial, element)
+ output.append(initial)
}
return output
diff --git a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
index 88c59faf..08ed2f2d 100644
--- a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
+++ b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
@@ -35,6 +35,14 @@ final class ReductionsTests: XCTestCase {
XCTAssertEqual([1].reductions(0, +), [0, 1])
XCTAssertEqual(EmptyCollection().reductions(0, +), [0])
+ var value0 = 0
+ var value1 = 0
+ var value2 = 0
+ func add(lhs: inout Int, rhs: Int) { lhs += rhs }
+ XCTAssertEqual([1, 2, 3, 4].reductions(into: &value0, add), [0, 1, 3, 6, 10])
+ XCTAssertEqual([1].reductions(into: &value1, add), [0, 1])
+ XCTAssertEqual(EmptyCollection().reductions(into: &value2, add), [0])
+
XCTAssertNoThrow(try [].reductions(0) { _, _ in throw TestError() })
XCTAssertThrowsError(try [1].reductions(0) { _, _ in throw TestError() })
}
From 6b017370f0744217a6985412ad137f96c0372489 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Thu, 3 Dec 2020 08:03:14 +0000
Subject: [PATCH 28/45] Use new lazy assertion functions
---
Tests/SwiftAlgorithmsTests/ReductionsTests.swift | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
index 08ed2f2d..920a759d 100644
--- a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
+++ b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
@@ -27,7 +27,9 @@ final class ReductionsTests: XCTestCase {
XCTAssertEqualCollections([1].lazy.reductions(0, +), [0, 1])
XCTAssertEqualCollections(EmptyCollection().lazy.reductions(0, +), [0])
- XCTAssertLazy([1].lazy.reductions(0, +))
+ XCTAssertLazySequence((1...).prefix(1).lazy.reductions(0, +))
+ XCTAssertLazySequence([1].lazy.reductions(0, +))
+ XCTAssertLazyCollection([1].lazy.reductions(0, +))
}
func testExclusiveEager() {
@@ -58,7 +60,9 @@ final class ReductionsTests: XCTestCase {
XCTAssertEqualCollections([1].lazy.reductions(+), [1])
XCTAssertEqualCollections(EmptyCollection().lazy.reductions(+), [])
- XCTAssertLazy([1].lazy.reductions(+))
+ XCTAssertLazySequence((1...).prefix(1).lazy.reductions(+))
+ XCTAssertLazySequence([1].lazy.reductions(+))
+ XCTAssertLazyCollection([1].lazy.reductions(+))
}
func testInclusiveEager() {
From 714f46bc63984947a32c910d2afd003215fde1af Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Thu, 25 Feb 2021 08:32:21 +0000
Subject: [PATCH 29/45] Correct the complexity claims for reductions
---
Guides/Reductions.md | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/Guides/Reductions.md b/Guides/Reductions.md
index 00280dec..f362230c 100644
--- a/Guides/Reductions.md
+++ b/Guides/Reductions.md
@@ -24,7 +24,7 @@ print(Array(runningMinimum))
## Detailed Design
-One pair of methods are added to `LazySequenceProtocol` for a lazily evaluated
+One pair of methods are added to `LazySequenceProtocol` for a lazily evaluated.
sequence and another pair are added to `Sequence` which are eagerly evaluated.
```swift
@@ -57,7 +57,8 @@ extension Sequence {
### Complexity
-Calling these methods is O(_1_).
+Calling the lazy methods, those defined on `LazySequenceProtocol`, is O(_1_).
+Calling the eager methods, those returning an array, is O(_n_).
### Naming
From 5182363625df0bfaaa1db5be9d5ec35eaa699acc Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Thu, 25 Feb 2021 08:51:04 +0000
Subject: [PATCH 30/45] Separate the index types
This will allow for future changes to either implementation without causing a breakage for the other.
---
Sources/Algorithms/Reductions.swift | 59 ++++++++++++++++++-----------
1 file changed, 36 insertions(+), 23 deletions(-)
diff --git a/Sources/Algorithms/Reductions.swift b/Sources/Algorithms/Reductions.swift
index f707ff2a..38fa1ad3 100644
--- a/Sources/Algorithms/Reductions.swift
+++ b/Sources/Algorithms/Reductions.swift
@@ -100,7 +100,15 @@ extension ExclusiveReductions: Sequence {
}
extension ExclusiveReductions: Collection where Base: Collection {
- public typealias Index = ReductionsIndex
+ public struct Index: Comparable {
+ let representation: ReductionsIndexRepresentation
+ public static func < (lhs: Self, rhs: Self) -> Bool {
+ lhs.representation < rhs.representation
+ }
+ static func base(index: Base.Index, result: Result) -> Self {
+ Self(representation: .base(index: index, result: result))
+ }
+ }
public var startIndex: Index { Index(representation: .start) }
public var endIndex: Index { Index(representation: .end) }
@@ -208,7 +216,15 @@ extension InclusiveReductions: Sequence {
}
extension InclusiveReductions: Collection where Base: Collection {
- public typealias Index = ReductionsIndex
+ public struct Index: Comparable {
+ let representation: ReductionsIndexRepresentation
+ public static func < (lhs: Self, rhs: Self) -> Bool {
+ lhs.representation < rhs.representation
+ }
+ static func base(index: Base.Index, result: Base.Element) -> Self {
+ Self(representation: .base(index: index, result: result))
+ }
+ }
public var startIndex: Index {
guard base.startIndex != base.endIndex else { return endIndex }
@@ -273,36 +289,33 @@ extension InclusiveReductions: LazySequenceProtocol
extension InclusiveReductions: LazyCollectionProtocol
where Base: LazyCollectionProtocol {}
-// MARK: - Shared ReductionsIndex
+// MARK: - ReductionsIndexRepresentation
-public struct ReductionsIndex: Comparable {
- enum Representation {
- case start
- case base(index: BaseIndex, result: Result)
- case end
- }
- let representation: Representation
-
- public static func < (lhs: Self, rhs: Self) -> Bool {
- switch (lhs.representation, rhs.representation) {
- case (_, .start): return false
- case (.start, _): return true
- case (.end, _): return false
- case (_, .end): return true
- case let (.base(lhs, _), .base(rhs, _)): return lhs < rhs
- }
- }
+enum ReductionsIndexRepresentation {
+ case start
+ case base(index: BaseIndex, result: Result)
+ case end
+}
+extension ReductionsIndexRepresentation: Equatable {
public static func == (lhs: Self, rhs: Self) -> Bool {
- switch (lhs.representation, rhs.representation) {
+ switch (lhs, rhs) {
case (.start, .start): return true
case (.end, .end): return true
case let (.base(lhs, _), .base(rhs, _)): return lhs == rhs
default: return false
}
}
+}
- static func base(index: BaseIndex, result: Result) -> Self {
- Self(representation: .base(index: index, result: result))
+extension ReductionsIndexRepresentation: Comparable {
+ public static func < (lhs: Self, rhs: Self) -> Bool {
+ switch (lhs, rhs) {
+ case (_, .start): return false
+ case (.start, _): return true
+ case (.end, _): return false
+ case (_, .end): return true
+ case let (.base(lhs, _), .base(rhs, _)): return lhs < rhs
+ }
}
}
From 84b6ddc05a721a5f224565ca583614041fbeb213 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Mon, 8 Mar 2021 08:32:55 +0000
Subject: [PATCH 31/45] Update guide to reflect that arrays are returned
directly
---
Guides/Reductions.md | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/Guides/Reductions.md b/Guides/Reductions.md
index f362230c..6f964c03 100644
--- a/Guides/Reductions.md
+++ b/Guides/Reductions.md
@@ -9,16 +9,16 @@ This has the behaviour of reduce, but instead of returning the final result
value, it returns the a sequence of the results returned from each element.
```swift
-let runningTotal = (1...5).reductions(0, +)
-print(Array(runningTotal))
+let runningTotalExclusive = (1...5).reductions(0, +)
+print(runningTotalExclusive)
// prints [0, 1, 3, 6, 10, 15]
-let runningTotal = (1...5).reductions(+)
-print(Array(runningTotal))
+let runningTotalInclusive = (1...5).reductions(+)
+print(runningTotalInclusive)
// prints [1, 3, 6, 10, 15]
let runningMinimum = [3, 4, 2, 3, 1].reductions(min)
-print(Array(runningMinimum))
+print(runningMinimum)
// prints [3, 3, 2, 2, 1]
```
From fb004f7611cf2f6e1039947d2bd284c4e5a8f133 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Mon, 8 Mar 2021 08:43:18 +0000
Subject: [PATCH 32/45] Add scan as deprecated methods
---
Sources/Algorithms/Reductions.swift | 52 +++++++++++++++++++++++++++++
1 file changed, 52 insertions(+)
diff --git a/Sources/Algorithms/Reductions.swift b/Sources/Algorithms/Reductions.swift
index 38fa1ad3..a5e7fcde 100644
--- a/Sources/Algorithms/Reductions.swift
+++ b/Sources/Algorithms/Reductions.swift
@@ -319,3 +319,55 @@ extension ReductionsIndexRepresentation: Comparable {
}
}
}
+
+// MARK: - Scan
+
+extension LazySequenceProtocol {
+
+ @available(*, deprecated, message: "Use reductions(_:_:) instead.")
+ public func scan(
+ _ initial: Result,
+ _ transform: @escaping (Result, Element) -> Result
+ ) -> ExclusiveReductions {
+ reductions(initial, transform)
+ }
+}
+
+extension Sequence {
+
+ @available(*, deprecated, message: "Use reductions(_:_:) instead.")
+ public func scan(
+ _ initial: Result,
+ _ transform: (Result, Element) throws -> Result
+ ) rethrows -> [Result] {
+ try reductions(initial, transform)
+ }
+
+ @available(*, deprecated, message: "Use reductions(into:_:) instead.")
+ public func scan(
+ into initial: inout Result,
+ _ transform: (inout Result, Element) throws -> Void
+ ) rethrows -> [Result] {
+ try reductions(into: &initial, transform)
+ }
+}
+
+extension LazySequenceProtocol {
+
+ @available(*, deprecated, message: "Use reductions(_:) instead.")
+ public func scan(
+ _ transform: @escaping (Element, Element) -> Element
+ ) -> InclusiveReductions {
+ reductions(transform)
+ }
+}
+
+extension Sequence {
+
+ @available(*, deprecated, message: "Use reductions(_:) instead.")
+ public func scan(
+ _ transform: (Element, Element) throws -> Element
+ ) rethrows -> [Element] {
+ try reductions(transform)
+ }
+}
From 7464876ef9c6c6edfe6b93d2d8469d53cd4b3b28 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Thu, 18 Mar 2021 16:47:46 +0000
Subject: [PATCH 33/45] Add lazy overload of reductions(into:_:)
---
Sources/Algorithms/Reductions.swift | 33 ++++++++++++++++---
.../ReductionsTests.swift | 8 +++++
2 files changed, 37 insertions(+), 4 deletions(-)
diff --git a/Sources/Algorithms/Reductions.swift b/Sources/Algorithms/Reductions.swift
index a5e7fcde..39f540ae 100644
--- a/Sources/Algorithms/Reductions.swift
+++ b/Sources/Algorithms/Reductions.swift
@@ -36,6 +36,17 @@ extension LazySequenceProtocol {
_ initial: Result,
_ transform: @escaping (Result, Element) -> Result
) -> ExclusiveReductions {
+
+ var result = initial
+ return reductions(into: &result) { result, element in
+ result = transform(result, element)
+ }
+ }
+
+ public func reductions(
+ into initial: inout Result,
+ _ transform: @escaping (inout Result, Element) -> Void
+ ) -> ExclusiveReductions {
ExclusiveReductions(base: self, initial: initial, transform: transform)
}
}
@@ -76,18 +87,22 @@ extension Sequence {
public struct ExclusiveReductions {
let base: Base
let initial: Result
- let transform: (Result, Base.Element) -> Result
+ let transform: (inout Result, Base.Element) -> Void
}
extension ExclusiveReductions: Sequence {
public struct Iterator: IteratorProtocol {
var iterator: Base.Iterator
var current: Result?
- let transform: (Result, Base.Element) -> Result
+ let transform: (inout Result, Base.Element) -> Void
public mutating func next() -> Result? {
guard let result = current else { return nil }
- current = iterator.next().map { transform(result, $0) }
+ current = iterator.next().map { element in
+ var result = result
+ transform(&result, element)
+ return result
+ }
return result
}
}
@@ -124,7 +139,9 @@ extension ExclusiveReductions: Collection where Base: Collection {
public func index(after i: Index) -> Index {
func index(base index: Base.Index, previous: Result) -> Index {
guard index != base.endIndex else { return endIndex }
- return .base(index: index, result: transform(previous, base[index]))
+ var previous = previous
+ transform(&previous, base[index])
+ return .base(index: index, result: previous)
}
switch i.representation {
case .start:
@@ -331,6 +348,14 @@ extension LazySequenceProtocol {
) -> ExclusiveReductions {
reductions(initial, transform)
}
+
+ @available(*, deprecated, message: "Use reductions(into:_:) instead.")
+ public func scan(
+ into initial: inout Result,
+ _ transform: @escaping (inout Result, Element) -> Void
+ ) -> ExclusiveReductions {
+ reductions(into: &initial, transform)
+ }
}
extension Sequence {
diff --git a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
index 920a759d..0f57b56b 100644
--- a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
+++ b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
@@ -27,6 +27,14 @@ final class ReductionsTests: XCTestCase {
XCTAssertEqualCollections([1].lazy.reductions(0, +), [0, 1])
XCTAssertEqualCollections(EmptyCollection().lazy.reductions(0, +), [0])
+ var value0 = 0
+ var value1 = 0
+ var value2 = 0
+ func add(lhs: inout Int, rhs: Int) { lhs += rhs }
+ XCTAssertEqual([1, 2, 3, 4].lazy.reductions(into: &value0, add), [0, 1, 3, 6, 10])
+ XCTAssertEqual([1].lazy.reductions(into: &value1, add), [0, 1])
+ XCTAssertEqual(EmptyCollection().lazy.reductions(into: &value2, add), [0])
+
XCTAssertLazySequence((1...).prefix(1).lazy.reductions(0, +))
XCTAssertLazySequence([1].lazy.reductions(0, +))
XCTAssertLazyCollection([1].lazy.reductions(0, +))
From 174efb8c167ef28dbd3ffa3e3583f4c4275fc7a5 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Thu, 18 Mar 2021 19:10:27 +0000
Subject: [PATCH 34/45] Add the reductions(into:_:) functions
---
Guides/Reductions.md | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/Guides/Reductions.md b/Guides/Reductions.md
index 6f964c03..78b2f23e 100644
--- a/Guides/Reductions.md
+++ b/Guides/Reductions.md
@@ -24,8 +24,8 @@ print(runningMinimum)
## Detailed Design
-One pair of methods are added to `LazySequenceProtocol` for a lazily evaluated.
-sequence and another pair are added to `Sequence` which are eagerly evaluated.
+One trio of methods are added to `LazySequenceProtocol` for a lazily evaluated
+sequence and another trio are added to `Sequence` which are eagerly evaluated.
```swift
extension LazySequenceProtocol {
@@ -35,6 +35,11 @@ extension LazySequenceProtocol {
_ transform: @escaping (Result, Element) -> Result
) -> ExclusiveReductions
+ public func reductions(
+ into initial: inout Result,
+ _ transform: @escaping (inout Result, Element) -> Void
+ ) -> ExclusiveReductions
+
public func reductions(
_ transform: @escaping (Element, Element) -> Element
) -> InclusiveReductions
@@ -48,7 +53,12 @@ extension Sequence {
_ initial: Result,
_ transform: (Result, Element) throws -> Result
) rethrows -> [Result]
-
+
+ public func reductions(
+ into initial: inout Result,
+ _ transform: (inout Result, Element) throws -> Void
+ ) rethrows -> [Result]
+
public func reductions(
_ transform: (Element, Element) throws -> Element
) rethrows -> [Element]
From 1e47f860564036ea7cb05c21c2095fe176dab835 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Thu, 18 Mar 2021 19:14:36 +0000
Subject: [PATCH 35/45] More succinctly introduce reductions
---
Guides/Reductions.md | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/Guides/Reductions.md b/Guides/Reductions.md
index 78b2f23e..f9a4d3fa 100644
--- a/Guides/Reductions.md
+++ b/Guides/Reductions.md
@@ -5,8 +5,7 @@
Produces a sequence of values.
-This has the behaviour of reduce, but instead of returning the final result
-value, it returns the a sequence of the results returned from each element.
+This has the behaviour of reduce, but also returns all intermediate results.
```swift
let runningTotalExclusive = (1...5).reductions(0, +)
From a73c6818d5c52d966e2ec2deda848a4043e4c987 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Thu, 18 Mar 2021 19:21:43 +0000
Subject: [PATCH 36/45] Add a note about the deprecated scan functions
---
Guides/Reductions.md | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/Guides/Reductions.md b/Guides/Reductions.md
index f9a4d3fa..87e6bf61 100644
--- a/Guides/Reductions.md
+++ b/Guides/Reductions.md
@@ -73,7 +73,11 @@ Calling the eager methods, those returning an array, is O(_n_).
While the name `scan` is the term of art for this function, it has been
discussed that `reductions` aligns better with the existing `reduce` function
-and will aid newcomers that might not know the existing `scan` term.
+and will aid newcomers that might not know the existing `scan` term.
+
+Deprecated `scan` methods have been added for people who are familiar with the
+term, so they can easily discover the `reductions` methods via compiler
+deprecation warnings.
Below are two quotes from the Swift forum [discussion about SE-0045][SE-0045]
which proposed adding `scan` to the standard library and one from
From f6b42ad18f95f881db7d2c5145feb3893d0676b0 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Thu, 18 Mar 2021 19:37:44 +0000
Subject: [PATCH 37/45] Update documentation
---
Sources/Algorithms/Reductions.swift | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/Sources/Algorithms/Reductions.swift b/Sources/Algorithms/Reductions.swift
index 39f540ae..8f71f195 100644
--- a/Sources/Algorithms/Reductions.swift
+++ b/Sources/Algorithms/Reductions.swift
@@ -20,9 +20,8 @@ extension LazySequenceProtocol {
/// providing the initial value followed by these results as a sequence.
///
/// ```
- /// let values = [1, 2, 3, 4]
- /// let sequence = values.reductions(0, +)
- /// print(Array(sequence))
+ /// let runningTotal = [1, 2, 3, 4].lazy.reductions(0, +)
+ /// print(Array(runningTotal))
///
/// // prints [0, 1, 3, 6, 10]
/// ```
From f7b90b5d902b129673191e8f94aa95a673937108 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Thu, 18 Mar 2021 20:01:48 +0000
Subject: [PATCH 38/45] Add @inlinable and @usableFromInline
---
Sources/Algorithms/Reductions.swift | 157 ++++++++++++++++++++++++----
1 file changed, 134 insertions(+), 23 deletions(-)
diff --git a/Sources/Algorithms/Reductions.swift b/Sources/Algorithms/Reductions.swift
index 8f71f195..bde86857 100644
--- a/Sources/Algorithms/Reductions.swift
+++ b/Sources/Algorithms/Reductions.swift
@@ -31,6 +31,7 @@ extension LazySequenceProtocol {
/// - transform: A closure that combines the previously reduced result and
/// the next element in the receiving sequence.
/// - Returns: A sequence of transformed elements.
+ @inlinable
public func reductions(
_ initial: Result,
_ transform: @escaping (Result, Element) -> Result
@@ -42,6 +43,7 @@ extension LazySequenceProtocol {
}
}
+ @inlinable
public func reductions(
into initial: inout Result,
_ transform: @escaping (inout Result, Element) -> Void
@@ -52,6 +54,7 @@ extension LazySequenceProtocol {
extension Sequence {
+ @inlinable
public func reductions(
_ initial: Result,
_ transform: (Result, Element) throws -> Result
@@ -63,6 +66,7 @@ extension Sequence {
}
}
+ @inlinable
public func reductions(
into initial: inout Result,
_ transform: (inout Result, Element) throws -> Void
@@ -84,17 +88,50 @@ extension Sequence {
/// A sequence of applying a transform to the element of a sequence and the
/// previously transformed result.
public struct ExclusiveReductions {
- let base: Base
- let initial: Result
- let transform: (inout Result, Base.Element) -> Void
+ @usableFromInline
+ internal let base: Base
+
+ @usableFromInline
+ internal let initial: Result
+
+ @usableFromInline
+ internal let transform: (inout Result, Base.Element) -> Void
+
+ @usableFromInline
+ internal init(
+ base: Base,
+ initial: Result,
+ transform: @escaping (inout Result, Base.Element) -> Void
+ ) {
+ self.base = base
+ self.initial = initial
+ self.transform = transform
+ }
}
extension ExclusiveReductions: Sequence {
public struct Iterator: IteratorProtocol {
- var iterator: Base.Iterator
- var current: Result?
- let transform: (inout Result, Base.Element) -> Void
+ @usableFromInline
+ internal var iterator: Base.Iterator
+
+ @usableFromInline
+ internal var current: Result?
+
+ @usableFromInline
+ internal let transform: (inout Result, Base.Element) -> Void
+
+ @usableFromInline
+ internal init(
+ iterator: Base.Iterator,
+ current: Result? = nil,
+ transform: @escaping (inout Result, Base.Element) -> Void
+ ) {
+ self.iterator = iterator
+ self.current = current
+ self.transform = transform
+ }
+ @inlinable
public mutating func next() -> Result? {
guard let result = current else { return nil }
current = iterator.next().map { element in
@@ -106,6 +143,7 @@ extension ExclusiveReductions: Sequence {
}
}
+ @inlinable
public func makeIterator() -> Iterator {
Iterator(iterator: base.makeIterator(),
current: initial,
@@ -115,18 +153,33 @@ extension ExclusiveReductions: Sequence {
extension ExclusiveReductions: Collection where Base: Collection {
public struct Index: Comparable {
- let representation: ReductionsIndexRepresentation
+ @usableFromInline
+ internal let representation: ReductionsIndexRepresentation
+
+ @inlinable
public static func < (lhs: Self, rhs: Self) -> Bool {
lhs.representation < rhs.representation
}
- static func base(index: Base.Index, result: Result) -> Self {
+
+ @usableFromInline
+ internal static func base(index: Base.Index, result: Result) -> Self {
Self(representation: .base(index: index, result: result))
}
+
+ @usableFromInline
+ internal static var start: Self { Self(representation: .start) }
+
+ @usableFromInline
+ internal static var end: Self { Self(representation: .end) }
}
- public var startIndex: Index { Index(representation: .start) }
- public var endIndex: Index { Index(representation: .end) }
+ @inlinable
+ public var startIndex: Index { .start }
+ @inlinable
+ public var endIndex: Index { .end }
+
+ @inlinable
public subscript(position: Index) -> Result {
switch position.representation {
case .start: return initial
@@ -135,6 +188,7 @@ extension ExclusiveReductions: Collection where Base: Collection {
}
}
+ @inlinable
public func index(after i: Index) -> Index {
func index(base index: Base.Index, previous: Result) -> Index {
guard index != base.endIndex else { return endIndex }
@@ -152,6 +206,7 @@ extension ExclusiveReductions: Collection where Base: Collection {
}
}
+ @inlinable
public func distance(from start: Index, to end: Index) -> Int {
switch (start.representation, end.representation) {
case (.start, .start):
@@ -186,6 +241,7 @@ extension ExclusiveReductions: LazyCollectionProtocol
extension LazySequenceProtocol {
+ @inlinable
public func reductions(
_ transform: @escaping (Element, Element) -> Element
) -> InclusiveReductions {
@@ -194,6 +250,8 @@ extension LazySequenceProtocol {
}
extension Sequence {
+
+ @inlinable
public func reductions(
_ transform: (Element, Element) throws -> Element
) rethrows -> [Element] {
@@ -204,16 +262,45 @@ extension Sequence {
}
public struct InclusiveReductions {
- let base: Base
- let transform: (Base.Element, Base.Element) -> Base.Element
+ @usableFromInline
+ internal let base: Base
+
+ @usableFromInline
+ internal let transform: (Base.Element, Base.Element) -> Base.Element
+
+ @usableFromInline
+ internal init(
+ base: Base,
+ transform: @escaping (Base.Element, Base.Element) -> Base.Element
+ ) {
+ self.base = base
+ self.transform = transform
+ }
}
extension InclusiveReductions: Sequence {
public struct Iterator: IteratorProtocol {
- var iterator: Base.Iterator
- var element: Base.Element?
- let transform: (Base.Element, Base.Element) -> Base.Element
+ @usableFromInline
+ internal var iterator: Base.Iterator
+
+ @usableFromInline
+ internal var element: Base.Element?
+
+ @usableFromInline
+ internal let transform: (Base.Element, Base.Element) -> Base.Element
+
+ @usableFromInline
+ internal init(
+ iterator: Base.Iterator,
+ element: Base.Element? = nil,
+ transform: @escaping (Base.Element, Base.Element) -> Base.Element
+ ) {
+ self.iterator = iterator
+ self.element = element
+ self.transform = transform
+ }
+ @inlinable
public mutating func next() -> Base.Element? {
guard let previous = element else {
element = iterator.next()
@@ -225,6 +312,7 @@ extension InclusiveReductions: Sequence {
}
}
+ @inlinable
public func makeIterator() -> Iterator {
Iterator(iterator: base.makeIterator(),
transform: transform)
@@ -233,24 +321,36 @@ extension InclusiveReductions: Sequence {
extension InclusiveReductions: Collection where Base: Collection {
public struct Index: Comparable {
- let representation: ReductionsIndexRepresentation
+ @usableFromInline
+ internal let representation: ReductionsIndexRepresentation
+
+ @inlinable
public static func < (lhs: Self, rhs: Self) -> Bool {
lhs.representation < rhs.representation
}
- static func base(index: Base.Index, result: Base.Element) -> Self {
+
+ @usableFromInline
+ internal static func base(index: Base.Index, result: Base.Element) -> Self {
Self(representation: .base(index: index, result: result))
}
+
+ @usableFromInline
+ internal static var start: Self { Self(representation: .start) }
+
+ @usableFromInline
+ internal static var end: Self { Self(representation: .end) }
}
+ @inlinable
public var startIndex: Index {
guard base.startIndex != base.endIndex else { return endIndex }
- return Index(representation: .start)
+ return .start
}
- public var endIndex: Index {
- Index(representation: .end)
- }
+ @inlinable
+ public var endIndex: Index { .end }
+ @inlinable
public subscript(position: Index) -> Base.Element {
switch position.representation {
case .start: return base[base.startIndex]
@@ -259,6 +359,7 @@ extension InclusiveReductions: Collection where Base: Collection {
}
}
+ @inlinable
public func index(after i: Index) -> Index {
func index(after i: Base.Index, previous: Base.Element) -> Index {
let index = base.index(after: i)
@@ -275,6 +376,7 @@ extension InclusiveReductions: Collection where Base: Collection {
}
}
+ @inlinable
public func distance(from start: Index, to end: Index) -> Int {
switch (start.representation, end.representation) {
case (.start, .start):
@@ -307,6 +409,7 @@ extension InclusiveReductions: LazyCollectionProtocol
// MARK: - ReductionsIndexRepresentation
+@usableFromInline
enum ReductionsIndexRepresentation {
case start
case base(index: BaseIndex, result: Result)
@@ -314,7 +417,8 @@ enum ReductionsIndexRepresentation {
}
extension ReductionsIndexRepresentation: Equatable {
- public static func == (lhs: Self, rhs: Self) -> Bool {
+ @usableFromInline
+ static func == (lhs: Self, rhs: Self) -> Bool {
switch (lhs, rhs) {
case (.start, .start): return true
case (.end, .end): return true
@@ -325,7 +429,8 @@ extension ReductionsIndexRepresentation: Equatable {
}
extension ReductionsIndexRepresentation: Comparable {
- public static func < (lhs: Self, rhs: Self) -> Bool {
+ @usableFromInline
+ static func < (lhs: Self, rhs: Self) -> Bool {
switch (lhs, rhs) {
case (_, .start): return false
case (.start, _): return true
@@ -341,6 +446,7 @@ extension ReductionsIndexRepresentation: Comparable {
extension LazySequenceProtocol {
@available(*, deprecated, message: "Use reductions(_:_:) instead.")
+ @inlinable
public func scan(
_ initial: Result,
_ transform: @escaping (Result, Element) -> Result
@@ -349,6 +455,7 @@ extension LazySequenceProtocol {
}
@available(*, deprecated, message: "Use reductions(into:_:) instead.")
+ @inlinable
public func scan(
into initial: inout Result,
_ transform: @escaping (inout Result, Element) -> Void
@@ -360,6 +467,7 @@ extension LazySequenceProtocol {
extension Sequence {
@available(*, deprecated, message: "Use reductions(_:_:) instead.")
+ @inlinable
public func scan(
_ initial: Result,
_ transform: (Result, Element) throws -> Result
@@ -368,6 +476,7 @@ extension Sequence {
}
@available(*, deprecated, message: "Use reductions(into:_:) instead.")
+ @inlinable
public func scan(
into initial: inout Result,
_ transform: (inout Result, Element) throws -> Void
@@ -379,6 +488,7 @@ extension Sequence {
extension LazySequenceProtocol {
@available(*, deprecated, message: "Use reductions(_:) instead.")
+ @inlinable
public func scan(
_ transform: @escaping (Element, Element) -> Element
) -> InclusiveReductions {
@@ -389,6 +499,7 @@ extension LazySequenceProtocol {
extension Sequence {
@available(*, deprecated, message: "Use reductions(_:) instead.")
+ @inlinable
public func scan(
_ transform: (Element, Element) throws -> Element
) rethrows -> [Element] {
From e9abcdb68e0e4a98933b372700422e3ef43d7567 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Fri, 19 Mar 2021 12:02:47 +0000
Subject: [PATCH 39/45] Copy documentation to all variants of reductions
---
Sources/Algorithms/Reductions.swift | 104 +++++++++++++++++++++++++++-
1 file changed, 102 insertions(+), 2 deletions(-)
diff --git a/Sources/Algorithms/Reductions.swift b/Sources/Algorithms/Reductions.swift
index bde86857..4a395320 100644
--- a/Sources/Algorithms/Reductions.swift
+++ b/Sources/Algorithms/Reductions.swift
@@ -13,8 +13,8 @@
extension LazySequenceProtocol {
- /// Returns a sequence containing the results of combining the elements of
- /// the sequence using the given transform.
+ /// Returns a sequence containing the accumulated results of combining the
+ /// elements of the sequence using the given closure.
///
/// This can be seen as applying the reduce function to each element and
/// providing the initial value followed by these results as a sequence.
@@ -31,6 +31,8 @@ extension LazySequenceProtocol {
/// - transform: A closure that combines the previously reduced result and
/// the next element in the receiving sequence.
/// - Returns: A sequence of transformed elements.
+ ///
+ /// - Complexity: O(1)
@inlinable
public func reductions(
_ initial: Result,
@@ -43,6 +45,26 @@ extension LazySequenceProtocol {
}
}
+ /// Returns a sequence containing the accumulated results of combining the
+ /// elements of the sequence using the given closure.
+ ///
+ /// This can be seen as applying the reduce function to each element and
+ /// providing the initial value followed by these results as a sequence.
+ ///
+ /// ```
+ /// let runningTotal = [1, 2, 3, 4].lazy.reductions(into: 0, +)
+ /// print(Array(runningTotal))
+ ///
+ /// // prints [0, 1, 3, 6, 10]
+ /// ```
+ ///
+ /// - Parameters:
+ /// - initial: The value to use as the initial value.
+ /// - transform: A closure that combines the previously reduced result and
+ /// the next element in the receiving sequence.
+ /// - Returns: A sequence of transformed elements.
+ ///
+ /// - Complexity: O(1)
@inlinable
public func reductions(
into initial: inout Result,
@@ -54,6 +76,26 @@ extension LazySequenceProtocol {
extension Sequence {
+ /// Returns an array containing the accumulated results of combining the
+ /// elements of the sequence using the given closure.
+ ///
+ /// This can be seen as applying the reduce function to each element and
+ /// providing the initial value followed by these results as a sequence.
+ ///
+ /// ```
+ /// let runningTotal = [1, 2, 3, 4].reductions(0, +)
+ /// print(runningTotal)
+ ///
+ /// // prints [0, 1, 3, 6, 10]
+ /// ```
+ ///
+ /// - Parameters:
+ /// - initial: The value to use as the initial value.
+ /// - transform: A closure that combines the previously reduced result and
+ /// the next element in the receiving sequence.
+ /// - Returns: An array of transformed elements.
+ ///
+ /// - Complexity: O(n)
@inlinable
public func reductions(
_ initial: Result,
@@ -66,6 +108,26 @@ extension Sequence {
}
}
+ /// Returns an array containing the accumulated results of combining the
+ /// elements of the sequence using the given closure.
+ ///
+ /// This can be seen as applying the reduce function to each element and
+ /// providing the initial value followed by these results as a sequence.
+ ///
+ /// ```
+ /// let runningTotal = [1, 2, 3, 4].reductions(into: 0, +)
+ /// print(runningTotal)
+ ///
+ /// // prints [0, 1, 3, 6, 10]
+ /// ```
+ ///
+ /// - Parameters:
+ /// - initial: The value to use as the initial value.
+ /// - transform: A closure that combines the previously reduced result and
+ /// the next element in the receiving sequence.
+ /// - Returns: An array of transformed elements.
+ ///
+ /// - Complexity: O(n)
@inlinable
public func reductions(
into initial: inout Result,
@@ -241,6 +303,25 @@ extension ExclusiveReductions: LazyCollectionProtocol
extension LazySequenceProtocol {
+ /// Returns a sequence containing the accumulated results of combining the
+ /// elements of the sequence using the given closure.
+ ///
+ /// This can be seen as applying the reduce function to each element and
+ /// providing the initial value followed by these results as a sequence.
+ ///
+ /// ```
+ /// let runningTotal = [1, 2, 3, 4].lazy.reductions(+)
+ /// print(Array(runningTotal))
+ ///
+ /// // prints [1, 3, 6, 10]
+ /// ```
+ ///
+ /// - Parameters:
+ /// - transform: A closure that combines the previously reduced result and
+ /// the next element in the receiving sequence.
+ /// - Returns: An array of accumulated elements.
+ ///
+ /// - Complexity: O(1)
@inlinable
public func reductions(
_ transform: @escaping (Element, Element) -> Element
@@ -251,6 +332,25 @@ extension LazySequenceProtocol {
extension Sequence {
+ /// Returns an array containing the accumulated results of combining the
+ /// elements of the sequence using the given closure.
+ ///
+ /// This can be seen as applying the reduce function to each element and
+ /// providing the initial value followed by these results as a sequence.
+ ///
+ /// ```
+ /// let runningTotal = [1, 2, 3, 4].reductions(+)
+ /// print(runningTotal)
+ ///
+ /// // prints [1, 3, 6, 10]
+ /// ```
+ ///
+ /// - Parameters:
+ /// - transform: A closure that combines the previously reduced result and
+ /// the next element in the receiving sequence.
+ /// - Returns: An array of accumulated elements.
+ ///
+ /// - Complexity: O(n)
@inlinable
public func reductions(
_ transform: (Element, Element) throws -> Element
From 04f0a23270f1e0a69b25d6aeebb184ed09950779 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Fri, 19 Mar 2021 12:32:09 +0000
Subject: [PATCH 40/45] Test the value after the function is complete
---
.../ReductionsTests.swift | 38 ++++++++++++-------
1 file changed, 24 insertions(+), 14 deletions(-)
diff --git a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
index 0f57b56b..2679da0b 100644
--- a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
+++ b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
@@ -16,6 +16,8 @@ final class ReductionsTests: XCTestCase {
struct TestError: Error {}
+ func add(lhs: inout Int, rhs: Int) { lhs += rhs }
+
// MARK: - Exclusive Reductions
func testExclusiveLazy() {
@@ -27,13 +29,17 @@ final class ReductionsTests: XCTestCase {
XCTAssertEqualCollections([1].lazy.reductions(0, +), [0, 1])
XCTAssertEqualCollections(EmptyCollection().lazy.reductions(0, +), [0])
- var value0 = 0
- var value1 = 0
- var value2 = 0
- func add(lhs: inout Int, rhs: Int) { lhs += rhs }
- XCTAssertEqual([1, 2, 3, 4].lazy.reductions(into: &value0, add), [0, 1, 3, 6, 10])
- XCTAssertEqual([1].lazy.reductions(into: &value1, add), [0, 1])
- XCTAssertEqual(EmptyCollection().lazy.reductions(into: &value2, add), [0])
+ var value = 0
+ XCTAssertEqual([1, 2, 3, 4].lazy.reductions(into: &value, add), [0, 1, 3, 6, 10])
+ XCTAssertEqual(value, 10)
+
+ value = 0
+ XCTAssertEqual([1].lazy.reductions(into: &value, add), [0, 1])
+ XCTAssertEqual(value, 1)
+
+ value = 0
+ XCTAssertEqual(EmptyCollection().lazy.reductions(into: &value, add), [0])
+ XCTAssertEqual(value, 0)
XCTAssertLazySequence((1...).prefix(1).lazy.reductions(0, +))
XCTAssertLazySequence([1].lazy.reductions(0, +))
@@ -45,13 +51,17 @@ final class ReductionsTests: XCTestCase {
XCTAssertEqual([1].reductions(0, +), [0, 1])
XCTAssertEqual(EmptyCollection().reductions(0, +), [0])
- var value0 = 0
- var value1 = 0
- var value2 = 0
- func add(lhs: inout Int, rhs: Int) { lhs += rhs }
- XCTAssertEqual([1, 2, 3, 4].reductions(into: &value0, add), [0, 1, 3, 6, 10])
- XCTAssertEqual([1].reductions(into: &value1, add), [0, 1])
- XCTAssertEqual(EmptyCollection().reductions(into: &value2, add), [0])
+ var value = 0
+ XCTAssertEqual([1, 2, 3, 4].reductions(into: &value, add), [0, 1, 3, 6, 10])
+ XCTAssertEqual(value, 10)
+
+ value = 0
+ XCTAssertEqual([1].reductions(into: &value, add), [0, 1])
+ XCTAssertEqual(value, 1)
+
+ value = 0
+ XCTAssertEqual(EmptyCollection().reductions(into: &value, add), [0])
+ XCTAssertEqual(value, 0)
XCTAssertNoThrow(try [].reductions(0) { _, _ in throw TestError() })
XCTAssertThrowsError(try [1].reductions(0) { _, _ in throw TestError() })
From 724e22318ffd17a76a4b9bda6968e7b8fc6adcff Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Fri, 19 Mar 2021 12:32:49 +0000
Subject: [PATCH 41/45] Just use the += operator
---
Tests/SwiftAlgorithmsTests/ReductionsTests.swift | 14 ++++++--------
1 file changed, 6 insertions(+), 8 deletions(-)
diff --git a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
index 2679da0b..60a53709 100644
--- a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
+++ b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift
@@ -16,8 +16,6 @@ final class ReductionsTests: XCTestCase {
struct TestError: Error {}
- func add(lhs: inout Int, rhs: Int) { lhs += rhs }
-
// MARK: - Exclusive Reductions
func testExclusiveLazy() {
@@ -30,15 +28,15 @@ final class ReductionsTests: XCTestCase {
XCTAssertEqualCollections(EmptyCollection().lazy.reductions(0, +), [0])
var value = 0
- XCTAssertEqual([1, 2, 3, 4].lazy.reductions(into: &value, add), [0, 1, 3, 6, 10])
+ XCTAssertEqual([1, 2, 3, 4].lazy.reductions(into: &value, +=), [0, 1, 3, 6, 10])
XCTAssertEqual(value, 10)
value = 0
- XCTAssertEqual([1].lazy.reductions(into: &value, add), [0, 1])
+ XCTAssertEqual([1].lazy.reductions(into: &value, +=), [0, 1])
XCTAssertEqual(value, 1)
value = 0
- XCTAssertEqual(EmptyCollection().lazy.reductions(into: &value, add), [0])
+ XCTAssertEqual(EmptyCollection().lazy.reductions(into: &value, +=), [0])
XCTAssertEqual(value, 0)
XCTAssertLazySequence((1...).prefix(1).lazy.reductions(0, +))
@@ -52,15 +50,15 @@ final class ReductionsTests: XCTestCase {
XCTAssertEqual(EmptyCollection().reductions(0, +), [0])
var value = 0
- XCTAssertEqual([1, 2, 3, 4].reductions(into: &value, add), [0, 1, 3, 6, 10])
+ XCTAssertEqual([1, 2, 3, 4].reductions(into: &value, +=), [0, 1, 3, 6, 10])
XCTAssertEqual(value, 10)
value = 0
- XCTAssertEqual([1].reductions(into: &value, add), [0, 1])
+ XCTAssertEqual([1].reductions(into: &value, +=), [0, 1])
XCTAssertEqual(value, 1)
value = 0
- XCTAssertEqual(EmptyCollection().reductions(into: &value, add), [0])
+ XCTAssertEqual(EmptyCollection().reductions(into: &value, +=), [0])
XCTAssertEqual(value, 0)
XCTAssertNoThrow(try [].reductions(0) { _, _ in throw TestError() })
From 042013f1612175154e46e66192350ef46d3a046a Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Fri, 19 Mar 2021 12:34:28 +0000
Subject: [PATCH 42/45] Add an example for reductions(into:_:)
---
Guides/Reductions.md | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)
diff --git a/Guides/Reductions.md b/Guides/Reductions.md
index 87e6bf61..93b3cb68 100644
--- a/Guides/Reductions.md
+++ b/Guides/Reductions.md
@@ -8,17 +8,20 @@ Produces a sequence of values.
This has the behaviour of reduce, but also returns all intermediate results.
```swift
-let runningTotalExclusive = (1...5).reductions(0, +)
-print(runningTotalExclusive)
+let exclusiveRunningTotal = (1...5).reductions(0, +)
+print(exclusiveRunningTotal)
// prints [0, 1, 3, 6, 10, 15]
-let runningTotalInclusive = (1...5).reductions(+)
-print(runningTotalInclusive)
-// prints [1, 3, 6, 10, 15]
+var value = 0
+let intoRunningTotal = (1...5).reductions(into: &value, +=)
+print(intoRunningTotal)
+// prints [0, 1, 3, 6, 10, 15]
+print(value)
+// prints 15
-let runningMinimum = [3, 4, 2, 3, 1].reductions(min)
-print(runningMinimum)
-// prints [3, 3, 2, 2, 1]
+let inclusiveRunningTotal = (1...5).reductions(+)
+print(inclusiveRunningTotal)
+// prints [1, 3, 6, 10, 15]
```
## Detailed Design
From 5f55e8bb732edcb1cde005744a10d825768d04da Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Sun, 21 Mar 2021 12:35:25 +0000
Subject: [PATCH 43/45] Improve the documentation of the return value
---
Sources/Algorithms/Reductions.swift | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/Sources/Algorithms/Reductions.swift b/Sources/Algorithms/Reductions.swift
index 4a395320..e2d73b91 100644
--- a/Sources/Algorithms/Reductions.swift
+++ b/Sources/Algorithms/Reductions.swift
@@ -30,7 +30,8 @@ extension LazySequenceProtocol {
/// - initial: The value to use as the initial value.
/// - transform: A closure that combines the previously reduced result and
/// the next element in the receiving sequence.
- /// - Returns: A sequence of transformed elements.
+ /// - Returns: A sequence of the initial value followed by the reduced
+ /// elements.
///
/// - Complexity: O(1)
@inlinable
@@ -62,7 +63,8 @@ extension LazySequenceProtocol {
/// - initial: The value to use as the initial value.
/// - transform: A closure that combines the previously reduced result and
/// the next element in the receiving sequence.
- /// - Returns: A sequence of transformed elements.
+ /// - Returns: A sequence of the initial value followed by the reduced
+ /// elements.
///
/// - Complexity: O(1)
@inlinable
@@ -93,7 +95,7 @@ extension Sequence {
/// - initial: The value to use as the initial value.
/// - transform: A closure that combines the previously reduced result and
/// the next element in the receiving sequence.
- /// - Returns: An array of transformed elements.
+ /// - Returns: An array of the initial value followed by the reduced elements.
///
/// - Complexity: O(n)
@inlinable
@@ -125,7 +127,7 @@ extension Sequence {
/// - initial: The value to use as the initial value.
/// - transform: A closure that combines the previously reduced result and
/// the next element in the receiving sequence.
- /// - Returns: An array of transformed elements.
+ /// - Returns: An array of the initial value followed by the reduced elements.
///
/// - Complexity: O(n)
@inlinable
@@ -319,7 +321,7 @@ extension LazySequenceProtocol {
/// - Parameters:
/// - transform: A closure that combines the previously reduced result and
/// the next element in the receiving sequence.
- /// - Returns: An array of accumulated elements.
+ /// - Returns: A sequence of the reduced elements.
///
/// - Complexity: O(1)
@inlinable
@@ -348,7 +350,7 @@ extension Sequence {
/// - Parameters:
/// - transform: A closure that combines the previously reduced result and
/// the next element in the receiving sequence.
- /// - Returns: An array of accumulated elements.
+ /// - Returns: An array of the reduced elements.
///
/// - Complexity: O(n)
@inlinable
From d4daf2c5a8c3b867a9b1d7161beabebd0c044102 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Mon, 22 Mar 2021 13:55:50 +0000
Subject: [PATCH 44/45] Update complexity note
---
Sources/Algorithms/Reductions.swift | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Sources/Algorithms/Reductions.swift b/Sources/Algorithms/Reductions.swift
index e2d73b91..31e561a1 100644
--- a/Sources/Algorithms/Reductions.swift
+++ b/Sources/Algorithms/Reductions.swift
@@ -97,7 +97,7 @@ extension Sequence {
/// the next element in the receiving sequence.
/// - Returns: An array of the initial value followed by the reduced elements.
///
- /// - Complexity: O(n)
+ /// - Complexity: O(_n_), where _n_ is the length of the sequence.
@inlinable
public func reductions(
_ initial: Result,
@@ -129,7 +129,7 @@ extension Sequence {
/// the next element in the receiving sequence.
/// - Returns: An array of the initial value followed by the reduced elements.
///
- /// - Complexity: O(n)
+ /// - Complexity: O(_n_), where _n_ is the length of the sequence.
@inlinable
public func reductions(
into initial: inout Result,
@@ -352,7 +352,7 @@ extension Sequence {
/// the next element in the receiving sequence.
/// - Returns: An array of the reduced elements.
///
- /// - Complexity: O(n)
+ /// - Complexity: O(_n_), where _n_ is the length of the sequence.
@inlinable
public func reductions(
_ transform: (Element, Element) throws -> Element
From 6660d642cdc0093b19ca20cdb2845faa79b83767 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Mon, 22 Mar 2021 14:10:23 +0000
Subject: [PATCH 45/45] Use the reduce documentation for inspiration for the
discussion of reductions
---
Sources/Algorithms/Reductions.swift | 44 +++++++++++++++++++++++++++++
1 file changed, 44 insertions(+)
diff --git a/Sources/Algorithms/Reductions.swift b/Sources/Algorithms/Reductions.swift
index 31e561a1..627cea48 100644
--- a/Sources/Algorithms/Reductions.swift
+++ b/Sources/Algorithms/Reductions.swift
@@ -91,6 +91,20 @@ extension Sequence {
/// // prints [0, 1, 3, 6, 10]
/// ```
///
+ /// When `reductions(_:_:)` is called, the following steps occur:
+ ///
+ /// 1. The `initial` result is added to an array of results.
+ /// 2. The `transform` closure is called with the `initial` result and the
+ /// first element of the sequence, appending the result to the array.
+ /// 3. The closure is called again repeatedly with the updated accumulating
+ /// result and each element of the sequence, adding each result to the
+ /// array.
+ /// 4. When the sequence is exhausted, the results array is returned to the
+ /// caller.
+ ///
+ /// If the sequence has no elements, `transform` is never executed and
+ /// an array containing only the `initial` result is returned.
+ ///
/// - Parameters:
/// - initial: The value to use as the initial value.
/// - transform: A closure that combines the previously reduced result and
@@ -123,6 +137,20 @@ extension Sequence {
/// // prints [0, 1, 3, 6, 10]
/// ```
///
+ /// When `reductions(into:_:_)` is called, the following steps occur:
+ ///
+ /// 1. The `initial` result is added to an array of results.
+ /// 2. The `transform` closure is called with the `initial` result and the
+ /// first element of the sequence, appending the result to the array.
+ /// 3. The closure is called again repeatedly with the updated accumulating
+ /// result and each element of the sequence, adding each result to the
+ /// array.
+ /// 4. When the sequence is exhausted, the results array is returned to the
+ /// caller.
+ ///
+ /// If the sequence has no elements, `transform` is never executed and
+ /// an array containing only the `initial` result is returned.
+ ///
/// - Parameters:
/// - initial: The value to use as the initial value.
/// - transform: A closure that combines the previously reduced result and
@@ -347,6 +375,22 @@ extension Sequence {
/// // prints [1, 3, 6, 10]
/// ```
///
+ /// When `reductions(_:)` is called, the following steps occur:
+ ///
+ /// 1. The `transform` closure is called with the first and second elements
+ /// of the sequence, appending the result to an array of results.
+ /// 2. The closure is called again repeatedly with the updated accumulating
+ /// result and the next element of the sequence, adding each result to the
+ /// array.
+ /// 3. When the sequence is exhausted, the results array is returned to the
+ /// caller.
+ ///
+ /// If the sequence has no elements, `transform` is never executed and
+ /// an empty array is returned.
+ ///
+ /// If the sequence has one element, `transform` is never executed and
+ /// an array containing only that first element is returned.
+ ///
/// - Parameters:
/// - transform: A closure that combines the previously reduced result and
/// the next element in the receiving sequence.