Vla*_*lad 7 collections iterator infinite-loop swift
我正在寻找迭代器以循环模式无限迭代集合.因此,当达到收集的结束索引时,迭代器应该在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())
}
Run Code Online (Sandbox Code Playgroud)
这是做自定义迭代器的正确方法吗?有什么可以改进的?
谢谢!
Rob*_*ier 11
在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
}
}
Run Code Online (Sandbox Code Playgroud)
现在我们只需向前移动索引,如果它现在指向结尾,则将其重置为开头.不需要count或IndexDistance.
请注意,我在formIndex这里使用过,AnyIndex因为你的Iterator可以处理任何Collection(因此也适用于任何索引),因此可以在某些模糊的情况下(特别是在周围)提高性能.更简单的版本将是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]
index = self.index(after: index)
if index == self.endIndex {
index = self.startIndex
}
return result
})
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
let infiniteLoopIterator = [1, 2, 3].makeInfiniteLoopIterator()
for val in infiniteLoopIterator.prefix(5) {
print(val)
}
/*
prints:
1
2
3
1
2
*/
Run Code Online (Sandbox Code Playgroud)
let infiniteLoopIterator = [1, 2, 3].makeInfiniteLoopIterator()
let array = Array(infiniteLoopIterator.prefix(7))
print(array) // prints: [1, 2, 3, 1, 2, 3, 1]
Run Code Online (Sandbox Code Playgroud)
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)
Run Code Online (Sandbox Code Playgroud)
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) // alternative to: index = self.index(after: index)
if index == self.endIndex {
index = self.startIndex
}
return result
})
})
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
let infiniteSequence = [1, 2, 3].makeInfiniteSequence()
for val in infiniteSequence.prefix(5) {
print(val)
}
/*
prints:
1
2
3
1
2
*/
Run Code Online (Sandbox Code Playgroud)
let infiniteSequence = [1, 2, 3].makeInfiniteSequence()
let array = Array(infiniteSequence.prefix(7))
print(array) // prints: [1, 2, 3, 1, 2, 3, 1]
Run Code Online (Sandbox Code Playgroud)