2010-07-12 11 views
5

Ho un iteratore di righe da un file molto grande che deve essere inserito in gruppi mentre mi muovo. So dove finisce ogni gruppo perché c'è un valore sentinella sull'ultima riga di ogni gruppo. Quindi in pratica voglio scrivere una funzione che prende un iteratore e un valore sentinella, e restituisce un iteratore di gruppi ciascuno terminato dal valore sentinella. Qualcosa di simile:raggruppare gli elementi in un iterable cercando un valore sentinella (in scala)

scala> groups("abc.defg.hi.jklmn.".iterator, '.') 
res1: Iterator[Seq[Char]] = non-empty iterator 

scala> groups("abc.defg.hi.jklmn.".iterator, '.').toList 
res19: List[Seq[Char]] = List(List(a, b, c, .), List(d, e, f, g, .), List(h, i, .), List(j, k, l, m, n, .)) 

Nota che desidero includere gli elementi sentinella alla fine di ogni gruppo. Ecco la mia soluzione attuale:

def groups[T](iter: Iterator[T], sentinel: T) = new Iterator[Seq[T]] {     
    def hasNext = iter.hasNext 
    def next = iter.takeWhile(_ != sentinel).toList ++ List(sentinel) 
} 

penso che questo funzionerà, e credo che sia bene, ma di dover aggiungere nuovamente la sentinella ogni volta che mi dà un odore di codice. C'è un modo migliore per farlo?

+0

volevi una sentinella aggiunto all'ultimo gruppo se non contenerlo? (ad esempio "abc.def" -> ["abc.", "def."]) –

+0

Idealmente no, anche se praticamente penso che non importi. – Steve

+0

Succede che io abbia voluto e chiesto un 'takeTo' (più' dropTo' e 'spanTo'), che si comporterebbero come' takeWhile', ma restituiscono un elemento in più - il primo per cui il il predicato è vero. Se ti senti come me, potresti lasciare una nota qui: https://lampsvn.epfl.ch/trac/scala/ticket/2963 –

risposta

2

Brutto, ma dovrebbe essere più performante rispetto la vostra soluzione:

def groups[T](iter: Iterator[T], sentinel: T) = new Iterator[Seq[T]] {     
    def hasNext = iter.hasNext 
    def next = iter.takeWhile{ 
     var last = null.asInstanceOf[T] 
     c => { val temp = last; last = c; temp != sentinel} 
    }.toList 
    } 
+0

Wow, è brutto, ma fico. =) Puoi spostare "var last" su una variabile privata, e poi sembra un po 'meno brutto. – Steve

5

meno leggibile della tua, ma più "corretta" quando il gruppo finale non ha un valore di terminazione sentinella:

def groups[T](iter: Iterator[T], sentinel: T) = new Iterator[Seq[T]] { 
def hasNext = iter.hasNext 
def next: Seq[T] = { 
    val builder = scala.collection.mutable.ListBuffer[T]() 
    while (iter.hasNext) { 
     val x = iter.next 
     builder.append(x) 
     if (x == sentinel) return builder 
    } 
    builder 
} 
} 

Oppure, in modo ricorsivo:

def groups[T](iter: Iterator[T], sentinel: T) = new Iterator[Seq[T]] { 
    def hasNext = iter.hasNext 
    def next: Seq[T] = { 
     @scala.annotation.tailrec 
     def build(accumulator: ListBuffer[T]): Seq[T] = { 
     val v = iter.next 
     accumulator.append(v) 
     if (v == sentinel || !iter.hasNext) => accumulator 
     else build(accumulator) 
     } 
     build(new ListBuffer[T]()) 
    } 
    } 
Problemi correlati