2016-05-18 15 views
1

Ich versuche, mit einem combinator zu kommen, die mich so etwas tun würde erlauben:Adapter für eine Partielle Funktion

def pfAdapter(pf: PartialFunction[String, String]): PartialFunction[(String,String), String]) = { 
    case (a,b) if(pf.isDefinedAt(a)) => pf(a) 
} 

Grundsätzlich habe ich eine Map[String, String], und möchte es als PartialFunction verwenden, die zwei Takes Parameter - Ignoriere das zweite Element und verwende das erste als Schlüssel.

Der Ansatz oben genannten Arbeiten, aber ich weiß nicht, wie die Tatsache, dass pf im Wesentlichen zweimal ausgewertet wird (es kann keinen Weg dran vorbei sein), und dass es nicht „elegant“ ... Ich frage mich, ob es etwas ist, Eine Art Kombinator, von dem ich nichts weiß, würde mich etwas wie { _._1 } andThen pf machen lassen. Dieser letzte Versuch funktioniert natürlich nicht, weil das Ergebnis davon immer definiert ist und einfach auf nicht existenten Schlüsseln scheitern wird. Ich benutze es nur als Beispiel dafür, wie die Lösung, nach der ich suche, ideal aussehen würde.

Ideen, jemand?

+0

Sie sind also OK mit einer Ausnahme, wenn der Schlüssel nicht existiert? Oder fehlt mir etwas? –

+0

Nein, ich möchte eine PartialFunction, die auf Tupeln definiert ist, deren erstes Element als Schlüssel in map vorhanden ist. Keine Ausnahmen :) – Dima

+0

Dann wie ist es teilweise? Was ist das gewünschte Ergebnis, wenn das erste Element nicht vorhanden ist? –

Antwort

2

Die Funktion selbst (pf.apply) ausgewertet zweimal nicht wirklich, aber seine isDefinedAtist zweimal für eine erfolgreiche Übereinstimmungen mit Ihrer Definition ausgewertet. Und das bedeutet, zweimal unapply -s und Wachen in der ersten PartialFunctionpf 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
+0

Danke, dass du mich auf den '.unlift' hingewiesen hast, ich wusste nichts davon. – Dima