ExSwift icon indicating copy to clipboard operation
ExSwift copied to clipboard

Sequence operations

Open ColinEberhardt opened this issue 11 years ago • 13 comments

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?

ColinEberhardt avatar Jun 24 '14 18:06 ColinEberhardt

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

pNre avatar Jun 24 '14 19:06 pNre

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.

ColinEberhardt avatar Jun 25 '14 13:06 ColinEberhardt

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.

pNre avatar Jun 25 '14 14:06 pNre

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 avatar Jun 25 '14 21:06 ColinEberhardt

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.

ColinEberhardt avatar Jun 26 '14 07:06 ColinEberhardt

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?

pNre avatar Jun 26 '14 11:06 pNre

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!

ColinEberhardt avatar Jun 26 '14 14:06 ColinEberhardt

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)
}

}

nstepan avatar Jul 10 '14 18:07 nstepan

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

pNre avatar Jul 11 '14 19:07 pNre

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

nstepan avatar Jul 23 '14 14:07 nstepan

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

ColinEberhardt avatar Jul 24 '14 11:07 ColinEberhardt

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?

pNre avatar Jul 24 '14 14:07 pNre

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.

oisdk avatar May 26 '15 20:05 oisdk