ho avuto una simile esigenza, ma il solution da @oxbow_lakes non prendere in per rendere conto della situazione quando la lista ha un solo elemento, o anche se la lista contiene elementi che non sono ripetuti. Inoltre, quella soluzione non si presta bene a un iteratore infinito (vuole "vedere" tutti gli elementi prima che ti dia un risultato).
Ciò di cui avevo bisogno era la possibilità di raggruppare elementi sequenziali che corrispondono a un predicato, ma anche includere i singoli elementi (posso sempre filtrarli se non ne ho bisogno).Avevo bisogno che quei gruppi venissero consegnati continuamente, senza dover aspettare che l'iteratore originale fosse completamente consumato prima di essere prodotto.
mi si avvicinò con il seguente approccio che funziona per le mie esigenze, e ho pensato che avrei dovuto condividere:
implicit class IteratorEx[+A](itr: Iterator[A]) {
def groupWhen(p: (A, A) => Boolean): Iterator[List[A]] = new AbstractIterator[List[A]] {
val (it1, it2) = itr.duplicate
val ritr = new RewindableIterator(it1, 1)
override def hasNext = it2.hasNext
override def next() = {
val count = (ritr.rewind().sliding(2) takeWhile {
case Seq(a1, a2) => p(a1, a2)
case _ => false
}).length
(it2 take (count + 1)).toList
}
}
}
È possibile che questo
sta usando un paio di classi di supporto:
abstract class AbstractIterator[A] extends Iterator[A]
/**
* Wraps a given iterator to add the ability to remember the last 'remember' values
* From any position the iterator can be rewound (can go back) at most 'remember' values,
* such that when calling 'next()' the memoized values will be provided as if they have not
* been iterated over before.
*/
class RewindableIterator[A](it: Iterator[A], remember: Int) extends Iterator[A] {
private var memory = List.empty[A]
private var memoryIndex = 0
override def next() = {
if (memoryIndex < memory.length) {
val next = memory(memoryIndex)
memoryIndex += 1
next
} else {
val next = it.next()
memory = memory :+ next
if (memory.length > remember)
memory = memory drop 1
memoryIndex = memory.length
next
}
}
def canRewind(n: Int) = memoryIndex - n >= 0
def rewind(n: Int) = {
require(memoryIndex - n >= 0, "Attempted to rewind past 'remember' limit")
memoryIndex -= n
this
}
def rewind() = {
memoryIndex = 0
this
}
override def hasNext = it.hasNext
}
uso Esempio:
List(1,2,2,3,3,3,4,5,5).iterator.groupWhen(_ == _).toList
dà: List(List(1), List(2, 2), List(3, 3, 3), List(4), List(5, 5))
Se si desidera filtrare i singoli elementi, basta applicare un filter
o withFilter
dopo groupWhen
Stream.continually(Random.nextInt(100)).iterator
.groupWhen(_ + _ == 100).withFilter(_.length > 1).take(3).toList
dà: List(List(34, 66), List(87, 13), List(97, 3))
Attenzione che questa implementazione farà cadere gli elementi in cui il predicato restituisce false. Meglio usare l'implementazione borice. –