Как узнать, когда использовать 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)
Другие вопросы по тегам