miałem podobną potrzebę, ale solution z @oxbow_lakes nie bierze w aby uwzględnić sytuację, w której lista zawiera tylko jeden element lub nawet jeśli lista zawiera elementy, które nie są powtarzane. Ponadto to rozwiązanie nie nadaje się dobrze do nieskończonego iteratora (chce "zobaczyć" wszystkie elementy, zanim da wynik).
Potrzebna mi była możliwość grupowania elementów sekwencyjnych zgodnych z predykatem, ale także pojedynczych elementów (zawsze mogę je odfiltrować, jeśli ich nie potrzebuję).Potrzebowałem, aby te grupy były dostarczane w sposób ciągły, bez czekania, aż oryginalny iterator zostanie całkowicie zużyty, zanim zostaną wyprodukowane.
wymyśliłem następujące podejście, które pracuje dla moich potrzeb, i że powinienem dzielić:
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
}
}
}
powyższe jest przy użyciu kilku klas pomocników:
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
}
Przykład użycia:
List(1,2,2,3,3,3,4,5,5).iterator.groupWhen(_ == _).toList
daje: List(List(1), List(2, 2), List(3, 3, 3), List(4), List(5, 5))
Jeśli chcesz odfiltrować pojedyncze elementy, po prostu zastosować filter
lub withFilter
po groupWhen
Stream.continually(Random.nextInt(100)).iterator
.groupWhen(_ + _ == 100).withFilter(_.length > 1).take(3).toList
daje: List(List(34, 66), List(87, 13), List(97, 3))
Pamiętaj, że ta implementacja usunie elementy, w których predykat zwróci fałsz. Lepiej wykorzystaj implementację borice. –