我正在尝试将一些基准测试从 F# 移植到 Rust。 F# 代码如下所示:
let inline iterNeighbors f (i, j) =
f (i-1, j)
f (i+1, j)
f (i, j-1)
f (i, j+1)
let rec nthLoop n (s1: HashSet<_>) (s2: HashSet<_>) =
match n with
| 0 -> s1
| n ->
let s0 = HashSet(HashIdentity.Structural)
let add p =
if not(s1.Contains p || s2.Contains p) then
ignore(s0.Add p)
Seq.iter (fun p -> iterNeighbors add p) s1
nthLoop (n-1) s0 s1
let nth n p =
nthLoop n (HashSet([p], HashIdentity.Structural)) (HashSet(HashIdentity.Structural))
(nth 2000 (0, 0)).Count
它从潜在无限图中的初始顶点计算第 n 个最近邻壳。我在攻读博士学位期间使用类似的东西来研究非晶材料。
我花了很多时间尝试将其移植到 Rust,但失败了。我已经设法让一个版本工作,但只能通过手动内联闭包并将递归转换为具有本地可变变量的循环(yuk!)。
我尝试写iterNeighbors
像这样的函数:
use std::collections::HashSet;
fn iterNeighbors<F>(f: &F, (i, j): (i32, i32)) -> ()
where
F: Fn((i32, i32)) -> (),
{
f((i - 1, j));
f((i + 1, j));
f((i, j - 1));
f((i, j + 1));
}
我认为这是一个接受闭包(本身接受一对并返回单位)和一对并返回单位的函数。我似乎必须把事情加双括号:这是正确的吗?
我尝试编写这样的递归版本:
fn nthLoop(n: i32, s1: HashSet<(i32, i32)>, s2: HashSet<(i32, i32)>) -> HashSet<(i32, i32)> {
if n == 0 {
return &s1;
} else {
let mut s0 = HashSet::new();
for &p in s1 {
if !(s1.contains(&p) || s2.contains(&p)) {
s0.insert(p);
}
}
return &nthLoop(n - 1, s0, s1);
}
}
请注意,我什至没有费心去打电话iterNeighbors
yet.
我认为我正在努力使参数的生命周期正确,因为它们在递归调用中进行轮换。如果我愿意,我应该如何注释生命周期s2
在之前被释放return
我想要s1
在返回时或进入递归调用时生存?
调用者看起来像这样:
fn nth<'a>(n: i32, p: (i32, i32)) -> &'a HashSet<(i32, i32)> {
let s0 = HashSet::new();
let mut s1 = HashSet::new();
s1.insert(p);
return &nthLoop(n, &s1, s0);
}
我放弃了,并将其写为while
改为使用可变局部变量循环:
fn nth<'a>(n: i32, p: (i32, i32)) -> HashSet<(i32, i32)> {
let mut n = n;
let mut s0 = HashSet::new();
let mut s1 = HashSet::new();
let mut s2 = HashSet::new();
s1.insert(p);
while n > 0 {
for &p in &s1 {
let add = &|p| {
if !(s1.contains(&p) || s2.contains(&p)) {
s0.insert(p);
}
};
iterNeighbors(&add, p);
}
std::mem::swap(&mut s0, &mut s1);
std::mem::swap(&mut s0, &mut s2);
s0.clear();
n -= 1;
}
return s1;
}
如果我手动内联闭包,这是可行的,但我不知道如何调用闭包。理想情况下,我希望在这里进行静态调度。
The main
那么函数是:
fn main() {
let s = nth(2000, (0, 0));
println!("{}", s.len());
}
那么...我做错了什么? :-)
另外,我只用过HashSet
在 F# 中,因为我认为 Rust 不提供纯函数式Set
具有高效的集合论运算(并集、交集和差集)。我的假设正确吗?