在自定义Array2D类上使用枚举的Swift for-in循环?

时间:2015-07-13 14:30:13

标签: swift swift2

我将如何实现自定义枚举函数,使其成为像这样的工作(Swift 2):

for ((column, row), item) in Array2D.enumerate() { ... }

在我的简单Array2D结构中:

struct Array2D<T> : SequenceType {
    let columns: Int
    let rows: Int
    private var array: Array<T?>

    init(columns: Int, rows: Int) {
        self.columns = columns
        self.rows = rows
        array = Array(count: rows*columns, repeatedValue: nil)
    }

    subscript(column: Int, row: Int) -> T? {
        get {
            return array[columns*row + column]
        }
        set {
            array[columns*row + column] = newValue
        }
    }

    func generate() -> AnyGenerator<T?> {
        var column = 0
        var row = 0

        return anyGenerator() {
            guard row < self.rows else {
                return nil
            }

            let item = self[column, row]

            if ++column == self.columns {
                column = 0
                ++row
            }

            return item
        }
    }
}

我无法在Swift

中实现枚举函数找到任何好的解释

2 个答案:

答案 0 :(得分:2)

Swift中的enumerate()函数返回其元组的第一部分从0开始的整数。那些与您列举的序列无关。因此,例如,这不起作用:

let word = "hello".characters

for (index, letter) in word.enumerate() {
  print(word[index])
}

因为characterView的索引是String.Index s。

所以有几种方法可以获得你想要的东西。第一个是为结构重载enumerate()。再说一次,有几天你可以做到这一点。首先,如何使用您自己的生成器的函数,并使用自己的逻辑来计算坐标。这可能有效:

func enumerate() -> AnyGenerator<((Int, Int), T?)> {
  let g = self.generate()
  var coord = -1
  return anyGenerator {
    g.next().map { ((++coord % self.columns, coord / self.columns), $0) }
  }
}

但是你在那里复制代码,尤其是你的generate方法。看到你已经使用坐标来返回每个元素,为什么不将你的枚举方法作为默认方法,并且你的generate方法调用它。像这样:

// Original generate method, now returns the coords it used
func enumerate() -> AnyGenerator<((Int, Int), T?)> {
  var column = 0
  var row = 0

  return anyGenerator() {
    guard row < self.rows else {
      return nil
    }

    let item = self[column, row]

    if ++column == self.columns {
      column = 0
      ++row
    }

    return ((column, row), item)
  }
}

// uses enumerate, ignores coords

func generate() -> AnyGenerator<T?> {
  let g = self.enumerate()
  return anyGenerator {
    g.next().map { $1 }
  }
}

如果你想稍微过分,你可以编写一个枚举函数,枚举其基数的特定索引。称之为specEnumerate:

public struct SpecEnumerateGen<Base : CollectionType> : GeneratorType {

  private var eG: Base.Generator
  private let sI: Base.Index
  private var i : Base.Index?

  public mutating func next() -> (Base.Index, Base.Generator.Element)? {
    i?._successorInPlace() ?? {self.i = self.sI}()
    return eG.next().map { (i!, $0) }
  }

  private init(g: Base.Generator, i: Base.Index) {
    self.eG = g
    self.sI = i
    self.i = nil
  }
}

public struct SpecEnumerateSeq<Base : CollectionType> : SequenceType {

  private let col: Base
  public func generate() -> SpecEnumerateGen<Base> {
    return SpecEnumerateGen(g: col.generate(), i: col.startIndex)
  }
}

public extension CollectionType {
  func specEnumerate() -> SpecEnumerateSeq<Self> {
    return SpecEnumerateSeq(col: self)
  }
}

使用此功能,工作:

let word = "hello".characters

for (index, letter) in word.specEnumerate() {
  print(word[index])
}

但是你的矩阵结构仍然是SequenceType,没有特定的索引。为此,您必须实现自己的MatrixIndex

public struct MatrixIndex: BidirectionalIndexType {

  public let x, y : Int

  private let columns: Int

  public func successor() -> MatrixIndex {
    return (x + 1 == columns) ?
      MatrixIndex(x: 0, y: y + 1, columns: columns) :
      MatrixIndex(x: x + 1, y: y, columns: columns)
  }

  public func predecessor() -> MatrixIndex {
    return (x == 0) ?
      MatrixIndex(x: columns - 1, y: y - 1, columns: columns) :
      MatrixIndex(x: x - 1, y: y, columns: columns)
  }
}

public func == (lhs: MatrixIndex, rhs: MatrixIndex) -> Bool {
  return lhs.x == rhs.x && lhs.y == rhs.y
}

extension MatrixIndex : CustomDebugStringConvertible {
  public var debugDescription: String {
    return "\(x), \(y)"
  }
}

extension MatrixIndex: RandomAccessIndexType {
  public func advancedBy(n: Int) -> MatrixIndex {
    let total = (y * columns) + x + n
    return MatrixIndex(x: total % columns, y: total / columns, columns: columns)
  }
  public func distanceTo(other: MatrixIndex) -> Int {
    return (other.x - x) + (other.y - y) * columns
  }
}

右。现在你需要另一个矩阵结构:

public struct Matrix2D<T> : MutableCollectionType {
  public var contents: [[T]]
  public subscript(index: MatrixIndex) -> T {
    get {
      return contents[index.y][index.x]
    } set {
      self.contents[index.y][index.x] = newValue
    }
  }
  public var count: Int { return contents[0].count * contents.count }
  public var startIndex: MatrixIndex {
    return MatrixIndex(x: 0, y: 0, columns: contents[0].count)
  }
  public var endIndex: MatrixIndex {
    return MatrixIndex(x: 0, y: contents.endIndex, columns: contents[0].count)
  }
}

右。所以现在,在所有这些之后,这有效:

let myMatrix = Matrix2D(contents: [[1, 2], [3, 4]])

for (coordinate, value) in myMatrix.specEnumerate() {
  value == myMatrix[coordinate] // True every time
}

答案 1 :(得分:0)

定义你自己的enumerate可能就足以利用你已经拥有的那个:

func enumerate() -> AnyGenerator<((Int, Int), T?)> {
    var index = 0
    var g = array.generate()
    return anyGenerator() {
        if let item = g.next() {
            let column = index % self.columns
            let row = index / self.columns
            ++index
            return ((column, row) , item)
        }
        return nil
    }
}

请注意,在这种情况下,您可以避免符合SequenceType,因为我使用私有数组中的generate。无论如何,这样做可能是一致的。

以下是如何使用它:

var a2d = Array2D<Int>(columns: 2, rows: 4)
a2d[0,1] = 4

for ((column, row), item) in a2d.enumerate() {
    print ("[\(column) : \(row)] = \(item)")
}

希望这有帮助