在声明性宏中构建所有元素对(二次集) [英] Build all pairs of elements (quadratic set) in declarative macro
问题描述
我有一个标识符列表,我想为该列表中的每对标识符调用一个宏.例如,如果我有 a
、b
和 c
,我想生成这个:
I have a list of identifier and I want to invoke a macro for each pair of identifiers from that list. For example, if I have a
, b
and c
, I would like to generate this:
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
块或类似的东西.
Of course, this is a dummy example. In my real code, the identifiers are types and I want to generate impl
blocks or something like that.
我的目标是只列出每个标识符一次.在我的实际代码中,我有大约 12 个标识符,并且不想手动写下所有 12² = 144 对.所以我认为一个宏可能对我有帮助.我知道它可以用所有强大的程序宏来解决,但我希望它也可以用声明性宏 (macro_rules!
) 来解决.
My goal is to list each identifier only once. In my real code, I have around 12 identifier and don't want to manually write down all 12² = 144 pairs. So I thought that a macro might help me. I know that it can be solved with the all powerful procedural macros, but I hoped that it's also possible with declarative macros (macro_rules!
).
我尝试了我认为最直观的方法来处理这个问题(两个嵌套的循环")(游乐场):
I tried what I thought was the intuitive way to handle this (two nested "loops") (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);
但是,这会导致此错误:
However, this results in this error:
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 | | )*
| |_____________^
我想这有点道理,所以我尝试了别的东西(游乐场):
I guess it makes kind of sense, so I tried something else (Playground):
macro_rules! print_all_pairs {
($($x:ident)*) => {
print_all_pairs!(@inner $($x)*; $($x)*);
};
(@inner $($x:ident)*; $($y:ident)*) => {
$(
$(
println!("{} <-> {}", $x, $y);
)*
)*
};
}
但这会导致与上述相同的错误!
But this results in the same error as above!
这完全可以通过声明性宏实现吗?
推荐答案
这完全可以通过声明性宏实现吗?
Is this possible with declarative macros at all?
是的.
但是(据我所知)我们必须通过头/尾递归遍历列表一次,而不是到处使用内置的 $( ... )*
机制.这意味着列表长度受宏扩展的递归深度限制.不过,这对于仅"12 个项目不是问题.
But (to the best of my knowledge) we have to iterate through the list via head/tail recursion once instead of using the built-in $( ... )*
mechanism everywhere. This means that the list length is limited by the recursion depth of macro expansion. That's not a problem for "only" 12 items, though.
在下面的代码中,我通过将宏名称传递给 for_all_pairs
宏,将所有对"功能与打印代码分开.(游乐场).>
In the code below, I separated the "for all pairs" functionality from the printing-code by passing a macro name to the for_all_pairs
macro. (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
这篇关于在声明性宏中构建所有元素对(二次集)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!