2016-05-30 5 views
1

Ich bin neu in Rust und habe noch nicht alles verstanden. Ich habe viel Rust gelesen und spiele auch mit Mathe-Problemen. Ich schrieb eine Funktion mit dieser Signatur:.next() after for ... .zip(): Verhindere, dass der Iterator verschoben wird

pub fn prime_factors(n: u64) -> Vec<u64> { 
So

, da n von 30, sollte es einen Vektor mit den Werten 2 zurückkehren, 3 und 5. ich einen Test gebaut, der lautet:

#[test] 
fn prime_factors_test() { 
    assert_list_eq([2,3,5].iter(), prime_factors(30).iter()); 
} 

Ein Highlight ist, dass ich ein statisches Array mit einem Vektor vergleiche (zu Lernzwecken ist dies derzeit wünschenswert, weil ich gerne Generika praktizieren möchte).

Meine Testfunktion ist die, mit der ich eigentlich Probleme habe. Zum Testen muss die Funktion beide Auflistungen durchlaufen und auf Gleichheit mit jedem Index prüfen. Ich habe .zip() verwendet, um dies zu tun, aber wenn die Sammlungen ungleich sind, wird der Iterator nur durch den Index der kürzeren ausströmen. Nach zip möchte ich überprüfen, ob einer der Iteratoren zusätzliche Elemente enthält.

Die folgende lässt sich nicht kompilieren:

fn assert_list_eq<I, T>(mut expected: I, mut actual: I) 
    where I: Iterator<Item=T> + Clone, 
      T: PartialEq + Debug { 
    for (e, a) in expected.zip(actual) { 
     assert_eq!(e, a); 
    } 
    // fail if there are any "leftovers" 
    assert_eq!(None, expected.next()); 
    assert_eq!(None, actual.next()); 
} 

Ich denke, die Schleife (oder vielleicht das Setup für die Schleife) bewegt/die Iteratoren verbraucht, und sie können nicht nach der Schleife verwendet werden. Aber ich wollen auf den Status dieser Iteratoren Post-Schleife überprüfen. Ich denke, ich weiß, was ist falsch, ich weiß nur nicht, wie man es rechts. Ich habe fast sicher ein konzeptionelles Problem.

Als Workaround habe ich das getan, aber ich fühle mich wie ich ein Anti-Muster umarmen mit dem .clone() nur um die Dinge funktionieren zu lassen.

fn assert_list_eq<I, T>(expected: I, actual: I) 
    where I: Iterator<Item=T> + Clone, 
      T: PartialEq + Debug { 
    assert!(expected.clone().count() == actual.clone().count()); // desperation 
    for (e, a) in expected.zip(actual) { 
     assert_eq!(e, a); 
    } 
} 

BTW (Anerkennung XY Problem): Ich sei glücklich, eine eingebaute Behauptung zu lernen zwei geordnete Sammlungen zu vergleichen, aber das ist nicht wirklich, was ich bin verwirrt.

Antwort

3

zip nimmt seine Argumente nach Wert, deshalb sind sie verschoben und Sie können sie nicht danach verwenden.

Es gibt jedoch eine Problemumgehung: Es gibt eine by_ref Methode, die einen veränderbaren Verweis auf den Iterator zurückgibt, und der Trick besteht darin, dass &mut I where I: Iterator auch Iterator implementiert. Sie können somit .by_ref() auf Ihre beiden Iteratoren anwenden, bevor Sie sie auf zip() passieren:

fn assert_list_eq<I, T>(mut expected: I, mut actual: I) 
    where I: Iterator<Item=T> + Clone, 
      T: PartialEq + Debug { 
    for (e, a) in expected.by_ref().zip(actual.by_ref()) { 
     assert_eq!(e, a); 
    } 
    // fail if there are any "leftovers" 
    assert_eq!(None, expected.next()); 
    assert_eq!(None, actual.next()); 
} 

Das heißt, die Standard-Bibliothek bereits alle notwendigen Werkzeuge bietet mit einem Vec eine feste Größe Array zu vergleichen. Es ist jedoch ein wenig schwierig, weil PartialEq ist generisch auf seiner rechten Seite Argument und es gibt nicht genug Implementierungen für es die "offensichtliche" Art und Weise arbeiten. Hier ist eine Möglichkeit, es zu tun:

#[test] 
fn prime_factors_test() { 
    assert_eq!([2,3,5][..], prime_factors(30)[..]); 
} 

[..] zwingt die Ausdrücke in Scheiben umgewandelt werden, unter Verwendung des Index<RangeFull> Merkmale (.. ist eine Abkürzung für RangeFull).

Eine andere Möglichkeit ist es, nur die Operanden zu tauschen, dann brauchen Sie nicht [..].

#[test] 
fn prime_factors_test() { 
    assert_eq!(prime_factors(30), [2,3,5]); 
} 

Das ist, weil die Standard-Bibliothek impl PartialEq<[T; 3]> for Vec<T> bietet, aber nicht impl PartialEq<Vec<T>> for [T; 3].

+0

Wenn Sie Iteratoren anstelle von Slices vergleichen möchten, gibt es auch ['Iterator :: eq'] (http://doc.rust-lang.org/std/iter/trait.Iterator.html#method. eq) in der Standardbibliothek und ['assert_equal'] (http://bluss.github.io/rust-itertools/doc/itertools/fn.assert_equal.html) in Itertools. – Shepmaster

Verwandte Themen