zip3, zip4, ...


(Jacob Bandes-Storch) #1

zip2 (aka "zip") is present in Swift. zip3 is often useful, but not
built-in.

It can be achieved by using multiple copies of zip2:

  for (one, (two, three)) in zip(list1, zip(list2, list3)) ...

It seems like either of these could make sense:

- Put some reasonable number of implementations, like zip2...zip10, in the
standard library, using gyb.

- Have the compiler generate them on the fly as requested by the user.

Or, some alternate approaches:

- Don't do this right now, but count it as motivation for a macro system.

- Consider making Swift's pattern-matching system extensible, which might
allow custom array-based patterns, like "for [one, two, three] in
zip(list1, list2, list3)". (I've been thinking of writing a proposal for
this anyway.)

Does anyone else care about zip3-and-higher? How do these options sound?

Jacob Bandes-Storch


(Erica Sadun) #2

It's pretty easy to build your own Zips. Not sure the language really needs this. For example, I recently built a zip that produces (T?, T?) which fills one of the two with nil until both lists are consumed:

func longZip<S0: SequenceType, S1: SequenceType>(seq0: S0, _ seq1: S1) ->
    AnyGenerator<(S0.Generator.Element?, S1.Generator.Element?)> {
        var generators = (seq0.generate(), seq1.generate())
        return anyGenerator {
            let items = (generators.0.next(), generators.1.next())
            if case (.None, .None) = items {return nil}
            return items
        }
}

I'm rather fond of this variant although I don't know if it's generally useful enough to be worth even considering for the language

-- E

···

On Dec 6, 2015, at 4:01 PM, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org> wrote:

zip2 (aka "zip") is present in Swift. zip3 is often useful, but not built-in.

It can be achieved by using multiple copies of zip2:

  for (one, (two, three)) in zip(list1, zip(list2, list3)) ...

It seems like either of these could make sense:

- Put some reasonable number of implementations, like zip2...zip10, in the standard library, using gyb.

- Have the compiler generate them on the fly as requested by the user.

Or, some alternate approaches:

- Don't do this right now, but count it as motivation for a macro system.

- Consider making Swift's pattern-matching system extensible, which might allow custom array-based patterns, like "for [one, two, three] in zip(list1, list2, list3)". (I've been thinking of writing a proposal for this anyway.)

Does anyone else care about zip3-and-higher? How do these options sound?

Jacob Bandes-Storch
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Dmitri Gribenko) #3

Just wanted to point out that AnyGenerator has an inherent cost from the
type erasure. The implementation in the standard library uses generics and
is fully optimizable.

Dmitri

···

On Sun, Dec 6, 2015 at 3:34 PM, Erica Sadun via swift-evolution < swift-evolution@swift.org> wrote:

It's pretty easy to build your own Zips. Not sure the language really
needs this. For example, I recently built a zip that produces (T?, T?)
which fills one of the two with nil until both lists are consumed:

