How do I return a sequence in Swift?

2020-05-21 03:27发布

问题:

I'm trying to write an extension for the Matrix example from the book, slightly tweaked to be generic.
I'm trying to write a method called getRow that returns a sequence of values at the given row.

In C#, I would have written this:

IEnumerable<T> GetRow (int row)
{
    return Enumerable
        .Range (0, this.columns)
        .Select ((column) => this.grid[row, columns]);
}

or alternatively

IEnumerable<T> GetRow (int row)
{
    for (var column = 0; column < this.columns; column++) {
        yield return this.grid[row, column];
    }
}

I'm not sure how to do this in Swift though.

Sequence seems to be the equivalent to IEnumerable<T> but I don't understand why it uses typealias instead of just being defined as Sequence<T> (see also this). Defining a method that returns generic Sequence<T> did not work:

extension Matrix {
    // Cannot specialize non-generic type 'Sequence'
    func getRow<T>(index: Int) -> Sequence<T> {
        return map(0..self.columns, { self[index, $0] })
    }
}

Then I got rid of <T> (but how is it supposed to be generic?):

extension Matrix {
    func getRow(index: Int) -> Sequence {
        return map(0..self.columns, { self[index, $0] })
    }
}

This compiles! However I can't use it:

var row = grid.getRow(0)
// 'Sequence' does not conform to protocol '_Sequence_'
for i in row {
    println("\(i)")
}

How do I properly type map result so it can be consumed in a for..in loop?

More on this issue: Associated Type Considered Weird

回答1:

Joe Groff suggested to wrap the result in SequenceOf<T>:

extension Matrix {
    func getRow(index: Int) -> SequenceOf<T> {
        return SequenceOf(map(0..self.columns, { self[index, $0] }))
    }
}

Indeed, this works but we had to wrap map result into a helper class which differs from how I do it in C#.

I have to admit I don't yet understand why Sequence and Generator use typealias and aren't generic protocols (like IEnumerable<T> in C#). There is an interesting ongoing discussion about this distinction so I'll leave a few links for a curious mind:

  1. Associated Types Considered Weird
  2. Associated types vs. type parameters - reason for the former?
  3. Abstract Type Members versus Generic Type Parameters in Scala
  4. Generics and protocols


回答2:

I think you are being mislead by the Swift compiler (which is a bit flaky at the moment). The type for your range 0..self.columns is Range<Int>, which is not a Sequence or Collection, so I don't think it can be used via map.

The implementation works for me:

extension Matrix {
  func getRow(index: Int) -> T[] {
    var row = T[]()
    for col in 0..self.columns {
      row.append(self[index, col])
    }
    return row
  }
}