【问题标题】:Cartesian product match笛卡尔积匹配
【发布时间】:2017-03-13 15:54:08
【问题描述】:

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

// 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&lt;X&gt;X&lt; A&lt;X&gt;::Color &gt; )。出于这个原因,我找不到使用通用函数或类似函数的任何解决方案。

我相信这个问题可以通过宏轻松解决;类似:

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!?如何实现我的逻辑?

【问题讨论】:

  • 哎呀,看起来很痛苦:(祝你好运。
  • 我相信这个问题可以通过宏轻松解决——宏不会给 Rust 的功能添加任何真正的魔力,它们只是删除了样板。但是,您还没有向我们展示样板。相反,您已经用... 忽略了问题的实质。我很确定您不希望我们创建一个扩展为文字 ... 的宏,但我们不知道它应该是什么。请查看如何创建minimal reproducible example。我将首先提供 2 组 2 种类型的完整定义和 match 的完整所需代码。你可能会从中得到一些有用的东西。
  • @Shepmaster by ... A ... X ... 我的意思是f(A, X),我认为这是一个最小的、完整的和可验证的例子。在我的特定情况下,我需要创建一个通道来传输与$first 实现的特征关联的类型,然后启动一个线程,在其中实例化$second 并将其传递给函数(f1),然后调用函数(f2) 在主线程上的$firstf2 要求 $first 实现一个 trait,其泛型参数必须是与 $secondf1 的 trait 实现的关联类型,而 f1 需要 $second
  • 我想我们不同意 verifiable 的含义,因为我无法用您提供的代码验证 anything 并且根据我目前的理解,无论是否使用宏,都不可能编写您要求的代码。如果您 edit 您的问题要显示 ABZ 的完整定义,matcha, zb, z 和您从宏调用的函数签名,那么也许我们可以看到样板,然后构造宏。

标签: macros nested rust match repeat


【解决方案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"]);
}

【讨论】:

    【解决方案2】:

    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 并将其传递给指定的回调。

    【讨论】:

      猜你喜欢
      • 2021-06-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-25
      • 2013-07-14
      • 2015-07-13
      • 2016-10-08
      相关资源
      最近更新 更多