Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add reductions(_:_:) #46

Merged
merged 45 commits into from
Mar 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
4bba172
Add scan function
danielctull Oct 9, 2020
548b1e7
Add initial conditional conformances to Collection and BidirectionalC…
danielctull Nov 24, 2020
e9edde0
Rename scan to reductions
danielctull Nov 24, 2020
b41af13
Add information to the readme about the reductions name
danielctull Nov 24, 2020
5116e25
Add eager API
danielctull Nov 25, 2020
e9d35f6
Remove Reductions' conformance to BidirectionalCollection
danielctull Nov 26, 2020
caf0b71
Implement Reductions subscript such that it occurs in constant time
danielctull Nov 26, 2020
531b6f9
Add a variant which includes the given initial result
danielctull Nov 26, 2020
c0c237a
Add a variant which takes no initial result value
danielctull Nov 26, 2020
7bb96ea
Add excluding label to show the initial result is not included
danielctull Nov 26, 2020
5b4d984
Test lazy implementations
danielctull Nov 26, 2020
e0ee01d
Remove reductions(including:_:)
danielctull Nov 26, 2020
1ce31ed
Add conformance to LazySequenceProtocol and LazyCollectionProtocol wh…
danielctull Nov 26, 2020
fe912e5
Fix implementation of lazy reductions
danielctull Nov 27, 2020
c3e918d
Add test cases for sequences with one element
danielctull Nov 27, 2020
c693579
Tidy up tests
danielctull Nov 27, 2020
55e87eb
Improve ergonomics of no initial value eager reductions call and prov…
danielctull Nov 27, 2020
4654ae7
Add lazy InclusiveReductions sequence
danielctull Nov 27, 2020
154fa40
Rename Reductions to ExclusiveReductions
danielctull Nov 27, 2020
959e963
Add Collection implementation for InclusiveReductions
danielctull Nov 27, 2020
0c60ad7
Update guide
danielctull Nov 27, 2020
2a41ecb
Add links for C++ implementations
danielctull Nov 27, 2020
aac96c3
Add conformance to Collection for ExclusiveReductions
danielctull Nov 28, 2020
a61878f
Improve ergonomics of ExclusiveReductions' index representation
danielctull Nov 28, 2020
058edc8
Improve ergonomics of InclusiveReductions' index representation by sh…
danielctull Nov 29, 2020
a221997
Tidy up internal function
danielctull Nov 29, 2020
ab40e33
Add exclusive eager version of reductions(into:_:)
danielctull Nov 29, 2020
6b01737
Use new lazy assertion functions
danielctull Dec 3, 2020
714f46b
Correct the complexity claims for reductions
danielctull Feb 25, 2021
5182363
Separate the index types
danielctull Feb 25, 2021
84b6ddc
Update guide to reflect that arrays are returned directly
danielctull Mar 8, 2021
fb004f7
Add scan as deprecated methods
danielctull Mar 8, 2021
7464876
Add lazy overload of reductions(into:_:)
danielctull Mar 18, 2021
174efb8
Add the reductions(into:_:) functions
danielctull Mar 18, 2021
1e47f86
More succinctly introduce reductions
danielctull Mar 18, 2021
a73c681
Add a note about the deprecated scan functions
danielctull Mar 18, 2021
f6b42ad
Update documentation
danielctull Mar 18, 2021
f7b90b5
Add @inlinable and @usableFromInline
danielctull Mar 18, 2021
e9abcdb
Copy documentation to all variants of reductions
danielctull Mar 19, 2021
04f0a23
Test the value after the function is complete
danielctull Mar 19, 2021
724e223
Just use the += operator
danielctull Mar 19, 2021
042013f
Add an example for reductions(into:_:)
danielctull Mar 19, 2021
5f55e8b
Improve the documentation of the return value
danielctull Mar 21, 2021
d4daf2c
Update complexity note
danielctull Mar 22, 2021
6660d64
Use the reduce documentation for inspiration for the discussion of re…
danielctull Mar 22, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions Guides/Reductions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# Reductions

[[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.

This has the behaviour of reduce, but also returns all intermediate results.

```swift
let exclusiveRunningTotal = (1...5).reductions(0, +)
print(exclusiveRunningTotal)
// prints [0, 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 inclusiveRunningTotal = (1...5).reductions(+)
print(inclusiveRunningTotal)
// prints [1, 3, 6, 10, 15]
```

## Detailed Design

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 {

public func reductions<Result>(
_ initial: Result,
_ transform: @escaping (Result, Element) -> Result
) -> ExclusiveReductions<Result, Self>

public func reductions<Result>(
into initial: inout Result,
_ transform: @escaping (inout Result, Element) -> Void
) -> ExclusiveReductions<Result, Self>

public func reductions(
_ transform: @escaping (Element, Element) -> Element
) -> InclusiveReductions<Self>
}
```

```swift
extension Sequence {

public func reductions<Result>(
_ initial: Result,
_ transform: (Result, Element) throws -> Result
) rethrows -> [Result]

public func reductions<Result>(
into initial: inout Result,
_ transform: (inout Result, Element) throws -> Void
) rethrows -> [Result]

public func reductions(
_ transform: (Element, Element) throws -> Element
) rethrows -> [Element]
}
```

### Complexity

Calling the lazy methods, those defined on `LazySequenceProtocol`, is O(_1_).
Calling the eager methods, those returning an array, is O(_n_).

### 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.

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
[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`.

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
[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 `<algorithm>` library includes both
[`exclusive_scan`][C++ Exclusive] and [`inclusive_scan`][C++ Inclusive]
functions.

**[Clojure][Clojure]:** Clojure 1.2 added a `reductions` function.

**[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]:** 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
Loading