у меня была подобная необходимость, но solution от @oxbow_lakes не принимает в для учета ситуации, когда список имеет только один элемент, или даже если список содержит элементы, которые не повторяются. Кроме того, это решение не поддается бесконечному итератору (он хочет «видеть» все элементы, прежде чем он даст вам результат).
Мне нужна была возможность группировать последовательные элементы, которые соответствуют предикату, но также включают в себя отдельные элементы (я всегда могу отфильтровать их, если они мне не нужны).Я нуждался в том, чтобы эти группы были доставлены непрерывно, не дожидаясь полного потребления оригинального итератора до их производства.
я придумал следующий подход, который работает для моих потребностей, и думал, что я должен поделиться:
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
}
}
}
Выше, используя несколько вспомогательных классов:
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
}
Пример использования:
List(1,2,2,3,3,3,4,5,5).iterator.groupWhen(_ == _).toList
дает: List(List(1), List(2, 2), List(3, 3, 3), List(4), List(5, 5))
Если Вы хотите, чтобы отфильтровать отдельные элементы, просто применить filter
или withFilter
после groupWhen
Stream.continually(Random.nextInt(100)).iterator
.groupWhen(_ + _ == 100).withFilter(_.length > 1).take(3).toList
дает: List(List(34, 66), List(87, 13), List(97, 3))
Остерегайтесь того, что эта реализация приведет к отбрасыванию элементов, в которых предикат возвращает false. Лучше использовать внедрение борзая. –