Как узнать, когда использовать PartialFunction vs return Option
В качестве примера, мы определяем функцию, которая должна преобразовывать 1, 3, 42 соответственно в "foo", "bar", "qix" и все другие целые числа в "X".
Я придумал 2 реализации: метод f
должны быть отделены друг от друга, потому что они могут быть повторно использованы в другом контексте.
def f(i: Int): Option[String] = i match {
case 1 => Some("foo")
case 3 => Some("bar")
case 42 => Some("qix")
case _ => None
}
def g(i: Int) : String = f(i).getOrElse("X")
А также:
def f_ : PartialFunction[Int, String] = {
case 1 => "foo"
case 3 => "bar"
case 42 => "qix"
}
def g_(i: Int) : String = f_.orElse { case _ => "X" }(i)
Я предпочитаю второе, потому что оно избегает много повторений. Некоторые (…)
WDYT?
5 ответов
Карта fScala уже является частичной функцией. Таким образом, вы можете использовать ее вместо определения своей собственной функции, которая делает именно то, что делает Map - "Карта от ключей типа A к значениям типа B".
Так что все, что вам нужно сделать, это:
val f = Map(1 -> "foo", 3 -> "bar", 42 -> "qix")
def g(i: Int) = f.getOrElse(i, "X")
f(1) //foo
f(4) // throw NoSuchElementException: Key not found: 4
f.get(1) // Some(foo)
f.get(4) // None
g(1) //foo
g(4) //X
Теперь вы можете использовать функцию "g" или повторно использовать "f" для других нужд.
Я не уверен, почему вы вообще хотите использовать option, когда вы можете так же легко сделать это и получить точно такой же результат:
def f(i: Int): String = i match {
case 1 => "foo"
case 3 => "bar"
case 42 => "qix"
case _ => "X"
}
Это даже спасает вас противно getOrElse
Вы можете даже пойти лучше и не использовать ни PartialFunction
или match
и просто сделай это:
def f: Int => String = {
case 1 => "foo"
case 3 => "bar"
case 42 => "qix"
case _ => "X"
}
Что спасает вас писать одноразовые i
Отредактировал мой пример согласно вашему комментарию:
def f(i: Int): Option[String] = {
val map = Map(1 -> "foo", 3 -> "bar", 42 -> "qix")
i match {
case x if (map.contains(x)) => Some(map(x))
case _ => None
}
}
def g(i: Int) : String = f(i).getOrElse("X")
Я думаю, что функция должна как-то осмысленно реагировать на целые числа вне заданного диапазона. Вот почему я предпочел бы Option
,
Вы можете попробовать это. Здесь HashMap дает вам быстрый поиск:
object SomeMain {
import scala.collection.immutable.HashMap
def f(i: Int): Option[String] = {
HashMap(1 -> "foo", 3 -> "bar", 42 -> "qix").get(i).orElse(None)
}
def g(i: Int): String = f(i).getOrElse("X")
def main(args: Array[String]) {
List(1, 3, 42, 10) foreach { x => println(x + ": " + g(x)) }
}
}
Выход:
1: foo
3: bar
42: qix
10: X
Опция - это хороший способ обработки нулевого значения. Частичная функция - это только частичное совпадение, они не одинаковы, хотя Option и PartialFunction имеют похожий метод orElse.
Из-за того, что частичная функция является функцией, она может быть объединена в цепочку, а опция - нет, Option - способ обработки нулевого значения.
Для частичной функции вы можете сделать это, это больше похоже на цепь ответственности
def f_1 : PartialFunction[Int, String] = {
case 1 => "1"
}
def f_2 : PartialFunction[Int, String] = {
case 2 => "2"
}
def f_3 : PartialFunction[Int, String] = {
case 3 => "3"
}
f_1.orElse(f_2).orElse(f_3)(3)