【发布时间】:2016-03-23 01:56:30
【问题描述】:
我正在尝试将一个小基准从 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 打电话而烦恼。
我认为我正在努力使参数的生命周期正确,因为它们在递归调用中被轮换。如果我希望 s2 在 returns 之前被释放并且我希望 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;
}
如果我手动内联闭包,这会起作用,但我不知道如何调用闭包。理想情况下,我想在这里进行静态调度。
main 函数是:
fn main() {
let s = nth(2000, (0, 0));
println!("{}", s.len());
}
那么……我做错了什么? :-)
另外,我只在 F# 中使用了HashSet,因为我假设 Rust 没有提供具有高效集合论运算(联合、交集和差异)的纯函数式 Set。我的假设是否正确?
【问题讨论】:
标签: rust