Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Combine 2 partial functions

Tags:

function

scala

I have two partial functions returning unit (f1, f2). For instance, something like that:

val f1 = {
   case s: arg => //do some
   //etc... lots of cases
}

val f2 = {
   case s: anotherArg => //do some
   //lots of cases
}

Is there a concise way to compose this to partial functions the way as that if

f(x) = {f1(x); f2(x)} iff f1.isDefinedAt(x) && f2.isDefinedAt(x)
f(x) = f1(x); iff f1.isDefinedAt(x) && !f2.isDefinedAt(x) 
f(x) = f2(x); iff !f1.isDefinedAt(x) && f2.isDefinedAt(x) 
like image 279
St.Antario Avatar asked Feb 05 '23 06:02

St.Antario


2 Answers

orElse

f1 orElse f2

Scala REPL

scala> val f: PartialFunction[Int, Int] = { case 1 => 1 }
f: PartialFunction[Int,Int] = <function1>

scala> val g: PartialFunction[Int, Int] = { case 2 => 2 }
g: PartialFunction[Int,Int] = <function1>

scala> val h = f orElse g
h: PartialFunction[Int,Int] = <function1>

scala> h(1)
res3: Int = 1

scala> h(2)
res4: Int = 2

scala> h.isDefinedAt(1)
res6: Boolean = true

scala> h.isDefinedAt(2)
res7: Boolean = true

Both both functions to execute on common cases

Using List of partial functions and foldLeft

Scala REPL

scala> val f: PartialFunction[Int, Int] = { case 1 => 1 case 3 => 3}
f: PartialFunction[Int,Int] = <function1>

scala> val g: PartialFunction[Int, Int] = { case 2 => 2  case 3 => 3}
g: PartialFunction[Int,Int] = <function1>

scala> val h = f orElse g
h: PartialFunction[Int,Int] = <function1>

scala> h(3)
res10: Int = 3

scala> h(3)
res11: Int = 3

scala> val h = List(f, g)
h: List[PartialFunction[Int,Int]] = List(<function1>, <function1>)

scala> def i(arg: Int) = h.foldLeft(0){(result, f) => if (f.isDefinedAt(arg)) result + f(arg) else result }
i: (arg: Int)Int

scala> i(3)
res12: Int = 6
like image 154
pamu Avatar answered Feb 15 '23 23:02

pamu


Although pamu's answer is good, I don't like the fact that it is bound to specific Int type. Unfortunately you didn't specify result type well enough, so I see 3 alternatives:

  1. You want to get list of all results of all defined functions and you don't care about which function produced which result. In this case something like this would work:
def callAll[A, B](funcs: List[PartialFunction[A, B]], a: A): List[B] = funcs.foldRight(List.empty[B])((f, acc) => if (f.isDefinedAt(a)) f.apply(a) :: acc else acc)

if order of elements is not important you may use

def callAll[A, B](funcs: List[PartialFunction[A, B]], a: A): List[B] = funcs.foldLeft(List.empty[B])((f, acc) => if (f.isDefinedAt(a)) f.apply(a) :: acc else acc)

which probably will be a bit faster

  1. You want to get Option with Some in case corresponding function is defined at the point or None otherwise. In such case something like this would work:
def callAllOption[A, B](funcs: List[PartialFunction[A, B]], a: A): List[Option[B]] = funcs.map(f => f.lift.apply(a))

If you don't want to create List explicitly, you can use varargs such as:

def callAllOptionVarArg[A, B](a: A, funcs: PartialFunction[A, B]*): List[Option[B]] = funcs.map(f => f.lift.apply(a)).toList

or such curried version to specify value after functions:

def callAllOptionVarArg2[A, B](funcs: PartialFunction[A, B]*)(a: A): List[Option[B]] = funcs.map(f => f.lift.apply(a)).toList
  1. You call functions purely for side effects and return value is not important, in which case you can safely use second (a bit faster) callAll definition

Examples:

val f: PartialFunction[Int, Int] = {
  case 1 => 1
  case 3 => 3
}
val g: PartialFunction[Int, Int] = {
  case 2 => 2
  case 3 => 4
}

val fl = List(f, g)
println(callAll(fl, 1))
println(callAll(fl, 3))
println(callAllOption(fl, 2))
println(callAllOptionVarArg(1, f, g))
println(callAllOptionVarArg2(f, g)(3))

List(1)
List(3, 4)
List(None, Some(2))
List(Some(1), None)
List(Some(3), Some(4))

like image 23
SergGr Avatar answered Feb 15 '23 21:02

SergGr