Чтобы построить ответ Джеймса на более сложном примере, в моей библиотеке вещей-the-Scala-library-забыли (или не доверяли вам) следующий код:
class DroppedFunction[-A,+B](f: A => Option[B]) extends PartialFunction[A,B] {
private[this] var tested = false
private[this] var arg: A = _
private[this] var ans: Option[B] = None
private[this] def cache(a: A) {
if (!tested || a != arg) {
tested = true
arg = a
ans = f(a)
}
}
def isDefinedAt(a: A) = {
cache(a)
ans.isDefined
}
def apply(a: A) = {
cache(a)
ans.get
}
}
class DroppableFunction[A,B](f: A => Option[B]) {
def drop = new DroppedFunction(f)
}
implicit def function_is_droppable[A,B](f: A => Option[B]) = new DroppableFunction(f)
Большая часть кода посвящена обеспечению того, чтобы оценка функции кэшировалась (до тех пор, пока apply применяется сразу после isDefinedAt). Пример использования:
scala> val f = (x: Int) => if (x>=0) Some(x) else None
f: (Int) => Option[Int] = <function1>
scala> Array(-2,-1,0,1,2).collect(f.drop)
res0: Array[Int] = Array(0, 1, 2)
Кэширование помогает ускорить процесс и избежать проблем с двойными побочными эффектами (по крайней мере, когда isDefinedAt
используется непосредственно перед apply
и когда функция пропускает побочные эффекты, когда возвращает None
).