Как расширить String.Iterator в Swift - PullRequest
0 голосов
/ 12 ноября 2018

У меня есть String, как LINNIIBDDDN, в основном серия токенов. Я хотел бы использовать несколько итераторов, по одному для каждого типа токена. Я бы хотел, чтобы каждый итератор игнорировал токены, которые ему не принадлежат. То есть я хочу вызвать что-то вроде next_ish(), чтобы переместить итератор к следующему элементу его конкретного токена. Поэтому, если итератор N находится в индексе 3, и я вызываю next_ish(), я хочу перейти к индексу 10, следующему N, а не I в индексе 4. У меня есть код, который уже работает , но это большой код, он превращает String в массив, и у меня есть подклассы итераторов, в основном рукописные, без помощи Swift, хотя я уверен, что итераторы Swift более стабильны и тщательно протестированы. Я бы лучше использовал их код, чем мой, где это возможно.

Вроде бы просто добавить String.Iterator и добавить next_ish(), но я в растерянности. Моей первой наивной попыткой было продление String.Iterator. Я получаю ошибку Constrained extension must be declared on the unspecialized generic type 'IndexingIterator' with constraints specified by a 'where' clause. Я попытался выяснить, какой пункт where использовать, и ничего не нашел.

Здесь, в SO, есть много ответов о расширении массивов и обобщений, объединении всех элементов определенного типа в собственный массив, даже некоторые ответы о специализированных циклах for...in, но я не могу найти что-нибудь о расширении итераторов. Я прочитал Collections.swift и не нашел ничего полезного. Можно ли продлить String.Iterator? Это сделало бы мою жизнь намного проще. Если нет, то есть ли какой-нибудь встроенный механизм Swift для подобных вещей?

1 Ответ

0 голосов
/ 12 ноября 2018

String.Iterator (неявно) определяется как

typealias Iterator = IndexingIterator<String>

и сообщение об ошибке

Ограниченное расширение должно быть объявлено для неспециализированного универсального типа 'IndexingIterator' с ограничениями, указанными в предложении 'where'

означает, что мы должны определить методы расширения как

extension IndexingIterator where Elements == String { }

В качестве альтернативы (с возрастающей общностью):

extension IndexingIterator where Elements: StringProtocol { }
extension IndexingIterator where Elements.Element == Character { }

Я не нашел способа получить доступ к базовой коллекции (или позиции) изнутри метода расширения соответствующие члены определены как «Внутренний»:

public struct IndexingIterator<Elements : Collection> {
  internal let _elements: Elements
  internal var _position: Elements.Index
  // ...
}

То, что вы можете сделать, это передать требуемый элемент в ваш метод «следующего выхода» либо как сам элемент, либо как предикат:

extension IndexingIterator where Elements.Element == Character {

    mutating func next(_ wanted: Character) -> Character? {
        while let c = next() {
            if c == wanted { return c }
        }
        return nil
    }

    mutating func next(where predicate: ((Character) -> Bool)) -> Character? {
        while let c = next() {
            if predicate(c) { return c }
        }
        return nil
    }
}

Пример использования:

var it1 = "ABCDABCE".makeIterator()
print(it1.next("C") as Any) // Optional("C")
print(it1.next() as Any)    // Optional("D")
print(it1.next("C") as Any) // Optional("C")
print(it1.next() as Any)    // Optional("E")
print(it1.next("C") as Any) // nil

var it2 = "LINnIIBDDDN".makeIterator()
while let c = it2.next(where: { "Nn".contains($0) }) {
    print(c, terminator: ", ")
}
print()
// N, n, N,

Но на самом деле я бы рассмотрел String.Iterator как IndexingIterator деталь реализации и вместо этого расширил бы IteratorProtocol:

extension IteratorProtocol where Element: Equatable {
    mutating func next(_ wanted: Element) -> Element? {
        while let e = next() {
            if e == wanted { return e }
        }
        return nil
    }
}

extension IteratorProtocol  {
    mutating func next(where predicate: ((Element) -> Bool)) -> Element? {
        while let e = next() {
            if predicate(e) { return e }
        }
        return nil
    }
}

Это делает его пригодным для произвольных последовательностей. Пример: * +1032 *

var it3 = [1, 1, 2, 3, 5, 8, 13, 21, 34].makeIterator()
while let e = it3.next(where: { $0 % 2 == 0} ) {
    print(e, terminator: ", ")
}
print()
// 2, 8, 34,
...