我正在寻找迭代器以循环模式无限迭代集合。因此,当达到收集的结束索引时,迭代器应该在start索引处返回元素。
以下解决方案似乎有效,但我希望它可以更好地制作。
public struct LoopIterator<T: Collection>: IteratorProtocol {
private let collection: T
private var startIndexOffset: T.IndexDistance
public init(collection: T) {
self.collection = collection
startIndexOffset = 0
}
public mutating func next() -> T.Iterator.Element? {
guard !collection.isEmpty else {
return nil
}
let index = collection.index(collection.startIndex, offsetBy: startIndexOffset)
startIndexOffset += T.IndexDistance(1)
if startIndexOffset >= collection.count {
startIndexOffset = 0
}
return collection[index]
}
}
extension Array {
func makeLoopIterator() -> LoopIterator<Array> {
return LoopIterator(collection: self)
}
}
// Testing...
// Will print: 1, 2, 3, 1, 2, 3
var it = [1, 2, 3].makeLoopIterator()
for _ in 0..<6 {
print(it.next())
}
这是做自定义迭代器的正确方法吗?有什么可以改进的?
谢谢!
在Swift 3(您正在使用)中,索引旨在由集合本身进行处理。有了它,您可以按如下方式简化:
public struct LoopIterator<Base: Collection>: IteratorProtocol {
private let collection: Base
private var index: Base.Index
public init(collection: Base) {
self.collection = collection
self.index = collection.startIndex
}
public mutating func next() -> Base.Iterator.Element? {
guard !collection.isEmpty else {
return nil
}
let result = collection[index]
collection.formIndex(after: &index) // (*) See discussion below
if index == collection.endIndex {
index = collection.startIndex
}
return result
}
}
现在我们只需向前移动索引,如果它现在指向结尾,则将其重置为开头。不需要count
或IndexDistance
。
请注意,我在这里使用了formIndex
,因为你的Iterator可以处理任何Collection(因此也适用于任何索引),因此可以在某些模糊的情况下(特别是在AnyIndex
周围)提高性能。更简单的版本是index = collection.index(after: index)
,在大多数情况下可能更好。
有关Swift 3指数的所有细节,请参阅SE-0065。
使用Swift 5,您可以使用以下示例之一来解决您的问题。
AnyIterator
作为创建符合IteratorProtocol
的新类型的替代方法,您可以使用AnyIterator
。以下代码基于Rob Napier的回答,展示了如何使用它:
extension Array {
func makeInfiniteLoopIterator() -> AnyIterator<Element> {
var index = self.startIndex
return AnyIterator({
if self.isEmpty {
return nil
}
let result = self[index]
self.formIndex(after: &index)
if index == self.endIndex {
index = self.startIndex
}
return result
})
}
}
用法:
let infiniteLoopIterator = [1, 2, 3].makeInfiniteLoopIterator()
for val in infiniteLoopIterator.prefix(5) {
print(val)
}
/*
prints:
1
2
3
1
2
*/
let infiniteLoopIterator = [1, 2, 3].makeInfiniteLoopIterator()
let array = Array(infiniteLoopIterator.prefix(7))
print(array) // prints: [1, 2, 3, 1, 2, 3, 1]
let infiniteLoopIterator = [1, 2, 3].makeInfiniteLoopIterator()
let val1 = infiniteLoopIterator.next()
let val2 = infiniteLoopIterator.next()
let val3 = infiniteLoopIterator.next()
let val4 = infiniteLoopIterator.next()
print(String(describing: val1)) // prints: Optional(1)
print(String(describing: val2)) // prints: Optional(2)
print(String(describing: val3)) // prints: Optional(3)
print(String(describing: val4)) // prints: Optional(1)
AnySequence
类似的方法是使用AnySequence
:
extension Array {
func makeInfiniteSequence() -> AnySequence<Element> {
return AnySequence({ () -> AnyIterator<Element> in
var index = self.startIndex
return AnyIterator({
if self.isEmpty {
return nil
}
let result = self[index]
self.formIndex(after: &index)
if index == self.endIndex {
index = self.startIndex
}
return result
})
})
}
}
用法:
let infiniteSequence = [1, 2, 3].makeInfiniteSequence()
for val in infiniteSequence.prefix(5) {
print(val)
}
/*
prints:
1
2
3
1
2
*/
let infiniteSequence = [1, 2, 3].makeInfiniteSequence()
let array = Array(infiniteSequence.prefix(7))
print(array) // prints: [1, 2, 3, 1, 2, 3, 1]