2013-05-01 17 views
13

Ho più iteratori che restituiscono gli articoli in un modo ordinato secondo un criterio di ordinamento. Ora, vorrei unire (multiplex) gli iteratori in uno, iteratore combinato. So come farlo in stile Java, ad es. albero-mappa, ma mi chiedevo se c'è un approccio più funzionale? Voglio preservare il più possibile la pigrizia degli iteratori.Scala: unione di più iteratori

+0

possibile duplicato di [Come combinare 2 Iteratori a Scala?] (Http://stackoverflow.com/questions/9047856/how-to-combine-2-iterators-in -scala) –

risposta

25

si può solo fare:

val it = iter1 ++ iter2 

Si crea un altro iteratore e non valutare gli elementi, ma avvolge i due iteratori esistenti. È completamente pigro, quindi non è necessario utilizzare iter1 o iter2 dopo averlo fatto.

In generale, se avete più iteratori a fondersi, è possibile utilizzare pieghevole:

val iterators: Seq[Iterator[T]] = ??? 
val it = iterators.foldLeft(Iterator[T]())(_ ++ _) 

Se avete qualche ordinamento sugli elementi che si desidera mantenere nel iteratore risultante, ma si vuole pigrizia, puoi convertirli in stream:

def merge[T: Ordering](iter1: Iterator[T], iter2: Iterator[T]): Iterator[T] = { 
    val s1 = iter1.toStream 
    val s2 = iter2.toStream 

    def mergeStreams(s1: Stream[T], s2: Stream[T]): Stream[T] = { 
    if (s1.isEmpty) s2 
    else if (s2.isEmpty) s1 
    else if (s1.head < s2.head) s1.head #:: mergeStreams(s1.tail, s2) 
    else s2.head #:: mergeStreams(s1, s2.tail) 
    } 

    mergeStreams(s1, s2).iterator 
} 

Non necessariamente più veloce, però, dovresti microbencharlo.

Una possibile alternativa è utilizzare buffered iterators per ottenere lo stesso effetto.

+0

OK, come posso assicurarmi che l'ordine relativo in base agli stessi criteri di classificazione sia mantenuto? Diciamo che ho un oggetto che ha un timestamp in una forma di 'DateTime'. Vorrei che questi due iteratori venissero accorpati ai timestamp, non uno dopo l'altro (in Java userei il comparatore) – Bober02

+0

Ho modificato la risposta. – axel22

+0

Grazie, ma non voglio assolutamente usare gli stream mentre nascondono gli elementi. Inoltre, posso fornire l'ordine sugli elementi reali, ad es. come in Java Comparator che puoi passare alle raccolte come argomento? – Bober02

2

Si potrebbe provare:

(iterA ++ iterB).toStream.sorted.toIterator 

Ad esempio:

 
val i1 = (1 to 100 by 3).toIterator 
val i2 = (2 to 100 by 3).toIterator 
val i3 = (3 to 100 by 3).toIterator 

val merged = (i1 ++ i2 ++ i3).toStream.sorted.toIterator 

merged.next // results in: 1 
merged.next // results in: 2 
merged.next // results in: 3 
+0

Oops, mio ​​male. Vedo che non vuoi usare gli Stream. –

4

Come @ axel22 accennato, è possibile farlo con BufferedIterators. Ecco una soluzione priva di Stream:

def combine[T](rawIterators: List[Iterator[T]])(implicit cmp: Ordering[T]): Iterator[T] = { 
    new Iterator[T] { 
    private val iterators: List[BufferedIterator[T]] = rawIterators.map(_.buffered) 

    def hasNext: Boolean = iterators.exists(_.hasNext) 

    def next(): T = if (hasNext) { 
     iterators.filter(_.hasNext).map(x => (x.head, x)).minBy(_._1)(cmp)._2.next() 
    } else { 
     throw new UnsupportedOperationException("Cannot call next on an exhausted iterator!") 
    } 
}