swiftiOS

Swift - 自定义一个Sequence类型进行迭代操作.

2018-01-08  本文已影响115人  Zafir_zzf

Sequence 协议是集合类型结构中的基础. 我们经常会对一个Array 或者Dictionary进行for 循环迭代正是因为它们遵守了Sequence协议并实现了迭代方法.
自然是一个协议,我们可以自定义一个类型来遵守它,也可以实现自己想要的迭代操作.
比如当前有一个整数10086, 有需求要取出这个数中从个位,十位,百位..到最高位的数..如果是字符串"10086" 我们可以直接对字符串序列进行遍历

    for i in "10086" {
        print(i)
     }

在Swift标准库总String类型是遵守了Sequence协议的,上图代码会根据字符串索引从第0个打印到它最后一个.
然而如果我们直接对10086这个int类型的整数进行for in 操作,编译器会告诉我们Type 'Int' does not conform to protocol 'Sequence'
没错,Int 当然不是一个序列.不遵守这个协议,无法进行迭代操作.

我们先来看一下Sequence协议是怎么定义的

protocol Sequence {
    associatedtype Iterator: IteratorProtocol
    func makeIterator() -> Iterator
}

满足一个Sequence协议的要求只有一个,,提供一个满足IteratorProtocol协议(迭代器协议)返回值的方法..很直接,我们实现它的目的也正是为了迭代.

/// InteratorProtocol 的定义如下
protocol IteratorProtocol {
    associatedtype Element
    mutating func next() -> Element?
}

关联类型 Element 指定了迭代器产生的值的类型. 比如String的迭代器的元素类型是Character. 在处理Sequence的泛型约束的时候我们经常会使用Iterator.Element, 这里的Element就是IteratorProtocl中定义的..
这个协议中只有一个 next方法,用来返回迭代的元素.可以使用for循环遍历的原因是for循环在背后帮我们做了一件事就是不断的调用迭代器的next方法,直到返回nil为止,

/// for循环做的事情实际是下面这个的简写模式
 var iterator = someSequence.makeIterator()
        while let element = iterator.next() {
            print(element)
        }

接下来完成我们的需求:

// 因为遍历一个Int的迭代器系统中没有,需要我们自己创建
struct NumberIterator: IteratorProtocol {
    typealias Element = Int
    var number: Int
    var offset = 1
    init(_ number: Int) {
        self.number = number
    }
    // IteratorPtocol中要求实现的函数
    mutating func next() -> NumberIterator.Element? {
    // 根据当前的offset返回个位/十位...数
        guard number / offset >= 1 else {return nil}
        offset = offset * 10
        return number / (offset / 10) % 10
    }
}
// 我们直接使用的序列
struct NumberSequence: Sequence {
    let number: Int // 其中只有一个int型的属性
    // Sequence要求实现的函数,返回一个迭代器,把我们刚才实现的迭代器返回.当然迭代器需要一个number值.
    func makeIterator() -> NumberInterator {
        return NumberInterator(number)
    }
}

现在我们成功创建好了自己的序列类型 NumberSequence,想要直接迭代一个Int类型的整数:

        for prefix in NumberSequence(193835) {
            print(prefix)
        }

直接转换成新的Sequence类型就可以进行迭代了..
在实际应用中,如果想要迭代某一个数据类型,让它遵守Sequence协议并且实现响应的方法就可以了.

上一篇 下一篇

猜你喜欢

热点阅读