Swift for-in 循环与枚举自定义 Array2D 类?

标签 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 中实现枚举函数的任何好的解释

最佳答案

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

let word = "hello".characters

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

因为 characterView 的索引是 String.Index

因此,有多种方法可以获得您想要的东西。第一个是为您的结构重载 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) }
  }
}

但是您在那里复制代码,尤其是来自您的生成方法的代码。看到您已经使用坐标来返回每个元素,为什么不将您的枚举方法设置为默认方法,然后调用您的生成方法。像这样的事情:

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

关于Swift for-in 循环与枚举自定义 Array2D 类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31386142/

相关文章:

swift - XCode 7 自动完成功能无法正常工作

swift - 使用时区转换字符串时获取错误日期

iOS/swift 4 : How to copy a folder containing several folders containing files without removing the files and folders existing destination?

android - 相同的 AES 代码 swift 2 和 android

swift - 将 CustomError 转换为 ErrorType 转换为 NSError 会丢失 userInfo

ios - iOS 中表格 View 顶部出现奇怪的间隙

swift - 如何声明 Dictionary<String, Decimal> 符合协议(protocol)

ios - 使用 alamofire 的 Swift 2.0 迁移错误

ios - swift2.0,从 REST 调用返回信息(Alamofire)