如何用闭包定义相互递归?

时间:2016-01-18 05:35:04

标签: rust

我可以这样做:

fn func() -> (Vec<i32>, Vec<i32>) {
    let mut u = vec![0;5];
    let mut v = vec![0;5];

    fn foo(u: &mut [i32], v: &mut [i32], i: usize, j: usize) {
        for k in i+1..u.len() {
            u[k] += 1;
            bar(u, v, k, j);
        }
    }
    fn bar(u: &mut [i32], v: &mut [i32], i: usize, j: usize) {
        for k in j+1..v.len() {
            v[k] += 1;
            foo(u, v, i, k);
        }
    }
    foo(&mut u, &mut v, 0, 0);
    (u,v)
}

fn main() {
    let (u,v) = func();
    println!("{:?}", u);
    println!("{:?}", v);
}

但我更愿意这样做:

fn func() -> (Vec<i32>, Vec<i32>) {
    let mut u = vec![0;5];
    let mut v = vec![0;5];

    let foo = |i, j| {
        for k in i+1..u.len() {
            u[k] += 1;
            bar(k, j);
        }
    };
    let bar = |i, j| {
        for k in j+1..v.len() {
            v[k] += 1;
            foo(i, k);
        }
    };
    foo(0, 0);
    (u,v)
}

fn main() {
    let (u,v) = func();
    println!("{:?}", u);
    println!("{:?}", v);
}

第二个示例没有使用错误编译:未解析的名称bar。 在我的任务中,我可以通过一次递归来完成它,但它看起来不会很清楚。 有没有人有任何其他建议?

2 个答案:

答案 0 :(得分:3)

我有一个相互递归闭包的解决方案,但它并没有使用多个可变借用,所以我无法将它扩展到你的例子。

有一种方法可以使用定义相互递归闭包,使用类似于单this answer单递归的方法。你可以将闭包放在一个结构中,每个结构都借用该结构作为额外的参数。

fn func(n: u32) -> bool {
    struct EvenOdd<'a> {
        even: &'a Fn(u32, &EvenOdd<'a>) -> bool,
        odd: &'a Fn(u32, &EvenOdd<'a>) -> bool
    }
    let evenodd = EvenOdd {
        even: &|n, evenodd| {
            if n == 0 {
                true
            } else {
                (evenodd.odd)(n - 1, evenodd)
            }
        },
        odd: &|n, evenodd| {
            if n == 0 {
                false
            } else {
                (evenodd.even)(n - 1, evenodd)
            }
        }
    };
    (evenodd.even)(n, &evenodd)
}

fn main() {
    println!("{}", func(5));
    println!("{}", func(6));
}

答案 1 :(得分:2)

虽然在某些情况下定义相互递归闭包是可行的,正如Alex Knauth的回答所证明的那样,但我认为您通常不应该采用这种方法。它是不透明的,在另一个答案中指出了一些局限性,并且由于它在运行时使用特征对象和动态调度而导致性能开销。

Rust中的闭包可以认为是具有关联结构的函数,这些结构存储了您关闭的数据。因此,更通用的解决方案是定义您自己的结构以存储要关闭的数据,并在该结构上定义方法而不是闭包。对于这种情况,代码如下所示:

pub struct FooBar {
    pub u: Vec<i32>,
    pub v: Vec<i32>,
}

impl FooBar {
    fn new(u: Vec<i32>, v: Vec<i32>) -> Self {
        Self { u, v }
    }

    fn foo(&mut self, i: usize, j: usize) {
        for k in i+1..self.u.len() {
            self.u[k] += 1;
            self.bar(k, j);
        }
    }

    fn bar(&mut self, i: usize, j: usize) {
        for k in j+1..self.v.len() {
            self.v[k] += 1;
            self.foo(i, k);
        }
    }
}

fn main() {
    let mut x = FooBar::new(vec![0;5], vec![0;5]);
    x.foo(0, 0);
    println!("{:?}", x.u);
    println!("{:?}", x.v);
}

Playground

虽然它比闭包更冗长,并且需要一些更明确的类型注释,但它更灵活,更易于阅读,所以我通常会喜欢这种方法。