临时措施
本节将留给那些价值有待探讨,以及那些可能有缺陷因而不适合出现在正文中的模式与技巧。
算盘计数
临时信息:需要更合适的例子。虽然它是
Ook!
宏的重要组成部分之一,但该用例——匹配Rust分组机制无法表示的嵌套结构——实在是过于特殊,因此不适作为例子使用。
macro_rules! abacus {
((- $($moves:tt)*) -> (+ $($count:tt)*)) => {
abacus!(($($moves)*) -> ($($count)*))
};
((- $($moves:tt)*) -> ($($count:tt)*)) => {
abacus!(($($moves)*) -> (- $($count)*))
};
((+ $($moves:tt)*) -> (- $($count:tt)*)) => {
abacus!(($($moves)*) -> ($($count)*))
};
((+ $($moves:tt)*) -> ($($count:tt)*)) => {
abacus!(($($moves)*) -> (+ $($count)*))
};
// 检查最终结果是否为零
(() -> ()) => { true };
(() -> ($($count:tt)+)) => { false };
}
fn main() {
let equals_zero = abacus!((++-+-+++--++---++----+) -> ());
assert_eq!(equals_zero, true);
}
当需要记录的计数会发生变化,且初始值为零或在零附近,且必须支持如下操作:
- 增加一;
- 减少一;
- 与0(或任何其它固定有限值)相比较;
时,可以使用次技巧。
数值n将由一组共n个相同的特定标记来表示。对数值的修改操作将采用下推累积模式由递归调用完成。假设所采用的特定标记是x
,则上述操作可实现为:
- 增加一:匹配
($($count:tt)*)
并替换为(x $($count)*)
。 - 减少一:匹配
(x $($count:tt)*)
并替换为($($count)*)
。 - 与0相比较:匹配
()
。 - 与1相比较:匹配
(x)
。 - 与2相比较:匹配
(x x)
。 - (依此类推…)
作用于计数值的操作将所选的标记来回摆动,如同算盘摆动算子。[^abacus]
[^abacus]: 在这句极度单薄的辩解下,隐藏着选用此名称的真实理由:避免造出又一个名含“标记”的术语。今天就该跟你认识的作者谈谈规避语义饱和吧!公平来讲,本来也可以称它为“一元计数(unary counting)”。
在想表示负数的情况下,值-n可被表示成n个相同的其它标记。在上例中,值+n被表示成n个+
标记,而值-m被表示成m个-
标记。
有负数的情况下操作起来稍微复杂一些,增减操作在当前数值为负时实际上互换了角色。给定+
和-
分别作为正数与负数标记,相应操作的实现将变成:
- 增加一:
- 匹配
()
并替换为(+)
- 匹配
(- $($count:tt)*)
并替换为($($count)*)
- 匹配
($($count:tt)+)
并替换为(+ $($count)+)
- 匹配
- 减少一:
- 匹配
()
并替换为(-)
- 匹配
(+ $($count:tt)*)
并替换为($($count)*)
- 匹配
($($count:tt)+)
并替换为(- $($count)+)
- 匹配
- 与0相比较:匹配
()
- 与+1相比较:匹配
(+)
- 与-1相比较:匹配
(-)
- 与+2相比较:匹配
(++)
- 与-2相比较:匹配
(--)
- (依此类推…)
注意在顶部的示例中,某些规则被合并到一起了(举例来说,对()
及($($count:tt)+)
的增加操作被合并为对($($count:tt)*)
的增加操作)。
如果想要提取出所计数目的实际值,可再使用普通的计数宏。对上例来说,终结规则可换为:
macro_rules! abacus {
// ...
// 下列规则将计数替换成实际值的表达式
(() -> ()) => {0};
(() -> (- $($count:tt)*)) => {
{(-1i32) $(- replace_expr!($count 1i32))*}
};
(() -> (+ $($count:tt)*)) => {
{(1i32) $(+ replace_expr!($count 1i32))*}
};
}
macro_rules! replace_expr {
($_t:tt $sub:expr) => {$sub};
}
仅限此例:严格来说,想要达到此例的效果,没必要做的这么复杂。如果你不需要在宏中匹配所计的值,可直接采用重复来更加高效地实现:
macro_rules! abacus {
(-) => {-1};
(+) => {1};
($($moves:tt)*) => {
0 $(+ abacus!($moves))*
}
}