Skip to content
This repository has been archived by the owner on Mar 29, 2018. It is now read-only.

Sequence operations #14

Open
ColinEberhardt opened this issue Jun 24, 2014 · 13 comments · Fixed by #15
Open

Sequence operations #14

ColinEberhardt opened this issue Jun 24, 2014 · 13 comments · Fixed by #15

Comments

@ColinEberhardt
Copy link
Contributor

Hi - are you interested in a contribution which adds Sequence operations to this library?

The Swift Sequence is lazy evaluated, and as a result the various operations (take, skip, contains, ...) can be written in such as way that they do not have to evaluate the entire sequence to return their value.

I've implemented a few operations here:

https://github.com/ColinEberhardt/ExSwift/blob/sequence-operations/ExSwift/Sequence.swift

With tests here:

https://github.com/ColinEberhardt/ExSwift/blob/sequence-operations/ExSwiftTests/ExSwiftSequenceTests.swift

Unfortunately because you cannot extend protocols, the only way I can find to add sequence operations is to extend SequenceOf, which as far as a I can tell adapts a Sequence returning a concrete type rather than a protocol.

Anyhow, just wanted to get your thoughts before I get too stuck in to the implementation. If I add a decent suite of Sequence methods, would you want to include them in ExSwift?

@pNre
Copy link
Owner

pNre commented Jun 24, 2014

Hi,
yes, that's something interesting, too bad the only way to do so is extending SequenceOf.
I would definitely merge it into the library. Let's just be sure this is the only way of doing so.

Also, I didn't think that both Array and Dictionary extend Collection (a Sequence).
Might be the case to review these classes though we can't extend protocols (so -> duplicate code).

@ColinEberhardt
Copy link
Contributor Author

Cool, glad to hear you are interested. I'll keep exploring different ideas to see if I can avoid the need to use SequenceOf, although I am pretty sure that this is a constraint that cannot be avoided,

Also, I didn't think that both Array and Dictionary extend Collection (a Sequence).

Actually they do (otherwise they would not work with for-in loops). The protocol inheritance is a bit convoluted, but you can see it for Array here:

Array<T> : MutableCollection, Sliceable 
protocol MutableCollection : Collection
protocol Collection : Sequence

I'll keep playing around with these ideas and get back to you.

@pNre
Copy link
Owner

pNre commented Jun 25, 2014

I'm pretty sure too there isn't a handier way (at least for the moment, Swift is still missing important pieces).
Anyway, of course I know that Array and Dictionary do conform to Sequence, what I meant is that I didn't consider that while writing the extensions.

@ColinEberhardt
Copy link
Contributor Author

Anyway, of course I know that Array and Dictionary do conform to Sequence, what I meant is that I didn't consider that while writing the extensions.

Sorry, my mistake :-)

I guess you could implement the Array and Dictionary methods via Sequence, for example, an array skip could be implemented as SequenceOf(array).skip(n), however, an array-specific implementation of the (and most other operations) would be much more efficient.

Anyhow, I'll get started!

@ColinEberhardt
Copy link
Contributor Author

Just a quick update, I have implemented the following methods:

first () -> T?
takeWhile (condition:(Element?) -> Bool) -> SequenceOf<Element>
take (n:Int) -> SequenceOf<Element>
contains<T:Equatable> (item: T) -> Bool
skip (n:Int) -> SequenceOf<T>
skipWhile(condition:(T) -> Bool) -> SequenceOf<T>

The 'take' methods were a bit more challenging!

Anyhow, my feeling is that the ExSwift should only expose operations on Sequence when it is possible to do this in an optimised (lazy) fashion. As a result, methods like 'last' should not be added to Sequence. This would force people to convert their sequence to an array, e.g. Array(sequence).last(), making it obvious that the operation requires an iteration over the entire sequence.

As a result, I propose adding the following methods:

indexOf <U: Equatable> (item: U) -> Int?
get (index: Int) -> Element?
get (range: Range<Int>) -> SequenceOf<T>
each (call: (Element) -> ())
each (call: (Int, Element) -> ())
any (call: (Element) -> Bool) -> Bool
reject (exclude: (Element -> Bool)) -> SequenceOf<Element>
unique <T: Equatable> ()
flatten <OutType> () -> OutType[]

I'll also add some documentation about when you should use the sequence operations, and why some operations are omitted.

@pNre
Copy link
Owner

pNre commented Jun 26, 2014

Anyhow, my feeling is that the ExSwift should only expose operations on Sequence when it is possible to do this in an optimised (lazy) fashion. As a result, methods like 'last' should not be added to Sequence. This would force people to convert their sequence to an array, e.g. Array(sequence).last(), making it obvious that the operation requires an iteration over the entire sequence.

I agree, but doesn't the same apply to reject, unique and flatten?

@ColinEberhardt
Copy link
Contributor Author

but doesn't the same apply to reject, unique and flatten?

I'm pretty sure they can be implemented lazily. I'll let you know how I progress!

@pNre pNre closed this as completed in #15 Jun 27, 2014
@pNre pNre reopened this Jun 27, 2014
@nstepan
Copy link

nstepan commented Jul 10, 2014

Hello,

I think there is no need for FilterSequence struct, we can reuse global 'filter' function.

extension SequenceOf
{

func filter(include: (T) -> (Bool)) -> SequenceOf<T>
{
    return SequenceOf<T>(Swift.filter(self, include))
}

func map<U>(transform: (T) -> (U)) -> SequenceOf<U>
{
    return SequenceOf<U>(Swift.map(self, transform))
}

func reduce<U>(initial: U, combine: (U, T) -> (U)) -> U
{
    return Swift.reduce(self, initial, combine)
}

}

@pNre
Copy link
Owner

pNre commented Jul 11, 2014

Yes, that's correct, the documentation says clearly that Swift.filter returns a lazy Sequence.

@nstepan
Copy link

nstepan commented Jul 23, 2014

In Xcode 6 Beta 4, Swift.filter no longer returns lazy Sequence, so it can't be used anymore.

@ColinEberhardt
Copy link
Contributor Author

@nstepan oh, that is a bit of an odd and unexpected change!

@pNre
Copy link
Owner

pNre commented Jul 24, 2014

I just pushed a temporary fix.
There's now a struct LazySequence in the standard library, generated by the Swift.lazy method.
Since it's a struct we can extend it instead of SequenceOf.
@ColinEberhardt what do you think?

@oisdk
Copy link

oisdk commented May 26, 2015

Hi! I saw this library mentioned on stack overflow, and I realised I had had a few things hanging around that were similar to what's in the SequenceOf extension here, except it's an extension of LazySequence. I looked through it and I've managed to implement (most of) the functions in this extension that I hadn't already. Would you be interested? Here's a gist of it. A few of the functions, like take, takeWhile, etc., I had done differently, and there are some extras, like cycle, scan, etc.

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

Successfully merging a pull request may close this issue.

4 participants