Skip to content

Instantly share code, notes, and snippets.

@leifwickland
Created August 8, 2018 20:34
Show Gist options
  • Save leifwickland/968877800565b530fec795956a0096d1 to your computer and use it in GitHub Desktop.
Save leifwickland/968877800565b530fec795956a0096d1 to your computer and use it in GitHub Desktop.
scanFilter/scanFind
object IndexedSeqSyntax {
@specialized(Specializable.Bits32AndUp)
implicit class IndexedSeqOps[T](ts: IndexedSeq[T]) {
/**
* Accumulates values, S, derived from T, and returns the first T, if any when p is true for S.
*
* It's kind of like a lazy conjunction of the standard library's `scan` and `find`.
*
* It could be implemented less efficiently as:
* ts.toIterator.zip(ts.toIterator.scanLeft(sz)(toS)).find(t => p(_._2)).map(_._1)
*
* @param z Initial value of the accumulated state.
* @param op Applied to the accumulated state and the element.
* @param p The predicate to satisfy against the accumulated state
* @tparam S The state which is accumulated
* @return The first T when p is satisfied, or None
*/
def scanFind[S](z: S)(op: (S, T) => S)(p: S => Boolean): Option[T] = {
val len = ts.length
@annotation.tailrec
def loop(i: Int, s: S): Option[T] = {
if (i >= len) None
else {
val t = ts(i)
val newS = op(s, t)
if (p(newS)) Some(t)
else loop(i + 1, newS)
}
}
loop(0, z)
}
/**
* Accumulates values, S, derived from T, and returns all Ts for which `p` is true.
*
* It's kind of like a lazy conjunction of the standard library's `scan` and `filter`.
*
* It could be implemented less efficiently as:
* ts.toIterator.zip(ts.toIterator.scanLeft(sz)(toS)).filter(t => p(_._2)).map(_._1)
*
* @param z Initial value of the accumulated state.
* @param op Applied to the accumulated state and the element.
* @param p The predicate to satisfy against the accumulated state
* @tparam S The state which is accumulated
* @return All Ts when p is satisfied; possibly empty.
*/
def scanFilter[S](z: S)(op: (S, T) => S)(p: S => Boolean): Vector[T] = {
val len = ts.length
val buffer = scala.collection.mutable.Buffer[T]()
@annotation.tailrec
def loop(i: Int, s: S): Vector[T] = {
if (i >= len) buffer.toVector
else {
val t = ts(i)
val newS = op(s, t)
if (p(newS)) { buffer += t }
loop(i + 1, newS)
}
}
loop(0, z)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment