笛卡儿产品匹配

问题描述 投票:3回答:2

我有两组不完整的类型(即结构名称,缺少通用参数和生命周期),我需要为每个可能的组合对执行一些代码:

// these are my types
struct A<T> { ... }
struct B<'a, 'b, T> { ... }
struct C { ... }

struct X<T> { ... }
struct Y { ... }
struct W<'a> { ... }
struct Z<T, D> { ... }

// this is the code I need to generate
match (first_key, second_key) {
    ("a", "x") => { ... A ... X ... }
    ("a", "y") => { ... A ... Y ... }
    ("a", "w") => { ... A ... W ... }
    ("a", "z") => { ... A ... Z ... }
    ("b", "x") => { ... B ... X ... }
    ("b", "y") => { ... B ... Y ... }
    // ...
}

第一组(ABC)和第二组(XYWZ)的结构具有相互依赖的通用参数(例如,对于("a", "x")的情况,实际类型将是使用的是A<X>X< A<X>::Color >)。出于这个原因,我找不到使用泛型函数或类似函数的任何解决方案。

我相信这个问题很容易用宏来解决;就像是:

macro_rules! my_code {
    ( $first_type:tt), $second_type:tt ) => {
        // ... $first_type ... $second_type ...
    }
}

product_match!( (first_key, second_key) {
    { "a" => A, "b" => B, "c" => C },
    { "x" => X, "y" => Y, "w" => W, "z" => Z }
} => my_code )

但是在我工作了好几个小时后,我没能实施product_match。我找不到任何简单的方法来重复嵌套;我相信唯一的解决方案是使用宏来将匹配案例列表转换为嵌套的值元组,然后对它们进行递归,但我发现这很难实现。

另一种选择可能是使用构建脚本生成那个大match的代码,但这个解决方案听起来很脏。

我错过了这个问题的简单解决方案吗?有没有简单的方法来实现product_match!?我该如何实现我的逻辑?

macros nested rust match repetition
2个回答
1
投票

我认为您使用宏实现笛卡尔积的想法是最好的。

我不太确定你想要的match表达式是什么,所以我将implemented改为重复函数调用。然而,宏观管道应该大致相同。希望你能从这里拿走它。

macro_rules! cp_inner {
    ($f: expr, $x: expr, [$($y: expr),*]) => {
        $($f($x, $y);)*
    }
}

macro_rules! cartesian_product {
    ($f: expr, [$($x: expr),*], $ys: tt) => {
        $(cp_inner!($f, $x, $ys);)*;
    }
}

fn print_pair(x: u32, y: &'static str) {
    println!("({}, {})", x, y);
}

pub fn main() {
    cartesian_product!(print_pair, [1, 2, 3], ["apple", "banana", "cherry"]);
}

0
投票

Here是一个宏cartesian_match,可以使用如下:

fn main() {
    macro_rules! test( ($x: tt, $y: tt, $z: tt,) => {
        println!("{:?} {:?} {:?}", $x, $y, $z);
    });
    #[derive(Debug)]
    enum E {
        V1, V2, V3,
    }
    let b = false;
    let oi = Some(6);
    let e = E::V1;
    cartesian_match!(
        test,
        match (oi) {
            Some(n) => {format!("{} is the number", n)},
            None => {None as Option<usize>},
        },
        match (b) {
            true => true,
            false => {E::V3},
        },
        match (e) {
            E::V1 => true,
            E::V2 => 'l',
            E::V3 => 2,
        },
    );
}

调用cartesian_match在边缘有点粗糙(注意所有大括号),并且可能不支持普通match语句中支持的所有模式。

宏定义如下:

macro_rules! cartesian_match(
    (
        $macro_callback: ident,
        $(match ($e: expr) {
            $($x: pat => $y: tt,)*
        },)*
    ) => {
        cartesian_match!(@p0,
            $macro_callback,
            (),
            $(match ($e) {
                $($x => $y,)*
            },)*
        )
    };
    (@p0,
        $macro_callback: ident,
        $rest_packed: tt,
        match ($e: expr) {
            $($x: pat => $y: tt,)*
        },
        $(match ($e2: expr) {
            $($x2: pat => $y2: tt,)*
        },)*
    ) => {
        cartesian_match!(@p0,
            $macro_callback,
            (
                match ($e) {
                    $($x => $y,)*
                },
                $rest_packed,
            ),
            $(match ($e2) {
                $($x2 => $y2,)*
            },)*
        )
    };
    (@p0,
        $macro_callback: ident,
        $rest_packed: tt,
    ) => {
        cartesian_match!(@p1,
            $macro_callback,
            @matched{()},
            $rest_packed,
        )
    };
    (@p1,
        $macro_callback: ident,
        @matched{$matched_packed: tt},
        (
            match ($e: expr) {
                $($x: pat => $y: tt,)*
            },
            $rest_packed: tt,
        ),
    ) => {
        match $e {
            $($x => cartesian_match!(@p1,
                $macro_callback,
                @matched{ ($matched_packed, $y,) },
                $rest_packed,
            ),)*
        }
    };
    (@p1,
        $macro_callback: ident,
        @matched{$matched_packed: tt},
        (),
    ) => {
        cartesian_match!(@p2,
            $macro_callback,
            @unpacked(),
            $matched_packed,
        )
        //$macro_callback!($matched_packed)
    };
    (@p2,
        $macro_callback: ident,
        @unpacked($($u: tt,)*),
        (
            $rest_packed: tt,
            $y: tt,
        ),
    ) => {
        cartesian_match!(@p2,
            $macro_callback,
            @unpacked($($u,)* $y,),
            $rest_packed,
        )
    };
    (@p2,
        $macro_callback: ident,
        @unpacked($($u: tt,)*),
        (),
    ) => {
        $macro_callback!($($u,)*)
    };
);

它需要可变数量的match项目,并以嵌套的方式一个接一个地扩展它们。它在不同的“内部阶段”(由宏参数列表中的@-prefixed参数表示)中这样做:

  • 阶段@p0获取matches列表并将它们转换为单个tt。从本质上讲,它将match_1, match_2, match_3,转变为像(match_1, (match_2, (match_3, (),)))这样的东西。 (这样做是为了防止“不一致的锁步迭代”。)
  • 阶段@p1解包由@p0生成的东西并将其转换为嵌套的match语句。它使用与@p0相同的技巧来存储与当前嵌套深度匹配的元素。
  • 阶段@p2解包由@p1生成的东西(即它实质上将((((), v3), v2), v1,)转换为v1, v2, v3并将其传递到指定的回调。
© www.soinside.com 2019 - 2024. All rights reserved.