Compor funções parciais
-
21-12-2019 - |
Pergunta
Eu tenho duas PartialFunctions f
e g
.Eles não têm efeitos colaterais e são rápidos de executar.Qual é a melhor maneira de compô-los em outra função parcial h
de tal modo queh.isDefinedAt(x)
se f.isDefinedAt(x) && g.isDefinedAt(f(x))
?
Também está tudo bem se h
é uma função que retorna um Option
em vez de uma função parcial.
Estou desapontado com isso f andThen g
não faz o que eu quero:
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)
Solução
Aqui está um caminho mais curto do que a pergunta vinculada, retirada de este tópico:
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
Isso também pode ser encerrado como uma classe implícita, é claro:
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
Outras dicas
val h = f.mapValues(g)
Mas isso só funciona para mapas.Não acho que exista uma maneira rápida de fazer isso para qualquer tipo de função parcial, você apenas terá que criar um novo objeto PartialFunction manualmente.
editar:Vejo que meu código acima não é o que você queria.Mas talvez isso seja melhor
val h = f.collect { case (k, v) if(g.contains(v)) => (k, g(v)) }