2015-11-28 2 views
11

Ich habe mit Rust von porting my Score4 AI engine bis es gespielt - basierend auf meiner funktionalen Umsetzung in OCaml. Ich wollte speziell sehen, wie Rust mit funktionalem Code arbeitet.Warum benötigt das Argument für den Fundabschluss zwei kaufmännische Und-Zeichen?

Das Endergebnis: Es funktioniert, und es ist sehr schnell - viel schneller als OCaml. Es berührt fast die Geschwindigkeit von imperativem C/C++ - was wirklich cool ist.

Es gibt eine Sache, die mich jedoch beunruhigt - warum brauche ich zwei kaufmännische Und-Zeichen in der letzten Zeile dieses Codes?

let moves_and_scores: Vec<_> = moves_and_boards 
    .iter() 
    .map(|&(column,board)| (column, score_board(&board))) 
    .collect(); 
let target_score = if maximize_or_minimize { 
    ORANGE_WINS 
} else { 
    YELLOW_WINS 
}; 
if let Some(killer_move) = moves_and_scores.iter() 
    .find(|& &(_,score)| score==target_score) { 
     ... 

Ich habe sie hinzugefügt, weil die Compilerfehler mich dazu "geführt" haben; aber ich versuche zu verstehen, warum ... habe ich den Trick an anderer Stelle im Stack-Überlauf erwähnt zu „fragen“ die Compiler, mir zu sagen, welche Art etwas ist:

let moves_and_scores: Vec<_> = moves_and_boards 
    .iter() 
    .map(|&(column,board)| (column, score_board(&board))) 
    .collect(); 
let() = moves_and_scores; 

..., die diesen Fehler verursacht:

src/main.rs:108:9: 108:11 error: mismatched types: 
expected `collections::vec::Vec<(u32, i32)>`, 
    found `()` 
(expected struct `collections::vec::Vec`, 
    found()) [E0308] 
src/main.rs:108  let() = moves_and_scores; 

... wie ich erwartet habe, moves_and_scores ist ein Vektor von Tupeln: Vec<(u32, i32)>. Aber dann, in der unmittelbaren nächsten Zeile, iter() und find() mich zwingen, die abscheulichen Doppel Et-Zeichen im Verschluß Parameter zu verwenden:

if let Some(killer_move) = moves_and_scores.iter() 
    .find(|& &(_,score)| score==target_score) { 

Warum die find Schließung müssen zwei Et-Zeichen? Ich konnte sehen, warum es einen brauchen könnte (das Tupel als Referenz übergeben, um Zeit/Raum zu sparen), aber warum zwei? Ist es wegen der iter? Das heißt, ist das iter Erstellen von Referenzen, und dann erwartet find eine Referenz für jeden Eingang, so dass eine Referenz auf eine Referenz?

Wenn das so ist, ist das wohl kein recht hässlicher Designfehler in Rust?

In der Tat würde ich find und map und alle anderen funktionalen Primitiven als Teile der Sammlungen selbst erwarten. Zwingt mich zu iter(), um jede Art von funktioneller Arbeit zu tun scheint lästig, und noch mehr, wenn es diese Art von "Doppel-Ampersands" in jeder möglichen funktionalen Kette zwingt.

Ich hoffe, dass ich etwas offensichtliches vermisse - jede Hilfe/Aufklärung willkommen.

+0

Herzlichen Glückwunsch zum Verwalten des Ports, schlagen OCaml auf funktionalen Stil Code bedeutet, dass Sie etwas richtig gemacht haben! –

+0

@MatthieuM. Vielen Dank! Ich hatte gehofft, es gibt eine sauberere Art, mit den Funktionsketten umzugehen (zB '.iter(). Map (...). Iter(). Filter() ... .iter(). Find (...)') ohne bei jedem Schritt eine zusätzliche Bezugsebene einzuführen - aber es scheint, ich kann es nicht vermeiden. – ttsiodras

Antwort

13

Das hier

moves_and_scores.iter() 

Sie einen Iterator gibt über Vektorelemente entlehnt. Wenn Sie dem API-Dokument folgen, welcher Typ das ist, werden Sie feststellen, dass es nur der Iterator für ein geliehenes Segment ist und Iterator mit Item=&T implementiert, wobei T in Ihrem Fall (u32, i32) ist.

Dann verwenden Sie find, die ein Prädikat nimmt, das einen &Item als Parameter nimmt. Sice Item ist bereits eine Referenz in Ihrem Fall, das Prädikat muss eine &&(u32, i32) nehmen.

pub trait Iterator { 
    ... 
    fn find<P>(&mut self, predicate: P) -> Option<Self::Item> 
    where P: FnMut(&Self::Item) -> bool {...} 
    ...   ^

Es wurde wahrscheinlich wie this definiert, weil es nur das Einzelteil zu überprüfen und gibt einen Bool angenommen hat.Dies erfordert nicht, dass der Artikel als Wert übergeben wird.

Wenn Sie ein Iterator über (u32, i32) wollen könnten Sie

moves_and_scores.iter().cloned() 

cloned() wandeln den Iterator von einem mit einem Item Typ &T einer mit einem Item Typ schreiben T wenn TClone ist. Eine andere Möglichkeit wäre, into_iter() anstelle von iter() zu verwenden.

moves_and_scores.into_iter() 

Der Unterschied zwischen den beiden ist, dass die erste Option Klone, die die geborgten Elemente während die 2. man den Vektor verbraucht und bewegt die Elemente aus ihm heraus.

von der Lambda-ähnlichen

|&&(_, score)| score == target_score 

Schreiben destrukturiert Sie die „doppelte Referenz“ und eine lokale Kopie der i32 erstellen. Dies ist zulässig, da i32 ein einfacher Typ ist, der Copy ist.

Statt die Parameter Ihres Prädikat Destrukturierung könnten Sie auch

|move_and_score| move_and_score.1 == target_score 

schreiben, weil der Punkt-Operator automatisch so oft wie nötig dereferenziert.

+0

Vielen Dank für Ihr Feedback! Hat 'iter(). Cloned()' einen Leistungseinfluss? Ich meine, teilt es tatsächlich "Klone" zu? Ich glaube auch, dass 'into_iter()' definitiv eine Auswirkung auf die Performance haben wird, da die Semantik in Bewegung den Quellvektor mutieren wird. Ich denke, der beste ist dein dritter Vorschlag - die "automatische Dereferenzierung so oft wie nötig" zu verwenden - muss darüber nachlesen. – ttsiodras

+0

BTW, irgendwelche Gedanken darüber, warum Rust die funktionalen Operatoren (Map, Filter, etc.) nicht als Teil der Sammlungen ausgewählt hat und die '.iter()' first benötigt (was eine zusätzliche Ebene der "Referenz-Indirection" einführt) bei jedem Schritt) ....? – ttsiodras

+1

@ttsiodras Was die Leistung angeht: Nichts davon sollte wirklich wichtig sein. Klonen eines Tupels vom Typ '(u32, i32)' ist sehr billig. Lass dich nicht von dem Wort "Klon" erschrecken. :-) Für simple "einfache alte Datentypen" sind das Klonen und das Kopieren grundsätzlich gleich. Einige Typen können jedoch nicht kopiert werden, da sie komplizierter sind. Nur in diesen Fällen würde ich mich um das Klonen kümmern. – sellibitze

Verwandte Themen