func longZip<S0: SequenceType, S1: SequenceType>(seq0: S0, _ seq1: S1) ->
    AnyGenerator<(S0.Generator.Element?, S1.Generator.Element?)> {

--
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr@gmail.com>*/


(Erica Sadun) #4

Is there an implementation in the stdlib for (T?, T?) like this?

···

On Dec 6, 2015, at 4:37 PM, Dmitri Gribenko <gribozavr@gmail.com> wrote:

On Sun, Dec 6, 2015 at 3:34 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
It's pretty easy to build your own Zips. Not sure the language really needs this. For example, I recently built a zip that produces (T?, T?) which fills one of the two with nil until both lists are consumed:

func longZip<S0: SequenceType, S1: SequenceType>(seq0: S0, _ seq1: S1) ->
    AnyGenerator<(S0.Generator.Element?, S1.Generator.Element?)> {

Just wanted to point out that AnyGenerator has an inherent cost from the type erasure. The implementation in the standard library uses generics and is fully optimizable.

Dmitri

--
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr@gmail.com <mailto:gribozavr@gmail.com>>*/


(Dmitri Gribenko) #5

No.

Dmitri

···

On Sun, Dec 6, 2015 at 3:44 PM, Erica Sadun <erica@ericasadun.com> wrote:

Is there an implementation in the stdlib for (T?, T?) like this?

--
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr@gmail.com>*/


(Donnacha Oisín Kidney) #6

An implementation of this is actually pretty complicated, since you aren’t supposed to call a generator once it’s returned nil.

public struct NilPaddedZipGenerator<G0: GeneratorType, G1: GeneratorType> : GeneratorType {
  
  private var (g0, g1): (G0?, G1?)
  
  public mutating func next() -> (G0.Element?, G1.Element?)? {
    let (e0,e1) = (g0?.next(),g1?.next())
    switch (e0,e1) {
    case (nil,nil): return nil
    case ( _,nil): g1 = nil
    case (nil, _): g0 = nil
    default: break
    }
    return (e0,e1)
  }
}

public struct NilPaddedZip<S0: SequenceType, S1: SequenceType> : LazySequenceType {
  
  private let (s0, s1): (S0, S1)
  public func generate() -> NilPaddedZipGenerator<S0.Generator, S1.Generator> {
    return NilPaddedZipGenerator(g0: s0.generate(), g1: s1.generate())
  }
}

@warn_unused_result
public func zipWithPadding<S0: SequenceType, S1: SequenceType>(s0: S0, _ s1: S1)
  -> NilPaddedZip<S0, S1> {
    return NilPaddedZip(s0: s0, s1: s1)
}

···

On 6 Dec 2015, at 23:44, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

Is there an implementation in the stdlib for (T?, T?) like this?

On Dec 6, 2015, at 4:37 PM, Dmitri Gribenko <gribozavr@gmail.com <mailto:gribozavr@gmail.com>> wrote:

On Sun, Dec 6, 2015 at 3:34 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
It's pretty easy to build your own Zips. Not sure the language really needs this. For example, I recently built a zip that produces (T?, T?) which fills one of the two with nil until both lists are consumed:

func longZip<S0: SequenceType, S1: SequenceType>(seq0: S0, _ seq1: S1) ->
    AnyGenerator<(S0.Generator.Element?, S1.Generator.Element?)> {

Just wanted to point out that AnyGenerator has an inherent cost from the type erasure. The implementation in the standard library uses generics and is fully optimizable.

Dmitri

--
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr@gmail.com <mailto:gribozavr@gmail.com>>*/

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Vinicius Vendramini) #7

I wouldn't really know how to do this, but a better approach than implementing zip2 through zip10 might be making it a variadic function, no?

···

On Dec 6, 2015, at 6:49 PM, Donnacha Oisín Kidney via swift-evolution <swift-evolution@swift.org> wrote:

An implementation of this is actually pretty complicated, since you aren’t supposed to call a generator once it’s returned nil.

public struct NilPaddedZipGenerator<G0: GeneratorType, G1: GeneratorType> : GeneratorType {
  
  private var (g0, g1): (G0?, G1?)
  
  public mutating func next() -> (G0.Element?, G1.Element?)? {
    let (e0,e1) = (g0?.next(),g1?.next())
    switch (e0,e1) {
    case (nil,nil): return nil
    case ( _,nil): g1 = nil
    case (nil, _): g0 = nil
    default: break
    }
    return (e0,e1)
  }
}

public struct NilPaddedZip<S0: SequenceType, S1: SequenceType> : LazySequenceType {
  
  private let (s0, s1): (S0, S1)
  public func generate() -> NilPaddedZipGenerator<S0.Generator, S1.Generator> {
    return NilPaddedZipGenerator(g0: s0.generate(), g1: s1.generate())
  }
}

@warn_unused_result
public func zipWithPadding<S0: SequenceType, S1: SequenceType>(s0: S0, _ s1: S1)
  -> NilPaddedZip<S0, S1> {
    return NilPaddedZip(s0: s0, s1: s1)
}

On 6 Dec 2015, at 23:44, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

Is there an implementation in the stdlib for (T?, T?) like this?

On Dec 6, 2015, at 4:37 PM, Dmitri Gribenko <gribozavr@gmail.com> wrote:

On Sun, Dec 6, 2015 at 3:34 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:
It's pretty easy to build your own Zips. Not sure the language really needs this. For example, I recently built a zip that produces (T?, T?) which fills one of the two with nil until both lists are consumed:

func longZip<S0: SequenceType, S1: SequenceType>(seq0: S0, _ seq1: S1) ->
    AnyGenerator<(S0.Generator.Element?, S1.Generator.Element?)> {

Just wanted to point out that AnyGenerator has an inherent cost from the type erasure. The implementation in the standard library uses generics and is fully optimizable.

Dmitri

--
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr@gmail.com>*/

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Davide De Franceschi) #8

Yes, it would, but you also need the ZipSequence to be able to support that, which would actually need variadic generics. Which aren't supported :slight_smile:

···

On 7 Dec 2015, at 13:12, Vinicius Vendramini via swift-evolution <swift-evolution@swift.org> wrote:

I wouldn't really know how to do this, but a better approach than implementing zip2 through zip10 might be making it a variadic function, no?

On Dec 6, 2015, at 6:49 PM, Donnacha Oisín Kidney via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

An implementation of this is actually pretty complicated, since you aren’t supposed to call a generator once it’s returned nil.

public struct NilPaddedZipGenerator<G0: GeneratorType, G1: GeneratorType> : GeneratorType {
  
  private var (g0, g1): (G0?, G1?)
  
  public mutating func next() -> (G0.Element?, G1.Element?)? {
    let (e0,e1) = (g0?.next(),g1?.next())
    switch (e0,e1) {
    case (nil,nil): return nil
    case ( _,nil): g1 = nil
    case (nil, _): g0 = nil
    default: break
    }
    return (e0,e1)
  }
}

public struct NilPaddedZip<S0: SequenceType, S1: SequenceType> : LazySequenceType {
  
  private let (s0, s1): (S0, S1)
  public func generate() -> NilPaddedZipGenerator<S0.Generator, S1.Generator> {
    return NilPaddedZipGenerator(g0: s0.generate(), g1: s1.generate())
  }
}

@warn_unused_result
public func zipWithPadding<S0: SequenceType, S1: SequenceType>(s0: S0, _ s1: S1)
  -> NilPaddedZip<S0, S1> {
    return NilPaddedZip(s0: s0, s1: s1)
}

On 6 Dec 2015, at 23:44, Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Is there an implementation in the stdlib for (T?, T?) like this?

On Dec 6, 2015, at 4:37 PM, Dmitri Gribenko <gribozavr@gmail.com <mailto:gribozavr@gmail.com>> wrote:

On Sun, Dec 6, 2015 at 3:34 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
It's pretty easy to build your own Zips. Not sure the language really needs this. For example, I recently built a zip that produces (T?, T?) which fills one of the two with nil until both lists are consumed:

func longZip<S0: SequenceType, S1: SequenceType>(seq0: S0, _ seq1: S1) ->
    AnyGenerator<(S0.Generator.Element?, S1.Generator.Element?)> {

Just wanted to point out that AnyGenerator has an inherent cost from the type erasure. The implementation in the standard library uses generics and is fully optimizable.

Dmitri

--
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr@gmail.com <mailto:gribozavr@gmail.com>>*/

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Matthew Johnson) #9

Yes, it would, but you also need the ZipSequence to be able to support that, which would actually need variadic generics. Which aren't supported :slight_smile:

Aren't supported *yet*. I am hoping somebody writes a proposal to add them. This isn't the first use case that has been discussed here in the first few days. The TupleConvertible proposal would also require them to be implemented in a general fashion.