与Rust相关的惯用语匹配

时间:2017-02-28 13:05:10

标签: rust idioms fall-through

我是Rust的新手,但作为Haskell的粉丝,我非常感谢match在Rust中的工作方式。现在我面临着一个罕见的情况,我确实需要堕落 - 从某种意义上说,我希望能够执行几个重叠的所有匹配情况。这有效:

fn options(stairs: i32) -> i32 {
    if stairs == 0 {
        return 1;
    }
    let mut count: i32 = 0;
    if stairs >= 1 {
        count += options(stairs - 1);
    }
    if stairs >= 2 {
        count += options(stairs - 2);
    }
    if stairs >= 3 {
        count += options(stairs - 3);
    }
    count
}

我的问题是,这是否是Rust中的惯用语或是否有更好的方法。

编辑:上下文是来自破解编码面试的问题:“孩子正在使用 n 步骤跑上楼梯,可以跳1步,2步,或一次3个步骤。实施一种方法来计算孩子爬楼梯的可能方式。“

4 个答案:

答案 0 :(得分:7)

基于definition of the tribonacci sequence,我发现你可以用这样更简洁的方式写出来:

fn options(stairs: i32) -> i32 {
    match stairs {
        0 => 0,
        1 => 1,
        2 => 1,
        3 => 2,
        _ => options(stairs - 1) + options(stairs - 2) + options(stairs - 3)
    }
}

我还建议将功能定义更改为仅接受正整数,例如u32

答案 1 :(得分:7)

为了回答这个一般性的问题,我认为<div class="a"> <p>This is some text.</p> </div> <div class="b"> <p>This is some text.</p> </div>和堕落在某种程度上是对立的。

match用于根据不同的模式执行不同的操作。大多数情况下,通过模式匹配提取的值非常不同,因此没有任何意义。

相反,引用指向序列的操作。表达序列的方法有很多种:递归,迭代,......

在您的情况下,例如,可以使用循环:

match

当然,在这个特定的例子中,我发现@ljedrz'解决方案更加优雅。

答案 2 :(得分:2)

我会建议Rust中的avoid recursion。最好使用iterators

struct Trib(usize, usize, usize);

impl Default for Trib {
    fn default() -> Trib {
        Trib(1, 0, 0)
    }
}

impl Iterator for Trib {
    type Item = usize;
    fn next(&mut self) -> Option<usize> {
        let &mut Trib(a, b, c) = self;
        let d = a + b + c;
        *self = Trib(b, c, d);
        Some(d)
    }
}

fn options(stairs: usize) -> usize {
    Trib::default().take(stairs + 1).last().unwrap()
}

fn main() {
    for (i, v) in Trib::default().enumerate().take(10) {
        println!("i={}, t={}", i, v);
    }

    println!("{}", options(0));
    println!("{}", options(1));
    println!("{}", options(3));
    println!("{}", options(7));
}

Playground

答案 3 :(得分:1)

你的代码对我来说看起来很地道,尽管@ljedrz 建议对相同的策略进行更优雅的重写。

由于这是一个面试问题,值得一提的是,这两种解决方案都不会被视为一个惊人的答案,因为这两种解决方案都占用楼梯数量的指数时间。

如果我想参加编码面试,我可能会写以下内容:

fn options(stairs: usize) -> u128 {
    let mut o = vec![1, 1, 2, 4];
    for _ in 3..stairs {
        o.push(o[o.len() - 1] + o[o.len() - 2] + o[o.len() - 3]);
    }
    o[stairs]
}

我们不是每次都重新计算 options(n),而是将每个值缓存在一个数组中。因此,这应该以线性时间而不是指数时间运行。我还切换到 u128 以便能够返回更大输入的解决方案。

请记住,这不是有效的解决方案,因为它使用线性空间。您可以通过只跟踪数组的最后三个元素来避免使用常量空间。我选择它是为了在简洁性、可读性和效率之间做出折衷。