Skip to content

25.3 对比立即求值

实际上,从代码组织逻辑上来说,迭代器模式已经是相对高阶一点的写法。对于一个刚刚接触编程的初学者来说,用下面这种写法才是最常见的:


rust
// 方案三
fn collector() -> Vec<u64> {
    let mut res = vec![];
    let mut curr : u64 = 1;
    let mut next : u64 = 1;
    loop {
        let new_next = curr.checked_add(next);

        if let Some(new_next) = new_next {
            curr = next;
            next = new_next;
            res.push(curr);
        } else {
            break;
        }
    }
    return res;
}

fn main() {
    let collected = collector();
    let mut it = collected.iter();
    while let Some(i) = it.next() {
        println!("{}", i);
    }
}

在这个方案中,我们用一个循环把 Fibonacci 数列提前生成出来了,存储在一个动态数组里,然后再去使用。这种做法可以看作是惰性求值的反向操作,叫作“立即求值”(eager evaluation)。不过,它有性能上的缺点,方案三提前把数据收集起来,缺少了灵活性。如果使用者只需要使用这个序列的前 10 个数据呢?如果是方案二迭代器的那种写法,使用者可以选择遍历 10 个元素后就提前 break;后面的数据既不需要生产,也不需要消费,还节省了一个临时的占用很大内存空间的容器,这就是“惰性求值”的好处。如果我们把方案三改成方案二迭代器的写法,性能和灵活性更佳,但是需要人工推理:哪些数据是需要存储在迭代器成员中的,哪些是不需要的,进入 next 方法时如何读取上一次的状态,退出 next 方法时如何保存这一次的状态等。这些都是“心智负担”。业务逻辑越复杂,这个负担越严重。

Released under the MIT License