我将如何实现一个自定义枚举函数来实现这样的功能(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/