2016-07-16 1 views
3

Я ищу итератор для бесконечной итерации коллекции в режиме цикла. Чтобы достигнуть конечного индекса коллекции, итератор должен вернуть элемент в начальный индекс.Пользовательский итератор для бесконечной итерации коллекции в режиме цикла

Следующее решение кажется работающим, но я надеюсь, что оно может быть сделано лучше.

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()) 
} 

Правильно ли это сделать пользовательский итератор? Что можно улучшить?

Спасибо!

+1

Для дальнейшего использования вопросы об улучшении рабочего кода, вероятно, лучше подходят для обмена курсором ** Code Review **. http://codereview.stackexchange.com/ –

+1

IMO, codereview.se лучше всего подходит для обзора кода, который сам по себе будет иметь мало общего интереса и вряд ли будет искать явно (т. е. представляют интерес лучшие методы, а не код). Этот вопрос относится к обычно интересному фрагменту кода, касающемуся конкретно интересующей темы (правильное использование индексов Swift 3) и, вероятно, будет искать в будущем. Если бы плакат не включал код, они, несомненно, получили бы «Что вы пробовали?» Комментарии. Если они включают код, представляется несправедливым перенаправить их на codereview. –

ответ

6

В 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 здесь, который существует для улучшения производительности в несколько неясных случаях (в частности, около AnyIndex), так как ваш Iterator работает с любой коллекцией (и, следовательно, с любым индексом). Более простая версия будет index = collection.index(after: index), и это может быть лучше в большинстве случаев.

Для всех подробностей о показателях Swift 3 см. SE-0065.

Смежные вопросы