Sequences | lazy

序列 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;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Lazy function refers to a programming concept where the evaluation of a function or expression is delayed until its result is actually needed. In other words, the function is not executed immediately but rather "lazy" or deferred until the value is required. Lazy evaluation can be useful in scenarios where computing a value is resource-intensive or time-consuming. By deferring the computation until it is needed, we can optimize performance and improve efficiency. Lazy functions are commonly used in functional programming languages and frameworks. For example, in Python, the `itertools` module provides a function called `islice` that allows you to lazily slice an iterable object without generating all the elements upfront. This can be handy when working with large datasets or infinite sequences. Here's an example of using `islice` to lazily slice an iterable: ```python from itertools import islice def generate_numbers(): n = 0 while True: yield n n += 1 numbers = generate_numbers() sliced_numbers = islice(numbers, 5, 10) for num in sliced_numbers: print(num) ``` In this example, `generate_numbers` is a generator function that produces an infinite sequence of numbers. Instead of generating all the numbers upfront, it yields them one by one as requested. The `islice` function lazily slices the sequence starting from index 5 to 10. As we iterate over `sliced_numbers`, it generates and prints the numbers on-demand, without having to store the entire sequence in memory. Lazy evaluation can be a powerful technique to optimize resource usage and improve performance in certain scenarios.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值