[go: up one dir, main page]

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

Consider adding scan #25

Closed
fwgreen opened this issue Oct 14, 2020 · 6 comments
Closed

Consider adding scan #25

fwgreen opened this issue Oct 14, 2020 · 6 comments

Comments

@fwgreen
Copy link
fwgreen commented Oct 14, 2020

I remember scan(_:combine:) not making the cut on the grounds of offering low utility. Is it possible for it to find a place here?

@kylemacomber
Copy link
Contributor

@fwgreen could you elaborate on the functionality scan(_:combine:) to make sure we're all on the same page?

@fwgreen
Copy link
Author
fwgreen commented Oct 14, 2020

Its a reduce operation that keeps the previous values:

let nums = [0,1,2,3,4,5].scan(+)
print(nums) //[0, 1, 3, 6, 10, 15]

let chars = ["0","1","2","3","4","5"].scan(+)
print(chars) //["0", "01", "012", "0123", "01234", "012345"]

let range = (1..<5).scan(0, +)
print(range) //[0, 1, 3, 6, 10]

I use an extension that I've cobbled together from Slashdot and other places, but it would be good if there was a correct (and performant) version available.

extension Sequence {
    func scan(_ combine: (Element, Element) throws -> Element) rethrows -> [Element] {
        var iterator = makeIterator()
        guard let initial = iterator.next() else {
            return []
        }
        var firstIteration = true
        var accumulator = initial
        return try map { element in
            if firstIteration == true {
                firstIteration = false
                return initial
            } else {
                accumulator = try combine(accumulator, element)
                return accumulator
            }
        }
    }
    
    func scan(_ initial: Element, _ combine: (Element, Element) throws -> Element) rethrows -> [Element] {
        var accumulator = initial
        let sequence = [initial] + self
        return try sequence.map { element in
            accumulator = try combine(accumulator, element)
            return accumulator
        }
    }
}

@pyrtsa
Copy link
pyrtsa commented Oct 15, 2020

+1 on the idea.

I'd rather see the latter function with the following signature, allowing for a different result type and aligning with Sequence.reduce(_:_:):

extension Sequence {
    func scan<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> [Result]
}

Also a lazy counterpart for both functions seems like a useful thing to include.

@pyrtsa
Copy link
pyrtsa commented Oct 15, 2020

I think scan can be taken as a name of art (also used by Combine framework), but this might be a good opportunity to consider the term reductions which is more noun-like, pairs up nicely with reduce, is also used elsewhere, and has been proposed and supported in the past for Swift.

The term scan is established e.g. in Haskell, but in Haskell what we call reduce is known by a different name too: it's a fold.

@kylemacomber
Copy link
Contributor

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.

@natecook1000
Copy link
Member

Thanks, @fwgreen — this looks like a good addition, especially since it's easy to get the implementation wrong. 👏👏 Let's go with reductions(_:_:) for the name.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants