在声明性宏中构建所有元素对(二次集)
Posted
技术标签:
【中文标题】在声明性宏中构建所有元素对(二次集)【英文标题】:Build all pairs of elements (quadratic set) in declarative macro 【发布时间】:2019-06-30 08:38:48 【问题描述】:我有一个标识符列表,我想为该列表中的每对标识符调用一个宏。例如,如果我有a
、b
和c
,我想生成这个:
println!(" <-> ", a, a);
println!(" <-> ", a, b);
println!(" <-> ", a, c);
println!(" <-> ", b, a);
println!(" <-> ", b, b);
println!(" <-> ", b, c);
println!(" <-> ", c, a);
println!(" <-> ", c, b);
println!(" <-> ", c, c);
当然,这是一个虚拟的例子。在我的真实代码中,标识符是类型,我想生成 impl
块或类似的东西。
我的目标是只列出每个标识符一次。在我的真实代码中,我有大约 12 个标识符并且不想手动写下所有 12² = 144 对。所以我认为宏可能会帮助我。我知道可以使用所有强大的过程宏来解决它,但我希望它也可以使用声明性宏 (macro_rules!
)。
我尝试了我认为是处理这个问题的直观方法(两个嵌套的“循环”)(Playground):
macro_rules! print_all_pairs
($($x:ident)*) =>
$(
$(
println!(" <-> ", $x, $x); // `$x, $x` feels awkward...
)*
)*
let a = 'a';
let b = 'b';
let c = 'c';
print_all_pairs!(a b c);
但是,这会导致此错误:
error: attempted to repeat an expression containing no syntax variables matched as repeating at this depth
--> src/main.rs:4:14
|
4 | $(
| ______________^
5 | | println!(" <-> ", $x, $x);
6 | | )*
| |_____________^
我想这有点道理,所以我尝试了其他方法 (Playground):
macro_rules! print_all_pairs
($($x:ident)*) =>
print_all_pairs!(@inner $($x)*; $($x)*);
;
(@inner $($x:ident)*; $($y:ident)*) =>
$(
$(
println!(" <-> ", $x, $y);
)*
)*
;
但这会导致与上面相同的错误!
声明性宏是否可以做到这一点?
【问题讨论】:
声明性宏的表达能力肯定是有缺陷的。但是我在 proc 宏中遇到了与quote
相同的问题。当您有两个重复变量时,它们总是成对插值,并且对所有排列进行插值非常棘手。
【参考方案1】:
声明性宏完全可以做到这一点吗?
是的。
但是(据我所知)我们必须通过头/尾递归遍历列表一次,而不是到处使用内置的$( ... )*
机制。这意味着列表长度受到宏扩展的递归深度的限制。不过,这对于“仅”12 个项目来说不是问题。
在下面的代码中,我通过将宏名称传递给for_all_pairs
宏,将“所有对”功能与打印代码分开。 (Playground)。
// The macro that expands into all pairs
macro_rules! for_all_pairs
($mac:ident: $($x:ident)*) =>
// Duplicate the list
for_all_pairs!(@inner $mac: $($x)*; $($x)*);
;
// The end of iteration: we exhausted the list
(@inner $mac:ident: ; $($x:ident)*) => ;
// The head/tail recursion: pick the first element of the first list
// and recursively do it for the tail.
(@inner $mac:ident: $head:ident $($tail:ident)*; $($x:ident)*) =>
$(
$mac!($head $x);
)*
for_all_pairs!(@inner $mac: $($tail)*; $($x)*);
;
// What you actually want to do for each pair
macro_rules! print_pair
($a:ident $b:ident) =>
println!(" <-> ", $a, $b);
// Test code
let a = 'a';
let b = 'b';
let c = 'c';
for_all_pairs!(print_pair: a b c);
此代码打印:
a <-> a
a <-> b
a <-> c
b <-> a
b <-> b
b <-> c
c <-> a
c <-> b
c <-> c
【讨论】:
以上是关于在声明性宏中构建所有元素对(二次集)的主要内容,如果未能解决你的问题,请参考以下文章