如何在Swift中扩展String.Iterator

时间:2018-11-11 21:58:40

标签: swift iterator extension-methods

我有一个String之类的LINNIIBDDDN,基本上是一系列令牌。我想使用多个迭代器,每个令牌类型一个。我想让每个迭代器都忽略不属于它的令牌。也就是说,我想调用类似next_ish()的名称,以将迭代器前进到其特定标记的下一个元素。因此,如果N迭代器位于索引3,并且我调用next_ish(),我希望它转到索引10,即下一个N,而不是索引4的I我有一些已经可以使用的代码,但是它很多,它使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 :(得分:2)

String.Iterator被(隐式)定义为

typealias Iterator = IndexingIterator<String>

和错误消息

  

必须在具有“ where”子句指定约束的非专业通用类型“ IndexingIterator”上声明受约束的扩展名

意味着我们必须将扩展方法定义为

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

这使得它可用于任意序列。示例:

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,