速成课第4节-练习解答

下面是上一节 Rust 速成课 “Crates, I/O 和更多迭代器” 中的练习题的解答。

这篇文章是基于 FP 完成 Rust 教学系列的一部分。 如果你在博客之外阅读这篇文章,你可以在介绍文章的顶部找到这个系列中所有文章的链接。 也可订阅 RSS 频道。

练习1

你可以在 Github Gist 找到我的完整解决方案。 如果你的解决方案看起来和我的有点不同,不要担心。 另外,看看你能从我的实现中学到什么有趣的东西,或者你想对它做一些改进。

练习2

struct TheAnswer;

impl Iterator for TheAnswer {
    type Item = u32;

    fn next(&mut self) -> Option<u32> {
        Some(42)
    }
}

练习3

让我们从简单的解决方案开始:

struct Fibs {
    x: u32,
    y: u32,
}

fn fibs() -> Fibs {
    Fibs {
        x: 0,
        y: 1,
    }
}

impl Iterator for Fibs {
    type Item = u32;

    fn next(&mut self) -> Option<u32> {
        let orig_x = self.x;
        let orig_y = self.y;

        self.x = orig_y;
        self.y = orig_x + orig_y;

        Some(orig_x)
    }
}

fn main() {
    for i in fibs().take(10) {
        println!("{}", i);
    }
}

然而,如果你把 take (10)提高到 take (47) ,你的输出结果看起来会是:

701408733
1134903170
thread 'main' panicked at 'attempt to add with overflow', foo.rs:21:18
note: Run with `RUST_BACKTRACE=1` for a backtrace.

一种解决方法是用 u64,但这只是在延迟问题。 相反,我们可以使用Rust的checked_add:

fn next(&mut self) -> Option<u32> {
    let orig_x = self.x;
    let orig_y = self.y;

    match orig_x.checked_add(orig_y) {
        // overflow
        None => None,

        // no overflow
        Some(new_y) => {
            self.x = orig_y;
            self.y = new_y;

            Some(orig_x)
        }
    }
}

现在,一旦发生溢出,我们的流将停止。

如果您想在这里真正进步,您实际上可以再输出两个值。 为此,我们需要分配一个取消引用的值,并使用一个枚举来跟踪我们的状态:

fn next(&mut self) -> Option<u32> {
    use Fibs::*;
    match *self {
        Done => None,
        OneLeft(x) => {
            *self = Done;
            Some(x)
        }
        Running(orig_x, orig_y) => {
            *self = match orig_x.checked_add(orig_y) {
                // overflow
                None => OneLeft(orig_y),
                Some(new_y) => Running(orig_y, new_y),
            };

            Some(orig_x)
        }
    }
}

练习4

impl<I> Iterator for Doubler<I>
    where
    I: Iterator,
    I::Item: std::ops::Add<Output=I::Item> + Copy,
{
    type Item = I::Item;
    fn next(&mut self) -> Option<Self::Item> {
        match self.iter.next() {
            None => None,
            Some(x) => Some(x + x),
        }
    }
}

练习5

Fold 方法有两个参数: 初始值 和 一个用于将运行总数与下一个值相加的函数。 使用闭包的一种方法是:

fn main() {
    let res = (1..11).fold(0, |x, y| x + y);
    println!("{}", res);
}

另一种方法是直接引用加法函数。 还记得 * 操作符有一个 Mul 特性吗? 还有一个添加特性:

fn main() {
    let res = (1..11).fold(0, std::ops::Add::add);
    println!("{}", res);
}

至于编写自己的求和函数:我们将最终回到通用的情况,并且必须提供适当的特征。 我们将使用类似的方法,分别使用From和u8:

fn sum<I>(iter: I) -> I::Item
    where
    I: Iterator,
    I::Item: std::ops::Add<Output=I::Item> + From<u8>,
{
    iter.fold(From::from(0u8), std::ops::Add::add)
}

最后更新于