Komponieren Sie Teilfunktionen
-
21-12-2019 - |
Frage
Ich habe zwei Teilfunktionen f
und g
.Sie haben keine Nebenwirkungen und sind schnell auszuführen.Was ist der beste Weg, um sie zu einer anderen Teilfunktion zusammenzusetzen h
so dass
h.isDefinedAt(x)
iff f.isDefinedAt(x) && g.isDefinedAt(f(x))
?
Es ist auch in Ordnung, wenn h
ist eine Funktion, die eine zurückgibt Option
eher als eine Teilfunktion.
Ich bin enttäuscht, dass f andThen g
macht nicht was ich will:
scala> val f = Map("a"->1, "b"->2)
f: scala.collection.immutable.Map[String,Int] = Map(a -> 1, b -> 2)
scala> val g = Map(1->'c', 3->'d')
g: scala.collection.immutable.Map[Int,Char] = Map(1 -> c, 3 -> d)
scala> (f andThen g).isDefinedAt("b")
res3: Boolean = true
scala> (f andThen g).lift("b")
java.util.NoSuchElementException: key not found: 2
at scala.collection.MapLike$class.default(MapLike.scala:228)
Lösung
Hier ist ein kürzerer Weg als die verknüpfte Frage, entnommen aus dieser Thread:
val f = Map("a" -> 1, "b" -> 2)
val g = Map(1 -> 'c', 3 -> 'd')
def andThenPartial[A, B, C](pf1: PartialFunction[A, B], pf2: PartialFunction[B, C]): PartialFunction[A, C] = {
Function.unlift(pf1.lift(_) flatMap pf2.lift)
}
val h = andThenPartial(f, g) //> h : PartialFunction[String,Char]
h.isDefinedAt("a") //> res2: Boolean = true
h.isDefinedAt("b") //> res3: Boolean = false
h.lift("a") //> res4: Option[Char] = Some(c)
h.lift("b") //> res5: Option[Char] = None
Dies kann natürlich auch als implizite Klasse zusammengefasst werden:
implicit class ComposePartial[A, B](pf: PartialFunction[A, B]) {
def andThenPartial[C](that: PartialFunction[B, C]): PartialFunction[A, C] =
Function.unlift(pf.lift(_) flatMap that.lift)
}
val h2 = f andThenPartial g //> h2 : PartialFunction[String,Char]
h2.isDefinedAt("a") //> res6: Boolean = true
h2.isDefinedAt("b") //> res7: Boolean = false
h2.lift("a") //> res8: Option[Char] = Some(c)
h2.lift("b") //> res9: Option[Char] = None
Andere Tipps
val h = f.mapValues(g)
Das funktioniert aber nur für Karten.Ich glaube nicht, dass es einen kurzen Weg gibt, dies für irgendeine Art von Partialfunktion zu tun, Sie müssen nur manuell ein neues PartialFunction Objekt erstellen.
bearbeiten:Ich sehe, dass mein obiger Code nicht das ist, was Sie wollten.Aber vielleicht ist das besser
val h = f.collect { case (k, v) if(g.contains(v)) => (k, g(v)) }