美文网首页
Sequences | lazy

Sequences | lazy

作者: 精神薇 | 来源:发表于2022-07-22 11:52 被阅读0次

    序列 Sequence

    序列协议是集合类型结构中的基础。
    序列代表一系列类型相同的元素,你可以对这些元素进行迭代

    Sequence协议

    Sequence协议是集合类型的基础,Swift中Sequence协议为序列提供了迭代的能力。Sequence 协议只要求实现makeIterator()方法,该方法返回一个迭代器Iterator;

    public protocol Sequence {
      // 元素类型
      associatedtype Element 
      // 迭代器类型 == 元素类型
      associatedtype Iterator: IteratorProtocol where Iterator.Element == Element
      //迭代器
      __consuming func makeIterator() -> Iterator
      //...
    }
    
    迭代器

    序列通过创建一个迭代器来提供对元素的访问。迭代器每次产生一个序列的值,并且当遍历序列时对遍历状态进行管理。在 IteratorProtocol 协议中唯一的一个方法是 next(),这个方法需要在每次被调用时返回序列中的下一个值。当序列被耗尽时,next() 返回 nil

    public protocol IteratorProtocol {
      /// The type of element traversed by the iterator.
      associatedtype Element
      mutating func next() -> Element?
    }
    

    for循环的背后是编译器创建了一个迭代器,然后不断的调用next(),直到返回nil

    // 猜测编译器实现 for
    var iter = CustomIterator()
    while let x = iter.next(){
            //...
    }
    

    例子

    1.自定义迭代器类型,遵守IteratorProtocol协议,不需要指明Element类型,编译器会从next的返回类型推断出Element的类型
    自定义序列,遵守Sequence协议,同样不需要指明Element类型,编译器会从makeIterator的类型中推断出Element类型。
    最后通过for-in 就能不断打印结果了

    struct customProtocol:IteratorProtocol{
            //Element 可以省略,编译器会从next的返回类型推断出Element的类型
            //typealias Element = Int
        func next() -> Int? {
            return 1
        }
    }
    //...
    struct customSequence: Sequence{
            //同样,编译器会从makeIterator的类型中推断出Element类型,不需要再次指明
        func makeIterator() -> some IteratorProtocol {
            customProtocol()
        }
    }
    //...
    var customPrint = customSequence()
    for i in customPrint{
        print(i) // 由于next返回1,所以这里会无限打印 
    }
    

    2.自定义反转一个迭代器

    // 先定义一个实现IteratorProtocol 的类型
    struct ReverseIterator<T>:IteratorProtocol{
        typealias Element = T;
        
        var arr : [Element];
        var idx = 0;
        
        init(arr:[Element]) {
            self.arr = arr;
            idx = arr.count - 1;
        }
        
        
        mutating func next() -> T? {
            if idx < 0 {
                return nil;
            }else{
                let ele = arr[idx];
                idx = idx - 1;
                return ele;
            }
        }
    }
    
    
    // 再来定义sequence
    
    struct ReverceSequence<T>:Sequence{
        
        var arr:[T];
        
        init(arr:[T]) {
            self.arr = arr;
        }
        
        __consuming func makeIterator() -> ReverseIterator<T> {
            return ReverseIterator(arr: self.arr);
        }
    }
    
    
    
    //
    let array = [2,5,8,10];
    for i in ReverceSequence(arr: array){
        // 10 8 5 2
       print(i, separator: "-", terminator: "-");
    }
    

    Lazy变量

    惰性变量是按需初始化的存储属性,只能在struct或class中使用惰性变量。
    例如,创建一个带有惰性变量的Person结构来计算BMI:

    struct Person {
        var weight: Double
        var height: Double
        
        lazy var BMIIndex: Double = {
            return weight / pow(height, 2)
        }()
    }
    ///当初始化Person对象时,BMI不会自动计算。而是在第一次引用的时候才计算
    var jack = Person(weight: 90, height: 120)
    print(jack.BMIIndex)
    

    Lazy Sequences

    在Swift标准库中,SequenceType和CollectionType协议都有个叫lazy的计算属性,它能返回一个特殊的LazySequence或LazyCollection。
    这些类型只能被用到map、filter、flatMap这样的高阶函数中,而且是以一种惰性的方式。
    对于那些不需要完全运行,可能提前退出的情况,使用lazy来进行性能优化效果会非常有效。

    func increment(x: Int) -> Int {
        print("访问:\(x)")
        return x + 1
    }
    
    let array = Array(0..<10)
    
    print("直接使用map的结果")
    let incrementArr = array.map(increment)
    print(incrementArr[5])
    
    print("\n使用lazy属性的结果")
    let lazyIncrementArr = array.lazy.map(increment)
    print(lazyIncrementArr[5])
    

    输出的结果:

    直接使用map的结果:
    访问:0
    访问:1
    访问:2
    访问:3
    访问:4
    访问:5
    访问:6
    访问:7
    访问:8
    访问:9
    6
    
    使用lazy属性的结果:
    访问:5
    6
    
    • 直接使用map,所有的输出值都被计算出来了!即使只读了第5个元素。
    • 使用了lazy,仅调用了第5个元素的计算,其他元素计算并不会被调用。
      使用lazy后,计算量明显降低很多。如果array的体量更大,且increment更复杂,那么节省的计算量就更明显了。
    懒加载情景
    • 全局的常量/变量都是懒加载的,不需要标记lazy
    • 标记为static的存储型的类型属性(常量变量)也是懒加载的,不需要标记lazy;(补充:static可以修饰存储型的和计算型的类型属性,class可以修饰类类型的计算型的类型属性)
    • 延迟属性:必须使用lazy var标记;(延迟属性只能使用var修饰,不能使用let修饰)
    延迟属性与闭包
    • 延迟属性使用lazy var标记声明,其初始值可以使用直接方式创建,也可以使用闭包方式创建;
    // 直接方式创建
    lazy var person1: Person = Person()
    
    // 闭包方式创建
    lazy var person2: Person = {
        let person = Person()
        p.name = "Tom"
        print("Tom...")
        return person
    }()
    
    • 闭包不仅仅可以给延迟属性(lazy var)设置初始值,也可以给全局的常/变量、类的属性、对象的属性进行初始值,计算属性本质通过闭包实现的;
    let i = {
        return 0
    }()
    
    class Person {
        static let a = {
            return 1
        }()
    
        var b = {
            return 2
        }()
    }
    
    
    • 在布局UI控件的时候,也可以使用闭包方式来完成初始化赋值,这样更便于复用、更简洁;
    let leftButton: UIButton = {
        let button = UIButton(frame: buttonSize)
        button.backgroundColor = .black
        button.titleLabel?.text = "left"
        return button
    }()
    
    let rightButton: UIButton = {
        let button = UIButton(frame: buttonSize)
        button.backgroundColor = .black
        button.titleLabel?.text = "right"
        return button
    }()
    
    
    • 延迟属性使用闭包方式创建,在闭包中使用self不会产生循环引用;全局的常/变量、类的属性、对象的属性使用闭包方式创建,在闭包中无法使用self;

    相关文章

      网友评论

          本文标题:Sequences | lazy

          本文链接:https://www.haomeiwen.com/subject/imlmirtx.html