【发布时间】:2014-06-07 16:23:45
【问题描述】:
我们知道,我们可以使用for..in 循环遍历Arrays 或Dictionaries。但是,我想像这样迭代我自己的CustomClass:
for i in CustomClass {
someFunction(i)
}
CustomClass 必须支持哪些操作/协议才能实现这一点?
【问题讨论】:
标签: swift
我们知道,我们可以使用for..in 循环遍历Arrays 或Dictionaries。但是,我想像这样迭代我自己的CustomClass:
for i in CustomClass {
someFunction(i)
}
CustomClass 必须支持哪些操作/协议才能实现这一点?
【问题讨论】:
标签: swift
假设您有一个“汽车”类,您希望能够使用 for..in 循环对其进行迭代:
let cars = Cars()
for car in cars {
println(car.name)
}
最简单的方法是将 AnyGenerator 与这样的类一起使用:
class Car {
var name : String
init(name : String) {
self.name = name
}
}
class Cars : SequenceType {
var carList : [Car] = []
func generate() -> AnyGenerator<Car> {
// keep the index of the next car in the iteration
var nextIndex = carList.count-1
// Construct a AnyGenerator<Car> instance, passing a closure that returns the next car in the iteration
return anyGenerator {
if (nextIndex < 0) {
return nil
}
return self.carList[nextIndex--]
}
}
}
要尝试一个完整的工作示例,请添加上面的两个类,然后尝试像这样使用它们,添加几个测试项:
let cars = Cars()
cars.carList.append(Car(name: "Honda"))
cars.carList.append(Car(name: "Toyota"))
for car in cars {
println(car.name)
}
就是这样,简单。
更多信息:http://lillylabs.no/2014/09/30/make-iterable-swift-collection-type-sequencetype
【讨论】:
AnyGenerator。你从哪里得到的? SequenceType 仍然是 for...in 的协议。
以上所有答案都可能有点棘手。如果你的类中有一个数组,你想对其进行迭代(就像在@Lee Whitney 的回答中一样),那么有一种更简单的方法来实现它。你有以下类,CustomClass:
class CustomClass: SequenceType {
let array: [String]
init(array: [String]) {
self.array = array
}
func generate() -> IndexingGenerator<[String]> {
return array.generate()
}
}
就这么简单。经测试可在最新的 Xcode 版本(撰写本文时为 6.1)和 iOS 8.1.2 中工作。不过,这段代码在未来的版本中应该是稳定的。
附:使用泛型,您可以按照这种模式轻松地制作自己的 Array 副本,并且只实现您想要的方法。
【讨论】:
@Matt Gibson 是正确的。不过,我想添加更多信息以供将来参考。
来自高级 Swift:
这段代码:
for x in someSequence {
...
}
转换成这个:
var __g = someSequence.generate()
while let x = __g.next() {
...
}
因此,必须采用序列,它给出了generate() 和next() 类。以下是这些协议:
protocol Generator {
typealias Element
mutating func next() -> Element?
}
protocol Sequence {
typealias GeneratorType : Generator
func generate() -> GeneratorType
}
【讨论】:
For in 语句循环遍历GeneratorType,它可以选择从SequenceType 检索它。所以从技术上讲,您需要为它提供GeneratorType 或 SequenceType。
这将是SequenceType 协议及其相关的Generator 协议。
SequenceType 协议表示该类必须实现generate(),它返回符合Generator 协议的内容,这是实际工作的位; Generator 协议是具有最重要的next() 方法的协议。
在WWDC 2014 video“Advanced Swift”中有一个实现它以允许for..in 的示例(在泛型示例“A Simple Generic Stack”中,从幻灯片 183 开始。)
关于为for..in 实现哪个协议的基本信息在文档的Statements 部分,其中简要概述了SequenceType 和Generator
【讨论】:
Sequence 在 Swift 1.0 中已重命名为 SequenceType。
注意
AnyGenerator和SequenceType是新版本中不存在的旧类型。你现在需要实现Sequence协议。
有两种方法可以实现Sequence。
只需实现next()方法即可同时符合Sequence、IteratorProtocol。这种方法是最简单的,并且在标题中有一个示例。见Sequence.swift。同时实现这两种协议可能是不现实的或无法满足您的需求。 (它可能会阻止同时迭代两个不同的实例等)
符合Sequence并返回一个实现IteratorProtocol的对象。我认为这是现实世界课程中最常见的情况(当事情变得有点复杂时,不是 Hello Worlds)。 Sequence.swift中也有例子
下面是方法 2 的示例。一个可迭代的自定义类(链表):
/// Linked List Node:
public class LinkedListNode <T> {
public internal(set) var value: T
public internal(set) var next: LinkedListNode<T>?
internal init(_ value: T) {
self.value = value
self.next = nil
}
}
/// Linked List with append method only.
public class LinkedList<T> {
public internal(set) var first: LinkedListNode<T>? = nil
public internal(set) var last: LinkedListNode<T>? = nil
/// Appends a new node.
public func append(_ value: T) {
if first == nil {
first = LinkedListNode(value)
last = first
} else {
last.next = LinkedListNode(value)
last = last.next
}
}
}
最后是序列实现
/// Sequence protocol adoption. It allows `for ... in` and a bunch of other methods too.
extension LinkedList: Sequence {
/// Iterator implementation
public class Iterator<T>: IteratorProtocol {
/// Maintain a ref to current element so next element can be reached
var cur: LinkedListNode<T>?
/// IteratorProtocol protocol requirement
public func next() -> T? {
let res = cur?.value
cur = cur?.next
return res
}
}
/// Sequence protocol requirement
public func makeIterator() -> Iterator<T> {
let g = LinkedList.Iterator()
g.cur = first
return g
}
}
用法:
let linkedList = LinkedList<Int>()
linkedList.append(3)
linkedList.append(6)
linkedList.append(9)
linkedList.append(12)
for element in linkedList {
print(element)
}
let odds = linkedList.filter { return $0 % 2 == 0 }
print(odds)
【讨论】:
接受的答案是正确的,直到最近才被接受的方法来解决这个问题。但是,鉴于在 Swift 2.0 中引入了协议扩展,而不是遵循 SequenceType 并实现 func generate() -> GeneratorOf<Car>,现在有一个抽象基类为您处理此功能的实现,称为 AnyGenerator<T> (see Apple docs),因为GeneratorOf<T> 不再存在。
这意味着您可以简单地继承这个抽象基类,并通过这样做继承上述协议一致性的所有功能:
class Cars: AnyGenerator<Car> {
private var carList = [Car]()
private var currentIndex:Int
...
}
然后只需要覆盖GeneratorType 协议(AnyGenerator<T> 也符合)声明的next() 方法即可定义所需的迭代行为:
class Cars: AnyGenerator<Car> {
private var carList = [Car]()
private var currentIndex:Int
override func next() -> Car? {
if (currentIndex < self.carList.count) {
currentIndex++
return self.carList[currentIndex-1]
} else {
currentIndex = 0;
return nil
}
}
}
【讨论】:
AnyGenerator”。我没有看到 AnyGenerator 为您做任何事情。我不明白他们为什么做出改变,但没有提供任何指导或解释。
AnyGenerator。查看对已接受答案的编辑。他们只是将 GeneratorOf 重命名为 AnyGenerator,并将 GeneratorOf(next:) 移至新函数 anyGenerator()