是否不可能在使用 Box、Rc 或 Arc 等智能指针的递归数据类型上进行嵌套匹配?

2024-01-23

我正在尝试移植这个程序 https://gist.github.com/jdh30/f3d90a65a7abc7c9faf5c0299b002db3计算出nx^x 对 Rust 的象征性导数。这似乎很简单:

use std::rc::Rc;

type Expr = Rc<Expr2>;

enum Expr2 {
    Int(i32),
    Var(String),
    Add(Expr, Expr),
    Mul(Expr, Expr),
    Pow(Expr, Expr),
    Ln(Expr),
}

use Expr2::*;

fn pown(a: i32, b: i32) -> i32 {
    match b {
        0 => 1,
        1 => a,
        n => {
            let b = pown(a, b / 2);
            let b2 = b * b;
            if n % 2 == 0 {
                b2
            } else {
                b2 * a
            }
        }
    }
}

fn add(f: Expr, g: Expr) -> Expr {
    match (f, g) {
        (Int(m), Int(n)) => Int(m + n),
        (Int(0), f) => f,
        (f, Int(n)) => add(Int(n), f),
        (f, Add(Int(n), g)) => add(Int(n), add(f, g)),
        (Add(f, g), h) => add(f, add(g, h)),
        (f, g) => Add(f, g),
    }
}

fn mul(f: Expr, g: Expr) -> Expr {
    match (f, g) {
        (Int(m), Int(n)) => Int(m * n),
        (Int(0), f) => Int(0),
        (Int(1), f) => f,
        (f, Int(n)) => mul(Int(n), f),
        (f, Mul(Int(n), g)) => mul(Int(n), mul(f, g)),
        (Mul(f, g), h) => mul(f, mul(g, h)),
        (f, g) => Mul(f, g),
    }
}

fn pow(f: Expr, g: Expr) -> Expr {
    match (f, g) {
        (Int(m), Int(n)) => Int(pown(m, n)),
        (f, Int(0)) => Int(1),
        (f, Int(1)) => f,
        (Int(0), f) => Int(1),
        (f, g) => Pow(f, g),
    }
}

fn ln(f: Expr) -> Expr {
    match f {
        Int(1) => Int(0),
        f => Ln(f),
    }
}

fn d(x: String, f: Expr) -> Expr {
    match f {
        Int(_) => Int(0),
        Var(y) => if x == y {
            x
        } else {
            y
        },
        Add(f, g) => add(d(x, f), d(x, g)),
        Mul(f, g) => add(mul(f, d(x, g)), mul(g, d(x, f))),
        Pow(f, g) => mul(
            pow(f, g),
            add(mul(mul(g, d(x, f)), pow(f, Int(-1))), mul(ln(f), d(x, g))),
        ),
        Ln(f) => mul(d(x, f), pow(f, Int(-1))),
    }
}

fn count(f: Expr) -> i32 {
    match f {
        Int(_) | Var(_) => 1,
        Add(f, g) | Mul(f, g) | Pow(f, g) => count(f) + count(g),
        Ln(f) => count(f),
    }
}

fn string_of_expr(f: Expr) -> String {
    count(f).to_string();
}

fn nest(n: i32, f: Expr, x: Expr) -> Expr {
    if n == 0 {
        x
    } else {
        nest(n - 1, f, f(x))
    }
}

fn deriv(f: Expr) -> Expr {
    let df = d("x", f);
    format!("D({}) = {}", string_of_expr(f), string_of_expr(df));
    df
}

fn main() {
    let x = "x";
    let f = pow(x, x);
    // FIXME: Read command-line argument
    let df = nest(9, deriv, f);
    format!("{}", count(df));
}

需要将类型转换为引用计数enum在 Rust 和模式匹配中,代码非常相似,除了......它不起作用。据我所知,Rust 中的模式无法匹配取消引用的结果Rc。因此,无论我做什么,它都会在嵌套模式上失败,例如(f, Add(Int(n), g)).

我是否遗漏了一些东西,或者嵌套模式真的不可能匹配 Rust 中的递归数据类型?显然,有一种称为“框语法”的东西可以在已经在绘图板上绘制四年的模式(除其他外)内取消引用。


看来是的,目前不可能做到这一点。递归数据类型需要间接,例如Rc。与嵌套模式匹配时,间接需要取消引用。如今,Rust 中无法在模式匹配内取消引用。

解决方法是手动编译模式,即就好像您只有 C 风格一样switch.

一个称为“盒子模式”的功能已被自2014年以来一直在讨论 https://github.com/rust-lang/rfcs/pull/469将来可能会解决这个问题,但还没有发货。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

是否不可能在使用 Box、Rc 或 Arc 等智能指针的递归数据类型上进行嵌套匹配? 的相关文章

随机推荐