Die Funktion selbst (pf.apply
) ausgewertet zweimal nicht wirklich, aber seine isDefinedAt
ist zweimal für eine erfolgreiche Übereinstimmungen mit Ihrer Definition ausgewertet. Und das bedeutet, zweimal unapply
-s und Wachen in der ersten PartialFunction
pf
zu bewerten.
Übrigens gibt es in Scalaz einen Kombinator, der eine ähnliche Sache macht: pf.first.andThen(_._1)
, aber es entspricht im Grunde Ihrer Definition.
Sie können einen kleinen Test schreiben, um zu sehen, ob pf.isDefinedAt
zweimal ausgewertet wird und es mit mehreren möglichen Implementierungen von pfAdapter
laufen:
object Unapply {
def unapply(s: String): Boolean = {
println(s"unapplying on $s")
s == "1"
}
}
val m = Map("1" -> 1, "2" -> 2)
def pf: PartialFunction[String, String] = {
case Unapply() => "11"
}
def pfAdapter1[A, B, T](pf: PartialFunction[A, B]): PartialFunction[(A, T), B] =
Function.unlift((t: (A, T)) => pf.lift(t._1))
def pfAdapter2[A, B, T](pf: PartialFunction[A, B]): PartialFunction[(A, T), B] =
new PartialFunction[(A, T), B] {
def isDefinedAt(arg: (A, T)) = pf.isDefinedAt(arg._1)
def apply(arg: (A, T)) = pf(arg._1)
}
def pfAdapter3[A, B, T](pf: PartialFunction[A, B]): PartialFunction[(A, T), B] = {
case (a,b) if pf.isDefinedAt(a) => pf(a)
}
def pfAdapter4[A, B, T](pf: PartialFunction[A, B]): PartialFunction[(A, T), B] = {
import scalaz.Scalaz._
pf.first.andThen(_._1)
}
println(m collect pfAdapter1(pf))
println(m collect pfAdapter2(pf))
println(m collect pfAdapter3(pf))
println(m collect pfAdapter4(pf))
Und das Ergebnis der Ausführung dieser Code wie folgt:
unapplying on 1
unapplying on 2
List(11)
unapplying on 1
unapplying on 1
unapplying on 2
List(11)
unapplying on 1
unapplying on 1
unapplying on 2
List(11)
unapplying on 1
unapplying on 1
unapplying on 2
List(11)
Also die erste Implementierung von pfAdapter
: Function.unlift((t: (A, T)) => pf.lift(t._1))
tatsächlich tut vermeiden, isDefinedAt
zweimal zu bewerten.
Dies funktioniert, weil Map.collect
mit PartialFunction.applyOrElse
und die Dokumentation für applyOrElse
Staaten umgesetzt wird, dass:
Für alle Teil Funktionsliteralen der Compiler generiert eine applyOrElse Implementierung, die Matcher Doppel Auswertung der Muster vermeidet und Wachen. Dies macht applyOrElse die Grundlage für die effiziente Implementierung für viele Operationen und Szenarien, wie zB:
...
- Lift und unlift keine Quellenfunktionen zweimal bei jedem Aufruf
bewerten
Sie sind also OK mit einer Ausnahme, wenn der Schlüssel nicht existiert? Oder fehlt mir etwas? –
Nein, ich möchte eine PartialFunction, die auf Tupeln definiert ist, deren erstes Element als Schlüssel in map vorhanden ist. Keine Ausnahmen :) – Dima
Dann wie ist es teilweise? Was ist das gewünschte Ergebnis, wenn das erste Element nicht vorhanden ist? –