英文:
Generating a combination tuple vec using a macro
问题
I will provide the translation of the code parts you've mentioned:
假设我有两个集合。
(a, b, c) 和 (p, q)
我想使用宏生成这样一个向量。
```let v = vec![(a,p), (a, q), (b, p), (b, q), (c, p), (c, q)]; ```
所以这是我正在编写的宏。可能有其他编写方式,但我想了解为什么这不起作用。我想将其用于更复杂的情况。我以前在其他地方(不在相同的上下文中)使用过这种模式,不确定为什么这不起作用。
fn main() {
macro_rules! vec_gen {
([$($left_item:ident),], $right:tt) => {
vec![$(vec_gen!(@call $right, $left_item)),]
};
(@call ($($right_item:ident),), $left_item:ident) => {
$(($left_item, $right_item),)
};
}
let a = 1;
let b = 2;
let c = 3;
let p = 1;
let q = 2;
let r = 3;
let data = vec_gen!([a, b, c], (p, q, r));
println!("{:?}", data);
}
rust-analyzer宏展开显示如下:(不完全是我想要的 - 它有额外的括号 - 至少它提供了一些结果)。
// vec_gen!宏的递归展开
// ======================================
(<[_]>::into_vec(
#[rustc_box]
$crate::boxed::Box::new([
((a, p), (a, q), (a, r)),
((b, p), (b, q), (b, r)),
((c, p), (c, q), (c, r)),
]),
))
尽管这在rust-analyzer宏展开中生成了*一些*代码,但在构建时会出现以下错误消息。
error: macro expansion ignores token ,
and any following
--> src\main.rs:7:40
|
4 | vec![$(vec_gen!(@call $right, $left_item)),]
| ----------------------------------- help: you might be missing a semicolon here: ;
| |
| caused by the macro expansion here
...
7 | $(($left_item, $right_item),)
| ^
|
= note: the usage of vec_gen!
is likely invalid in expression context
现在,如果我删除了`,`并添加了`;`,整个事情就会崩溃,rust-analyzer不会产生任何输出。
还要理解这个:“note: the usage of `vec_gen!` is likely invalid in expression context”。为什么?
如何正确使用这个宏模式?
Please note that translating code can sometimes result in a loss of clarity due to the specific syntax and context of the programming language. If you have any specific questions or need further assistance with the code, please feel free to ask.
英文:
Suppose I have two sets.
(a, b, c) and (p, q)
I want to generate a vector like this using a macro.
let v = vec![(a,p), (a, q), (b, p), (b, q), (c, p), (c, q)];
So this is the macro I was writing. There may be other ways to write this, but I want to understand why this does not work. I want to use this for something more complicated. I have used this pattern in the past (not in the same context) and not sure why this does not work.
fn main() {
macro_rules! vec_gen {
([$($left_item:ident),*], $right:tt) => {
vec![$(vec_gen!(@call $right, $left_item)),*]
};
(@call ($($right_item:ident),*), $left_item:ident) => {
$(($left_item, $right_item),)*
};
}
let a = 1;
let b = 2;
let c = 3;
let p = 1;
let q = 2;
let r = 3;
let data = vec_gen!([a, b, c], (p, q, r));
println!("{:?}", data);
}
rust-analyzer macro expansion shows this: (Not exactly what I want - it has extra set of parenthesis - at least it gives some results).
// Recursive expansion of vec_gen! macro
// ======================================
(<[_]>::into_vec(
#[rustc_box]
$crate::boxed::Box::new([
((a, p), (a, q), (a, r)),
((b, p), (b, q), (b, r)),
((c, p), (c, q), (c, r)),
]),
))
Even though this generates some code in rust-analyzer macro expansion, it gives this error message when building.
error: macro expansion ignores token `,` and any following
--> src\main.rs:7:40
|
4 | vec![$(vec_gen!(@call $right, $left_item)),*]
| ----------------------------------- help: you might be missing a semicolon here: `;`
| |
| caused by the macro expansion here
...
7 | $(($left_item, $right_item),)*
| ^
|
= note: the usage of `vec_gen!` is likely invalid in expression context
Now if I removed the ',' and add a ';' whole thing breaks down and rust-analyzer does not produce any output.
Also I am trying to understand this: "note: the usage of vec_gen!
is likely invalid in expression context". Why?
How to get this macro pattern right?
答案1
得分: 1
宏必须展开为完整的项。您不能创建一个宏,该宏将展开为vec![]
中的多个元素。
要解决这个问题,您需要使用push-down accumulation,使用一个宏展开,该宏将展开为另一个宏并随着时间构建表达式:
macro_rules! vec_gen {
(@expand [ $($expanded:tt)* ] [ $left_item:ident, $($rest:ident,)* ] [ $($right:ident,)* ]) => {
vec_gen!(@expand [ $($expanded)* $( ( $left_item, $right ), )* ] [ $($rest,)* ] [ $($right,)* ])
};
(@expand [ $($expanded:tt)* ] [] [ $($right:ident,)* ]) => {
vec![$($expanded)*]
};
([ $($left_item:ident),* $(,)? ], ( $($right_item:tt),* $(,)? )) => {
vec_gen!(@expand [ ] [ $($left_item,)* ] [ $($right_item,)* ])
};
}
但请注意,您正在构建的东西只是itertools::iproduct!()
的一种方式:
let data = Vec::from_iter(itertools::iproduct!([a, b, c], [p, q, r]));
英文:
Macros has to expand to whole items. You cannot create a macro that will expand to multiple elements in vec![]
.
To solve that, you have to use push-down accumulation using one macro expansion that will expand to another and build the expression over time:
macro_rules! vec_gen {
(@expand [ $($expanded:tt)* ] [ $left_item:ident, $($rest:ident,)* ] [ $($right:ident,)* ]) => {
vec_gen!(@expand [ $($expanded)* $( ( $left_item, $right ), )* ] [ $($rest,)* ] [ $($right,)* ])
};
(@expand [ $($expanded:tt)* ] [] [ $($right:ident,)* ]) => {
vec![$($expanded)*]
};
([ $($left_item:ident),* $(,)? ], ( $($right_item:tt),* $(,)? )) => {
vec_gen!(@expand [ ] [ $($left_item,)* ] [ $($right_item,)* ])
};
}
But note that what you're building is just itertools::iproduct!()
:
let data = Vec::from_iter(itertools::iproduct!([a, b, c], [p, q, r]));
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论