2010-07-12 4 views
5

J'ai un itérateur de lignes à partir d'un très gros fichier qui doit être mis en groupes au fur et à mesure que je progresse. Je sais où chaque groupe se termine parce qu'il y a une valeur sentinelle sur la dernière ligne de chaque groupe. Donc, fondamentalement, je veux écrire une fonction qui prend un itérateur et une valeur sentinelle, et retourne un itérateur de groupes terminés par la valeur sentinelle. Quelque chose comme:regrouper des éléments dans un itérable en recherchant une valeur sentinelle (en 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, .)) 

Notez que je veux les éléments sentinelles inclus à la fin de chacun des groupes. Voici ma solution actuelle:

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

Je pense que cela va fonctionner, et je pense qu'il est très bien, mais avoir à ajouter de nouveau la sentinelle à chaque fois que me donne une odeur de code. Y a-t-il une meilleure manière de faire cela?

+0

Souhaitez-vous ajouter une sentinelle au dernier groupe s'il ne l'a pas contenue? (par exemple "abc.def" -> ["abc.", "def."]) –

+0

Idéalement non, même si pratiquement je pense que cela n'a pas d'importance. – Steve

+0

Il se trouve que j'ai voulu, et demandé, un 'takeTo' (plus' dropTo' et 'spanTo'), qui agirait comme' takeWhile', mais retourner un élément de plus - le premier pour lequel le le prédicat est vrai. Si vous vous sentez comme moi, vous pouvez laisser tomber une note ici: https://lampsvn.epfl.ch/trac/scala/ticket/2963 –

Répondre

2

laid, mais devrait être plus performant que votre solution:

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, c'est moche, mais cool. =) Vous pouvez déplacer le "var last" vers une variable privée, puis il semble un peu moins laid. – Steve

5

moins lisible que le vôtre, mais plus "correct" lorsque le dernier groupe n'a pas de valeur sentinelle de terminaison:

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 
} 
} 

Ou, récursive:

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]()) 
    } 
    } 
Questions connexes