Sto provando a portare un piccolo benchmark da F # a Rust. Il codice F # è simile al seguente:Lottando con chiusure e vite in Rust
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
calcola gli ennesimi-più vicine conchiglie vicino di un vertice iniziale in un grafico potenzialmente infinita. Ho usato qualcosa di simile durante il mio dottorato di ricerca per studiare materiali amorfi.
Ho passato molte ore a provare e non riuscire a portare questo a Rust. Sono riuscito a far funzionare una versione, ma solo inserendo manualmente la chiusura e convertendo la ricorsione in un loop con mutabili locali (yuk!).
use std::collections::HashSet;
Ho provato a scrivere la funzione iterNeighbors
simili:
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));
}
Credo che è una funzione che accetta una chiusura (che si accetta una coppia e ritorna unità) ed una coppia e ritorna unità. Mi sembra di dover raddoppiare le cose: è corretto?
ho provato a scrivere una versione ricorsiva come questo:
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);
}
}
Si noti che non ho nemmeno preso la briga con la chiamata a iterNeighbors
ancora.
Penso di dover fare in modo che le durate degli argomenti siano corrette perché vengono ruotate nella chiamata ricorsiva. Come dovrei annotare le durate se desidero che los sia deallocato immediatamente prima dello return
e voglio che sopravviva quando viene restituito o nella chiamata ricorsiva?
Il chiamante sarebbe simile a questa:
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);
}
ho rinunciato a questo e ha scritto come un ciclo while
con i locali mutevoli invece:
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;
}
Questo funziona se INLINE la chiusura a mano ma non riesco a capire come invocare la chiusura. Idealmente, mi piacerebbe la spedizione statica qui.
La funzione main
è quindi:
fn main() {
let s = nth(2000, (0, 0));
println!("{}", s.len());
}
Allora ... cosa sto sbagliando? :-)
Inoltre, ho utilizzato solo HashSet
in F # perché presumo che Rust non fornisca un Set
puramente funzionale con operazioni di teorizzazione dell'insieme efficienti (unione, intersezione e differenza). Sono corretto nel presupposto che?
Un raro esempio di utilizzo di 'drop' che in realtà ha un senso. Eccitante! Per OP, sottolineo che avere 'return' come ultima istruzione in un metodo/blocco è Rust non idiomatico; normalmente finisci con un'espressione. Inoltre ruggine usa identificatori 'snake_case', non' camelCase' – Shepmaster
Fantastico, grazie a tutti e due! :-) –
"Tuttavia, HashSet vuol attuare operazioni di insiemistica Ci sono due modi di usarli:. Iteratori (differenza, symmetric_difference, intersezione, unione), che generano la sequenza pigramente, o operatori (|, &, ^, - , come elencato nelle implementazioni dei tratti per HashSet), che producono nuovi set contenenti cloni dei valori dai set sorgente ".Sarà molto lento rispetto ad un 'Set' puramente funzionale. –