计算宏中重复的长度
Counting length of repetition in macro
我正在尝试实现一个宏以允许创建类似 MATLAB 的矩阵。我有一个基本的工作宏,但我还有很长的路要走。
我希望能够执行正确的结构(每行中的元素数量相同),但我不确定如何在宏中执行此操作。我想我想强制每个内部重复具有相同的长度 - 这是我能做的吗?
到目前为止,这是我的代码:
pub struct Matrix<T> {
pub cols: usize,
pub rows: usize,
pub data: Vec<T>
}
macro_rules! mat {
( $($( $x:expr ),*);* ) => {
{
let mut vec = Vec::new();
let mut rows = 0;
$(
$(
vec.push($x);
)*
rows += 1;
)*
Matrix { cols : vec.len()/rows, rows: rows, data: vec}
}
};
}
It works 但如您所见,这并不是很安全。对结构没有限制。
我想用这个宏做更多的事情,但我认为这是一个好的开始!
更新:
这里是 some playground code 我设计的一个蹩脚的实现。如果有人有更好的建议请告诉我!不然我自己关了
首先,要快速解决问题的标题:请参阅 Counting chapter in The Little Book of Rust Macros。总结一下:没有直接的方法,你需要编写一个宏来扩展到你可以用常规代码计算的东西。
现在,来解决你的实际问题:天哪。
这不是你想要的计数,如果sub-sequences有不同的长度,它会在编译时失败。
首先,没有clean 方法可以从宏触发编译失败。您可以触发一些 other pre-existing 错误,但您无法控制实际的错误消息。
其次,在宏中进行"variable"比较根本就没有简单的方法。您可以有时 与固定的标记序列进行比较,但您不会在这里这样做。
所以它是两倍 not-really-doable。
最简单的做法是在运行时检查构造期间的长度,如果不匹配,return 会出现错误或恐慌。
实际上不可能吗?我不这么认为。如果您愿意接受难以理解的错误消息和 大量 的复杂性跳跃,您可以检查两个标记序列之间的长度是否相等,如下所示:
macro_rules! tts_equal_len {
(($_lhs:tt $($lhs_tail:tt)*), ($_rhs:tt $($rhs_tail:tt)*)) => {
tts_equal_len!(($($lhs_tail)*), ($($rhs_tail)*))
};
(($($_lhs_tail:tt)+), ()) => { do_something_bad!() };
((), ($($_rhs_tail:tt)+)) => { do_something_bad!() };
((), ()) => { do_something_good!() };
}
macro_rules! do_something_bad { () => { { println!("kaboom!") } } }
macro_rules! do_something_good { () => { { println!("no kaboom!") } } }
fn main() {
tts_equal_len!((,,,), (,,,));
tts_equal_len!((,,,), (,,));
tts_equal_len!((,), (,,));
}
同样,真正的问题是找到一些在编译时失败的方法,以便用户理解为什么编译失败。
macro_rules! count {
() => (0usize);
( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));
}
macro_rules! mat {
( $( $x:expr ),* ) => { {
let vec = vec![$($x),*];
Matrix { cols : vec.len(), rows: 1, data: vec }
} };
( $( $x0:expr ),* ; $($( $x:expr ),*);* ) => { {
let mut _assert_width0 = [(); count!($($x0)*)];
let mut vec = Vec::new();
let rows = 1usize;
let cols = count!($($x0)*);
$( vec.push($x0); )*
$(
let rows = rows + 1usize;
let _assert_width = [(); count!($($x)*)];
_assert_width0 = _assert_width;
$( vec.push($x); )*
)*
Matrix { cols : cols, rows: rows, data: vec }
} }
}
count!
宏扩展为常量表达式,表示它作为输入获得的参数数量。它只是 mat!
宏的帮手。如果你需要计算很多项而编译器无法处理它,请参阅 the Counting chapter in The Little Book of Rust Macros,其中有更复杂的计数宏。
我的宏版本使用虚拟变量和赋值来验证所有行的宽度是否相同。首先,我更改了宏的模式以将第一行与后续行分开处理。第一个变量 _assert_width0
用单位数组(()
初始化,这使得数组不占用内存),数组的大小是第一行中的项目数。然后,_assert_width
也用一个单位数组初始化,数组的大小是每个后续行中的项目数。然后,_assert_width
被分配给 _assert_width0
。这里的神奇之处在于,如果一行的宽度与第一行的宽度不匹配,则此行将引发编译器错误,因为数组的类型将不匹配(您可能有例如 [(); 3]
和[(); 4]
)。但是,如果您不知道宏中发生了什么,则错误不是很清楚:
<anon>:38:24: 38:37 error: mismatched types:
expected `[(); 3]`,
found `[(); 4]`
(expected an array with a fixed size of 3 elements,
found one with 4 elements) [E0308]
<anon>:38 _assert_width0 = _assert_width;
^~~~~~~~~~~~~
<anon>:47:13: 47:44 note: in this expansion of mat! (defined in <anon>)
<anon>:38:24: 38:37 help: see the detailed explanation for E0308
我正在尝试实现一个宏以允许创建类似 MATLAB 的矩阵。我有一个基本的工作宏,但我还有很长的路要走。
我希望能够执行正确的结构(每行中的元素数量相同),但我不确定如何在宏中执行此操作。我想我想强制每个内部重复具有相同的长度 - 这是我能做的吗?
到目前为止,这是我的代码:
pub struct Matrix<T> {
pub cols: usize,
pub rows: usize,
pub data: Vec<T>
}
macro_rules! mat {
( $($( $x:expr ),*);* ) => {
{
let mut vec = Vec::new();
let mut rows = 0;
$(
$(
vec.push($x);
)*
rows += 1;
)*
Matrix { cols : vec.len()/rows, rows: rows, data: vec}
}
};
}
It works 但如您所见,这并不是很安全。对结构没有限制。
我想用这个宏做更多的事情,但我认为这是一个好的开始!
更新:
这里是 some playground code 我设计的一个蹩脚的实现。如果有人有更好的建议请告诉我!不然我自己关了
首先,要快速解决问题的标题:请参阅 Counting chapter in The Little Book of Rust Macros。总结一下:没有直接的方法,你需要编写一个宏来扩展到你可以用常规代码计算的东西。
现在,来解决你的实际问题:天哪。
这不是你想要的计数,如果sub-sequences有不同的长度,它会在编译时失败。
首先,没有clean 方法可以从宏触发编译失败。您可以触发一些 other pre-existing 错误,但您无法控制实际的错误消息。
其次,在宏中进行"variable"比较根本就没有简单的方法。您可以有时 与固定的标记序列进行比较,但您不会在这里这样做。
所以它是两倍 not-really-doable。
最简单的做法是在运行时检查构造期间的长度,如果不匹配,return 会出现错误或恐慌。
实际上不可能吗?我不这么认为。如果您愿意接受难以理解的错误消息和 大量 的复杂性跳跃,您可以检查两个标记序列之间的长度是否相等,如下所示:
macro_rules! tts_equal_len {
(($_lhs:tt $($lhs_tail:tt)*), ($_rhs:tt $($rhs_tail:tt)*)) => {
tts_equal_len!(($($lhs_tail)*), ($($rhs_tail)*))
};
(($($_lhs_tail:tt)+), ()) => { do_something_bad!() };
((), ($($_rhs_tail:tt)+)) => { do_something_bad!() };
((), ()) => { do_something_good!() };
}
macro_rules! do_something_bad { () => { { println!("kaboom!") } } }
macro_rules! do_something_good { () => { { println!("no kaboom!") } } }
fn main() {
tts_equal_len!((,,,), (,,,));
tts_equal_len!((,,,), (,,));
tts_equal_len!((,), (,,));
}
同样,真正的问题是找到一些在编译时失败的方法,以便用户理解为什么编译失败。
macro_rules! count {
() => (0usize);
( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));
}
macro_rules! mat {
( $( $x:expr ),* ) => { {
let vec = vec![$($x),*];
Matrix { cols : vec.len(), rows: 1, data: vec }
} };
( $( $x0:expr ),* ; $($( $x:expr ),*);* ) => { {
let mut _assert_width0 = [(); count!($($x0)*)];
let mut vec = Vec::new();
let rows = 1usize;
let cols = count!($($x0)*);
$( vec.push($x0); )*
$(
let rows = rows + 1usize;
let _assert_width = [(); count!($($x)*)];
_assert_width0 = _assert_width;
$( vec.push($x); )*
)*
Matrix { cols : cols, rows: rows, data: vec }
} }
}
count!
宏扩展为常量表达式,表示它作为输入获得的参数数量。它只是 mat!
宏的帮手。如果你需要计算很多项而编译器无法处理它,请参阅 the Counting chapter in The Little Book of Rust Macros,其中有更复杂的计数宏。
我的宏版本使用虚拟变量和赋值来验证所有行的宽度是否相同。首先,我更改了宏的模式以将第一行与后续行分开处理。第一个变量 _assert_width0
用单位数组(()
初始化,这使得数组不占用内存),数组的大小是第一行中的项目数。然后,_assert_width
也用一个单位数组初始化,数组的大小是每个后续行中的项目数。然后,_assert_width
被分配给 _assert_width0
。这里的神奇之处在于,如果一行的宽度与第一行的宽度不匹配,则此行将引发编译器错误,因为数组的类型将不匹配(您可能有例如 [(); 3]
和[(); 4]
)。但是,如果您不知道宏中发生了什么,则错误不是很清楚:
<anon>:38:24: 38:37 error: mismatched types:
expected `[(); 3]`,
found `[(); 4]`
(expected an array with a fixed size of 3 elements,
found one with 4 elements) [E0308]
<anon>:38 _assert_width0 = _assert_width;
^~~~~~~~~~~~~
<anon>:47:13: 47:44 note: in this expansion of mat! (defined in <anon>)
<anon>:38:24: 38:37 help: see the detailed explanation for E0308