10 个版本 (破坏性)
| 0.8.0 | 2024 年 5 月 17 日 |
|---|---|
| 0.7.0 | 2024 年 4 月 24 日 |
| 0.6.0 | 2024 年 4 月 18 日 |
| 0.5.0 | 2024 年 4 月 12 日 |
| 0.1.2 | 2024 年 3 月 21 日 |
#963 在 算法
每月 42 次下载
34KB
492 行
rand-functors
rand-functors 提供了对不同方式评估随机过程的抽象,这些随机过程表示为确定性和随机数据的函数。这是通过结合基于类型的策略模式版本和函数式编程的函子模式实现的。
这个 crate 的一个动机问题是在这两个建模相同随机过程的功能之间存在的代码重复
use rand::prelude::*;
fn next_state(mut state: u8) -> u8 {
state = state.wrapping_add(random());
if random() {
state %= 3;
}
state
}
fn next_states(state: u8) -> Vec<u8> {
let mut out: Vec<_> = (0..=255).map(|r| state.wrapping_add(r)).collect();
out.append(&mut out.iter().copied().map(|i| i % 3).collect());
out
}
虽然这些功能可能看起来不同,但相同的随机过程都嵌入在其中。将一个随机的 u8 添加到 state,然后如果随机的 bool 是 true,则将状态设置为自身模 3。
这种随机过程的冗余实现可能在重构期间引起问题。如果决定将 %= 3 在 next_state 中改为 %= 5,他或她需要在对 next_states 的相应更新。
使用 rand-functors,这两个函数可以合并为
use rand::prelude::*;
use rand_functors::{Functor, RandomStrategy};
fn next_state<S: RandomStrategy>(state: u8) -> S::Functor<u8> {
let mut out = S::fmap_rand(Functor::pure(state), &mut thread_rng(), |s, r| {
s.wrapping_add(r)
});
out = S::fmap_rand(out, &mut thread_rng(), |s, r| if r { s % 3 } else { s });
out
}
这种新的实现使 next_state 在一个 RandomStrategy S 上变得泛型。它的返回类型也改为与 S 相关的 Functor。在内部,state 从 u8 转换为 S::Functor<u8>。函数的其他部分基本上与原始的 next_state 相同,但现在每个操作都会将随机样本包装在调用 S::fmap_rand 中。调用 next_state::<Sampler>(s) 相当于之前调用 next_state(s)。同样,可以调用 next_state::<Enumerator>(s) 而不是使用 next_states(s),这需要维护相同的核心过程的单独实现。
目前,rand-functors 只支持类型为 bool 或占用不超过 16 位的数字类型的随机变量。但是,可以实现自定义数据类型所需的所有必要特征。
依赖项
~0.3–0.8MB
~14K SLoC