2014-07-17 17 views
7

Esiste un modo naturale in Rust per ripetere il "prodotto" di più intervalli o iteratori?Come eseguire iterazioni sul prodotto di più intervalli o iteratori?

Viene fuori quando si sta ripetendo un array multidimensionale o forse uno spazio di stato. Ad esempio, voglio considerare tutti i valori possibili di una tupla booleana con 5 elementi. L'annidamento dei loop 5 for è un po 'ingombrante.

+1

Penso quasi che mi aspetto che i bool siano un caso speciale, vorrei uno strumento diverso. Ma uno generico sarebbe utile per altre situazioni. In realtà, per i bool, molte persone "eseguono l'iterazione" (conteggio) su un 'std :: bitset'. –

+0

Quello che vuoi sarebbe in Python essere ['itertools.product'] (https://docs.python.org/3/library/itertools.html#itertools.product). Implementare una cosa del genere in Rust, tuttavia, non è così facile come potrebbe sembrare a prima vista. L'implementazione in genere richiederà praticamente macro. –

risposta

7

Ecco una macro che fa il lavoro:

#![feature(macro_rules)] 

macro_rules! product(
    ($first:ident, $($next:ident),*) => (
     $first.iter() $(
      .flat_map(|e| std::iter::Repeat::new(e) 
       .zip($next.iter())) 
     )* 
    ); 
) 

fn main() { 
    let a = ['A', 'B', 'C']; 
    let b = [1i, 4]; 
    let c = [true, false]; 
    let d = ['x', 'y']; 

    for (((a, b), c), d) in product![a, b, c, d] { 
     println!("{} {} {} {}", a, b, c, d); 
    } 
} 

uscita:

A 1 true x 
A 1 true y 
A 1 false x 
A 1 false y 
A 4 true x 
A 4 true y 
etc... 

Playpen example

La macro restituisce il seguente

a.iter() 
    .flat_map(|e| std::iter::Repeat::new(e).zip(b.iter())) 
    .flat_map(|e| std::iter::Repeat::new(e).zip(c.iter())) 
    .flat_map(|e| std::iter::Repeat::new(e).zip(d.iter())) 

flat_map(|e| ...) combina una sequenza di iteratori in un iteratore. Il e è un elemento prodotto da un iteratore.

std::iter::Repeat::new(e) crea un iteratore che si ripete e.

.zip(...) itera su due iteratori contemporaneamente, producendo gli elementi di entrambi in coppia.

macro sono un po 'più lungo da spiegare, quindi è meglio leggere il macro guide

non so se questo è il modo migliore per farlo.

+1

Grazie, ma potresti una breve descrizione di cosa fa? (Non la sintassi, solo la tecnica di base.) Sono estremamente nuovo di ruggine, e voglio imparare piuttosto che copiare/incollare. :) – starwed

+0

@starwed fatto, speriamo che sia più chiaro ora. –

+4

Vale la pena notare che questo ricrea l'iteratore 'b' per ogni elemento di' a', e il 'c' iteratore per ogni coppia di elementi in' a' 'b', ecc. Quindi se uno di questi ne consuma il valore, o sono costosi, o dovrebbero essere chiamati una sola volta, quindi questa strategia non funziona necessariamente come desiderato. (Tuttavia, non è realmente possibile produrre tali iteratori in generale comunque, almeno, non senza prima aver raccolto una struttura dati secondaria, come 'Vec', e immagino che la maggior parte degli iteratori siano economici da fare, quindi questi non sembrano particolarmente problematico.) – huon

3

Il itertools crate ha una macro molto ergonomica (iproduct!) per iterare sul prodotto degli iteratori. Ecco un esempio:

#[macro_use] 
extern crate itertools; 

pub fn main() { 
    let a = ['A', 'B', 'C']; 
    let b = [1, 4]; 
    let c = [true, false]; 
    let d = ['x', 'y']; 

    for (a, b, c, d) in iproduct!(&a, &b, &c, &d) { 
     println!("{} {} {} {}", a, b, c, d); 
    } 
} 
+0

Esiste anche la funzione ['cartesian_product'] (https://bluss.github.io/rust-itertools/doc/itertools/trait.Itertools.html#method.cartesian_product) in' itertools'. –

Problemi correlati