Posted in scala, partial function
Partial Functions, Scala
Comment
Case?
case
는 match
가 없어도 쓰일 수 있다. 예를 들어
val m1 = Map(1 -> "one", 2 -> "two")
m1 foreach { case(k, v) => println(s"k -> v" }
사실, case
가 들어간 문장은 function
이다. Scala Doc 을 보면, scala.collection.immutable.Map
의 foreach
는 다음처럼 정의되어 있다.
def foreach(f: ((A, B)) => Unit): Unit
Map vs Collect
여기 재미난 예제가 하나 더 있다. map
과 collect
가 서로 다른 결과를 보여준다.
scala > val l = List(490, "Nexus5")
scala > l map { case i: Int => i * 1000 }
scala.MatchError: Nexus5 (of class java.lang.String)
at $anonfun$1.apply(<console>:9)
at $anonfun$1.apply(<console>:9)
at scala.collection.immutable.List.map(List.scala:276)
문자열인 "Nexus5"
를 만나면 MatchError
가 발생한다. 반면 collect
는
scala > val l = List(490, "Nexus5")
scala > l collect { case i: Int => i * 1000 }
res11: List[Int] = List(49000)
왜 그럴까? scala.collection.immutable.List
의 collect
정의를 한번 보자.
def collect[B](pf: PartialFunction[A, B]): Map[B]
function
이 아니고 PartialFunction
을 취한다.
Partial Function
Partial Function 을 이해하는 쉬운 방법은, 일반 Function 을 Total Function 으로 이해하면 된다. 즉, 일반 Function 이 주어진 인자에 대해 모든 값을 취한다면, Partial Function 은 주어진 타입에 대해서 특정 값만 취할 수 있다. Scala School 의 원문을 첨부하면
A function works for every argument of the defined type. In other words, a function defined as (Int) => String takes any Int and returns a String.
A Partial Function is only defined for certain values of the defined type. A Partial Function (Int) => String might not accept every Int.
val onlyOne : PartialFunction[Int, String] = { case 1 => "one" }
onlyOne.isDefinedAt(1) // true
onlyOne.isDefinedAt(2) // false
onlyOne
은 Int
를 인자로 받지만, 그 중에서도 1
만 취해 "one"
을 돌려주는 PartialFunction[Int, String]
이다.
PartialFunction
또한 compose 될 수 있다.
val onlyOne : PartialFunction[Int, String] = { case 1 => "one" }
val onlyTwo : PartialFunction[Int, String] = { case 2 => "two" }
val wildcard : PartialFunction[Int, String] = { case _ => "else" }
val partial = onlyOne orElse onlyTwo orElse wildcard
Case!
처음엔 case
가 Function
을 만든다고 했지만, 사실 PartialFunction
을 정의한다. map
나 filter
는 Function
을 받지만, PartialFunction
은 Function
의 subtype 이므로 파라미터로 넘겨줄 수 있다.
case class Car(model: String, price: Int)
val list = List(Car("H", 3000), Car("A", 2800))
list filter { case Car(model, price) => price < 2900 }
// List[Car] = List(Car("A", 2800))
References
(1) http://blog.bruchez.name/2011/10/scala-partial-functions-without-phd.html
(2) https://twitter.github.io/scala_school/pattern-matching-and-functional-composition.html