# Mau
一个用于 Rust 的过程宏库,提供内存化(memoization)功能和 MauQueue 优化。
## 概述
`#[memo]` 宏是 Mau 库的核心功能,它能够自动为函数添加内存化(memoization)缓存,避免重复计算,显著提升性能。这个宏特别适用于递归函数和动态规划问题。
## 为什么要使用 `#[memo]`
### 解决重复计算问题
在递归算法和动态规划中,经常会出现**重复计算相同子问题**的情况。以斐波那契数列为例:
```rust
// 不使用 memo 的版本
fn fibonacci_naive(n: u64) -> u64 {
match n {
0 | 1 => n,
_ => fibonacci_naive(n - 1) + fibonacci_naive(n - 2),
}
}
// 计算 fibonacci(5) 时:
// fibonacci(5) 调用 fibonacci(4) 和 fibonacci(3)
// fibonacci(4) 调用 fibonacci(3) 和 fibonacci(2)
// fibonacci(3) 被计算了 2 次!
// fibonacci(2) 被计算了 3 次!
// fibonacci(1) 被计算了 5 次!
```
**问题**: 随着 n 增大,重复计算呈指数级增长,导致性能急剧下降。
**解决方案**: 使用 `#[memo]` 宏自动缓存计算结果,避免重复计算。
### 性能提升的巨大价值
| n=20 | 0.1ms | 0.01ms | 10倍 |
| n=30 | 10ms | 0.01ms | 1000倍 |
| n=40 | 1000ms | 0.01ms | 100000倍 |
| n=50 | 超时(>60s) | 0.01ms | >6000000倍 |
## 功能特性
- **自动去重**: 相同参数的函数调用只会计算一次
- **线程安全**: 内置线程安全机制,支持多线程环境
- **类型安全**: 完全保持 Rust 的类型系统特性
- **内存高效**: 智能的缓存策略,避免内存泄漏
- **零配置**: 只需要在函数前添加宏标记即可
- **MauQueue 优化**: 通过 MauQueue 将复杂的循环逻辑转换为高效的代码
- **范围宏**: 提供 `min!`, `max!`, `sum!`, `and!`, `or!` 等高效的范围操作宏
## 安装
在 `Cargo.toml` 中添加:
```toml
[dependencies]
mau = "0.1.0"
```
## 核心工作原理
### 1. 自动缓存管理
- 宏会自动创建一个线程安全的哈希表来存储函数调用的结果
- 使用函数参数作为键,函数返回值作为值
- 支持任意实现了 `Hash` 和 `Eq` trait 的参数类型
### 2. 透明化处理
- 对原函数进行包装,在调用时先检查缓存
- 如果缓存中存在结果,直接返回缓存值
- 如果不存在,执行原函数并将结果存入缓存
### 3. 零运行时开销
- 在编译时生成优化代码
- 运行时性能损失最小
- 完全保持 Rust 的类型系统特性
## 详细使用方法
### 1. 基础使用步骤
#### 步骤1: 添加依赖
在 `Cargo.toml` 中添加:
```toml
[dependencies]
mau = "0.1.0"
```
#### 步骤2: 导入宏
```rust
// 导入 memo 宏
use mau::memo;
// 导入范围宏
use mau::{min, max, sum, and, or};
```
#### 步骤3: 在函数前添加宏标记
```rust
#[memo]
fn your_function(param1: Type1, param2: Type2) -> ReturnType {
// 你的函数逻辑
}
```
### 2. 参数类型要求
函数参数必须实现 `Hash` 和 `Eq` trait:
```rust
// ✅ 支持的类型
#[memo]
fn func1(n: i32) -> i32 { n * 2 }
#[memo]
fn func2(s: String) -> usize { s.len() }
#[memo]
fn func3(v: Vec<i32>) -> i32 { v.iter().sum() }
#[memo]
fn func4(tuple: (i32, String)) -> String { tuple.1 }
// ❌ 不支持的类型(未实现 Hash)
#[memo]
fn func5(f: f64) -> f64 { f * 2.0 } // 编译错误!
```
### 3. 引用参数的处理
对于引用参数,需要特别注意生命周期:
```rust
#[memo]
fn process_string(s: &str) -> usize {
s.len()
}
// 或者使用 String 类型
#[memo]
fn process_string_owned(s: String) -> usize {
s.len()
}
```
### 4. 复杂参数类型示例
```rust
use std::collections::HashMap;
#[memo]
fn complex_calculation(
nums: Vec<i32>,
target: i32,
memo_map: HashMap<String, i32>
) -> i32 {
// 复杂的计算逻辑
nums.iter().sum::<i32>() + target
}
#[memo]
fn tuple_params(
pos: (usize, usize),
state: (bool, bool, bool),
data: Vec<Vec<i32>>
) -> i32 {
// 使用元组作为参数
pos.0 + pos.1
}
```
### 5. 错误处理
```rust
#[memo]
fn safe_division(a: i32, b: i32) -> Result<i32, String> {
match b {
0 => Err("Division by zero".to_string()),
_ => Ok(a / b),
}
}
```
### 6. 多返回值处理
```rust
#[memo]
fn multiple_returns(n: i32) -> (i32, i32, i32) {
(n, n * 2, n * 3)
}
```
### 7. 泛型函数使用
```rust
#[memo]
fn generic_function<T: Hash + Eq + Clone>(value: T) -> T {
value.clone()
}
```
## 范围宏 (Range Macros)
Mau 库提供了一系列高效的范围操作宏,用于在指定范围内进行常见的聚合操作。
### 可用宏
| `min!` | 找最小值 | `min!(|i| expr, [start..end])` | `min!(|i| arr[i], [0..arr.len()])` |
| `max!` | 找最大值 | `max!(|i| expr, [start..end])` | `max!(|i| arr[i], [0..arr.len()])` |
| `sum!` | 求和 | `sum!(|i| expr, [start..end])` | `sum!(|i| arr[i], [0..arr.len()])` |
| `and!` | 逻辑与 | `and!(|i| expr, [start..end])` | `and!(|i| bools[i], [0..bools.len()])` |
| `or!` | 逻辑或 | `or!(|i| expr, [start..end])` | `or!(|i| bools[i], [0..bools.len()])` |
### 基本用法
```rust
use mau::{min, max, sum, and, or};
fn main() {
let numbers = vec![3, 1, 4, 1, 5, 9, 2, 6];
// 找最小值
let min_val = min!(|i| numbers[i], [0..numbers.len()]);
println!("最小值: {}", min_val); // 输出: 1
// 找最大值
let max_val = max!(|i| numbers[i], [0..numbers.len()]);
println!("最大值: {}", max_val); // 输出: 9
// 求和
let sum_val = sum!(|i| numbers[i], [0..numbers.len()]);
println!("总和: {}", sum_val); // 输出: 31
// 布尔运算
let bools = vec![true, true, false, true];
let and_result = and!(|i| bools[i], [0..bools.len()]);
let or_result = or!(|i| bools[i], [0..bools.len()]);
println!("逻辑与: {}", and_result); // 输出: false
println!("逻辑或: {}", or_result); // 输出: true
}
```
### 部分范围操作
```rust
use mau::{min, max, sum};
fn main() {
let data = vec![10, 5, 8, 3, 7, 2, 9];
// 只处理索引 2 到 5 的元素
let partial_min = min!(|i| data[i], [2..5]);
let partial_max = max!(|i| data[i], [2..5]);
let partial_sum = sum!(|i| data[i], [2..5]);
println!("部分范围 [2..5]: {:?}", &data[2..5]); // [8, 3, 7]
println!("部分最小值: {}", partial_min); // 3
println!("部分最大值: {}", partial_max); // 8
println!("部分总和: {}", partial_sum); // 18
}
```
### 复杂表达式
```rust
use mau::{min, max, sum};
fn main() {
let data = vec![1, 2, 3, 4, 5];
// 平方后找最小值
let min_squared = min!(|i| data[i] * data[i], [0..data.len()]);
println!("平方后的最小值: {}", min_squared); // 1
// 乘以2后找最大值
let max_doubled = max!(|i| data[i] * 2, [0..data.len()]);
println!("乘以2后的最大值: {}", max_doubled); // 10
// 加1后求和
let sum_plus_one = sum!(|i| data[i] + 1, [0..data.len()]);
println!("加1后的总和: {}", sum_plus_one); // 20
}
```
### 浮点数支持
```rust
use mau::{min, max, sum};
fn main() {
let floats = vec![3.5, 1.2, 4.8, 1.1, 5.9, 2.3];
let min_float = min!(|i| floats[i], [0..floats.len()]);
let max_float = max!(|i| floats[i], [0..floats.len()]);
let sum_float = sum!(|i| floats[i], [0..floats.len()]);
println!("浮点最小值: {}", min_float); // 1.1
println!("浮点最大值: {}", max_float); // 5.9
println!("浮点总和: {}", sum_float); // 18.8
}
```
### 字符串操作
```rust
use mau::{min, max, sum};
fn main() {
let words = vec!["apple", "banana", "cherry", "date"];
let min_length = min!(|i: usize| words[i].len(), [0..words.len()]);
let max_length = max!(|i: usize| words[i].len(), [0..words.len()]);
let total_length = sum!(|i: usize| words[i].len(), [0..words.len()]);
println!("最短长度: {}", min_length); // 4
println!("最长长度: {}", max_length); // 6
println!("总长度: {}", total_length); // 21
}
```
### 惰性计算特性
#### 短路优化
`and!` 和 `or!` 宏具有短路优化特性:
```rust
use mau::{and, or};
fn expensive_calculation(value: bool) -> bool {
println!("计算 expensive_calculation({})", value);
// 模拟昂贵的计算
value
}
fn main() {
let data = vec![true, true, false, true, true];
// and! 会在遇到第一个 false 时停止计算
let result = and!(|i| expensive_calculation(data[i]), [0..data.len()]);
// 输出:
// 计算 expensive_calculation(true)
// 计算 expensive_calculation(true)
// 计算 expensive_calculation(false)
// 然后停止,不再计算后续元素
let data2 = vec![false, false, true, false, true];
// or! 会在遇到第一个 true 时停止计算
let result2 = or!(|i| expensive_calculation(data2[i]), [0..data2.len()]);
// 输出:
// 计算 expensive_calculation(false)
// 计算 expensive_calculation(false)
// 计算 expensive_calculation(true)
// 然后停止,不再计算后续元素
}
```
#### 单次计算
所有宏都确保每个元素只计算一次:
```rust
use mau::sum;
fn expensive_calculation(value: i32) -> i32 {
println!("计算 expensive_calculation({})", value);
value * value + 1
}
fn main() {
let data = vec![1, 2, 3, 4, 5];
let result = sum!(|i| expensive_calculation(data[i]), [0..data.len()]);
// 每个元素只会被计算一次,不会重复计算
}
```
### 性能优势
- **零分配**: 宏在编译时展开,无运行时开销
- **类型推断**: 自动推断返回类型,支持泛型
- **短路优化**: `and!` 和 `or!` 宏具有短路特性
- **单次计算**: 每个元素只计算一次,避免重复计算
- **范围支持**: 支持部分范围操作,提高灵活性
## 使用示例
### 基础内存化
```rust
use mau::memo;
#[memo]
fn fibonacci(n: u64) -> u64 {
match n {
0 | 1 => n,
_ => fibonacci(n - 1) + fibonacci(n - 2),
}
}
fn main() {
println!("{}", fibonacci(40)); // 高效计算,结果会被缓存
}
```
### 复杂参数类型
```rust
use mau::memo;
#[memo]
fn complex_calculation(
nums: &Vec<i32>,
start: usize,
end: usize,
target: i32
) -> i32 {
if start >= end {
return 0;
}
let mut result = 0;
for i in start..end {
if nums[i] == target {
result += 1;
}
}
result + complex_calculation(nums, start + 1, end, target)
}
```
### MauQueue 优化
```rust
use mau::memo;
#[memo]
fn optimized_calculation(nums: &Vec<i32>, n: usize) -> i32 {
match n {
0 => return 0,
_ => {
let start = n.saturating_sub(4);
let end = n.saturating_sub(1);
// MauQueue 会被转换为高效的循环代码
let result = MauQueue(
move || start,
move || end,
|i| {
let current_value = nums[i];
let prev_max = optimized_calculation(nums, i);
std::cmp::max(prev_max, prev_max + current_value)
}
);
result
}
}
}
```
## 实际应用示例
### 1. 守望者的逃离 (P1095) - 递归优化
```rust
use mau::memo;
#[memo]
fn max_distance(magic: i32, time_left: i32, distance_left: i32) -> i32 {
match (time_left <= 0, distance_left <= 0) {
(true, true) => 0,
(true, false) => -distance_left,
(false, true) => 0, // 已经到达
(false, false) => {
let mut max_dist = -distance_left;
// 选择1: 跑步 (17m/s)
let run_result = match distance_left <= 17 {
true => 0,
false => max_distance(magic, time_left - 1, distance_left - 17),
};
max_dist = max_dist.max(run_result);
// 选择2: 闪烁 (60m/s, 消耗10魔法)
let blink_result = match (magic >= 10, distance_left <= 60) {
(true, true) => 0,
(true, false) => max_distance(magic - 10, time_left - 1, distance_left - 60),
(false, _) => i32::MIN,
};
if blink_result != i32::MIN {
max_dist = max_dist.max(blink_result);
}
// 选择3: 休息 (恢复4魔法)
let rest_result = max_distance((magic + 4).min(1000), time_left - 1, distance_left);
max_dist = max_dist.max(rest_result);
max_dist
}
}
}
```
### 2. 动态规划 - 最长公共子序列
```rust
use mau::memo;
#[memo]
fn lcs(s1: &str, s2: &str, i: usize, j: usize) -> usize {
match (i == 0, j == 0) {
(true, _) | (_, true) => 0,
(false, false) => {
if s1.chars().nth(i - 1) == s2.chars().nth(j - 1) {
1 + lcs(s1, s2, i - 1, j - 1)
} else {
lcs(s1, s2, i - 1, j).max(lcs(s1, s2, i, j - 1))
}
}
}
}
```
### 3. 图算法 - 最短路径
```rust
use mau::memo;
use std::collections::HashMap;
#[memo]
fn shortest_path(
graph: &HashMap<usize, Vec<(usize, i32)>>,
start: usize,
end: usize,
visited: Vec<usize>
) -> Option<i32> {
match start == end {
true => Some(0),
false => {
let mut min_dist = None;
if let Some(neighbors) = graph.get(&start) {
for &(next, weight) in neighbors {
if !visited.contains(&next) {
let mut new_visited = visited.clone();
new_visited.push(next);
if let Some(dist) = shortest_path(graph, next, end, new_visited) {
let total_dist = weight + dist;
min_dist = match min_dist {
Some(current_min) => Some(current_min.min(total_dist)),
None => Some(total_dist),
};
}
}
}
}
min_dist
}
}
}
```
## 性能对比
### 详细对比分析
| **时间复杂度** | 指数级 O(2^n) | 线性 O(n) |
| **空间复杂度** | O(n) 递归栈 | O(n) 缓存空间 |
| **重复计算** | 大量重复计算 | 避免重复计算 |
| **内存使用** | 递归栈消耗 | 缓存表消耗 |
| **性能表现** | 随输入增长急剧下降 | 线性增长 |
| **适用场景** | 小规模问题 | 大规模问题 |
### 实际测试数据
以斐波那契数列为例:
| 20 | 0.1 | 0.01 | 10x |
| 30 | 10 | 0.01 | 1000x |
| 40 | 1000 | 0.01 | 100000x |
| 50 | 超时 (>60s) | 0.01 | >6000000x |
### 递归调用树对比
#### 不使用 memo 的调用树 (fibonacci(5))
```
fibonacci(5)
├── fibonacci(4)
│ ├── fibonacci(3)
│ │ ├── fibonacci(2)
│ │ │ ├── fibonacci(1) = 1
│ │ │ └── fibonacci(0) = 0
│ │ └── fibonacci(1) = 1
│ └── fibonacci(2)
│ ├── fibonacci(1) = 1
│ └── fibonacci(0) = 0
└── fibonacci(3)
├── fibonacci(2)
│ ├── fibonacci(1) = 1
│ └── fibonacci(0) = 0
└── fibonacci(1) = 1
总调用次数: 15次
重复计算: fibonacci(3)计算2次, fibonacci(2)计算3次, fibonacci(1)计算5次
```
#### 使用 memo 的调用树 (fibonacci(5))
```
fibonacci(5)
├── fibonacci(4) [缓存]
│ ├── fibonacci(3) [缓存]
│ │ ├── fibonacci(2) [缓存]
│ │ │ ├── fibonacci(1) = 1 [缓存]
│ │ │ └── fibonacci(0) = 0 [缓存]
│ │ └── fibonacci(1) = 1 [从缓存获取]
│ └── fibonacci(2) = 1 [从缓存获取]
└── fibonacci(3) = 2 [从缓存获取]
总调用次数: 6次
重复计算: 0次
```
## 工作原理
### 内存化机制
`#[memo]` 宏会:
1. 创建一个线程安全的哈希表缓存
2. 生成一个无缓存版本的函数
3. 在原函数中添加缓存查找和存储逻辑
### MauQueue 转换
`MauQueue(start_fn, end_fn, optimize_fn)` 会被转换为:
```rust
{
let mut max = 0;
let start = start_fn();
let end = end_fn();
for i in start..=end {
max = optimize_fn(i);
}
max
}
```
## 使用注意事项
### 内存管理
```rust
// 注意:缓存会持续占用内存直到程序结束
#[memo]
fn expensive_calculation(n: i32) -> i32 {
// 如果 n 的范围很大,缓存可能会占用大量内存
n * n * n
}
```
### 副作用函数
```rust
// ❌ 避免在 memo 函数中使用副作用
#[memo]
fn bad_example(n: i32) -> i32 {
println!("计算中..."); // 副作用:打印语句
n * 2
}
// ✅ 纯函数更适合 memo
#[memo]
fn good_example(n: i32) -> i32 {
n * 2
}
```
### 参数设计
```rust
// ❌ 避免使用会频繁变化的无意义参数
#[memo]
fn bad_design(n: i32, timestamp: u64) -> i32 {
n * 2 // timestamp 参数会导致缓存失效
}
// ✅ 只包含影响结果的参数
#[memo]
fn good_design(n: i32) -> i32 {
n * 2
}
```
## 使用建议
### 适用场景
1. **递归函数**: 特别是存在重复子问题的递归
2. **动态规划**: 需要缓存中间结果的DP问题
3. **状态转移**: 复杂状态空间的搜索问题
4. **数学计算**: 需要重复计算的数学函数
### 最佳实践
1. **函数设计**: 尽量设计纯函数,避免副作用
2. **参数优化**: 减少不必要的参数,提高缓存命中率
3. **内存监控**: 监控缓存大小,避免内存溢出
4. **性能测试**: 对比使用前后的性能差异
## 完整示例:范围宏与内存化的结合使用
```rust
use mau::{memo, min, max, sum, and, or};
// 使用 memo 宏优化递归函数
#[memo]
fn fibonacci(n: u64) -> u64 {
match n {
0 | 1 => n,
_ => fibonacci(n - 1) + fibonacci(n - 2),
}
}
// 使用范围宏进行高效的数据处理
fn analyze_data(data: &[i32]) -> (i32, i32, i32, bool, bool) {
let min_val = min!(|i| data[i], [0..data.len()]);
let max_val = max!(|i| data[i], [0..data.len()]);
let sum_val = sum!(|i| data[i], [0..data.len()]);
// 检查是否所有值都大于0
let all_positive = and!(|i| data[i] > 0, [0..data.len()]);
// 检查是否有任何值等于0
let has_zero = or!(|i| data[i] == 0, [0..data.len()]);
(min_val, max_val, sum_val, all_positive, has_zero)
}
fn main() {
// 使用 memo 宏
println!("Fibonacci(40) = {}", fibonacci(40));
// 使用范围宏
let numbers = vec![3, 1, 4, 1, 5, 9, 2, 6];
let (min_val, max_val, sum_val, all_positive, has_zero) = analyze_data(&numbers);
println!("数据: {:?}", numbers);
println!("最小值: {}", min_val);
println!("最大值: {}", max_val);
println!("总和: {}", sum_val);
println!("全部为正数: {}", all_positive);
println!("包含零: {}", has_zero);
// 部分范围操作
let partial_min = min!(|i| numbers[i], [2..6]);
let partial_sum = sum!(|i| numbers[i], [2..6]);
println!("部分范围 [2..6] 最小值: {}", partial_min);
println!("部分范围 [2..6] 总和: {}", partial_sum);
}
```
## 许可证
本项目采用 MIT 或 Apache-2.0 双许可证。