Поиск…


Состав

Частичные функции часто используются для определения полной функции по частям:

sealed trait SuperType
case object A extends SuperType
case object B extends SuperType
case object C extends SuperType

val pfA: PartialFunction[SuperType, Int] = {
  case A => 5
}

val pfB: PartialFunction[SuperType, Int] = {
  case B => 10
}

val input: Seq[SuperType] = Seq(A, B, C)

input.map(pfA orElse pfB orElse {
  case _ => 15
}) // Seq(5, 10, 15)

При этом использовании частичные функции предпринимаются в порядке конкатенации с orElse метода orElse . Как правило, предоставляется заключительная частичная функция, которая соответствует всем остальным случаям. В совокупности комбинация этих функций действует как общая функция.

Этот шаблон обычно используется для разделения проблем, когда функция может эффективно выполнять диспетчер для разрозненных путей кода. Это обычное явление, например, в методе приема Акка Акка .

Использование с `collect`

Хотя частичная функция часто используется в качестве удобного синтаксиса для общих функций, путем включения окончательного подстановочного соответствия ( case _ ) в некоторых методах их пристрастность является ключевым. Один очень распространенный пример в идиоматической Scala - это метод collect , определенный в библиотеке коллекций Scala. Здесь частичные функции позволяют общими функциями проверки элементов коллекции отображать и / или фильтровать их в одном компактном синтаксисе.

Пример 1

Предполагая, что мы имеем функцию квадратного корня, определенную как частичная функция:

val sqRoot:PartialFunction[Double,Double] = { case n if n > 0 => math.sqrt(n) }

Мы можем вызвать его с помощью комбинатора collect :

List(-1.1,2.2,3.3,0).collect(sqRoot)

эффективно выполняя ту же операцию, что и:

List(-1.1,2.2,3.3,0).filter(sqRoot.isDefinedAt).map(sqRoot)

Пример 2.

sealed trait SuperType // `sealed` modifier allows inheritance within current build-unit only
case class A(value: Int) extends SuperType
case class B(text: String) extends SuperType
case object C extends SuperType

val input: Seq[SuperType] = Seq(A(5), B("hello"), C, A(25), B(""))

input.collect {
  case A(value) if value < 10   => value.toString
  case B(text) if text.nonEmpty => text
} // Seq("5", "hello")

В приведенном выше примере есть несколько вещей:

  • Левая часть каждого совпадения шаблонов эффективно выбирает элементы для обработки и включения в выход. Любое значение, которое не имеет подходящего case , просто опускается.
  • Правая часть определяет применимую к делу обработку.
  • Соответствие шаблону связывает переменную для использования в защитных выражениях (предложения if ) и в правой части.

Основной синтаксис

Scala имеет специальный тип функции, называемый частичной функцией , которая расширяет нормальные функции, что означает, что экземпляр PartialFunction можно использовать везде, где ожидается Function1 . Частичные функции могут быть определены анонимно с использованием синтаксиса case также используемого при сопоставлении шаблонов :

val pf: PartialFunction[Boolean, Int] = {
  case true => 7
}

pf.isDefinedAt(true) // returns true
pf(true) // returns 7

pf.isDefinedAt(false) // returns false
pf(false) // throws scala.MatchError: false (of class java.lang.Boolean)

Как видно из примера, частичная функция не обязательно должна быть определена во всей области ее первого параметра. Стандартный экземпляр Function1 считается полным , что означает, что он определен для каждого возможного аргумента.

Использование в качестве общей функции

Частичные функции очень распространены в идиоматической Scala. Они часто используются для их удобного синтаксиса на основе case для определения общих функций по признакам :

sealed trait SuperType // `sealed` modifier allows inheritance within current build-unit only
case object A extends SuperType
case object B extends SuperType
case object C extends SuperType

val input: Seq[SuperType] = Seq(A, B, C)

input.map {
  case A => 5
  case _ => 10
} // Seq(5, 10, 10)

Это сохраняет дополнительный синтаксис оператора match в обычной анонимной функции. Для сравнения:

input.map { item => 
  item match {
    case A => 5
    case _ => 10
  }
} // Seq(5, 10, 10)

Он также часто используется для выполнения декомпозиции параметров с использованием сопоставления шаблонов, когда кортеж или класс case передаются функции:

val input = Seq("A" -> 1, "B" -> 2, "C" -> 3)

input.map { case (a, i) =>
   a + i.toString
} // Seq("A1", "B2", "C3")

Использование для извлечения кортежей в функции карты

Эти три функции карты эквивалентны, поэтому используйте вариацию, которую ваша команда считает наиболее читаемой.

val numberNames = Map(1 -> "One", 2 -> "Two", 3 -> "Three")

// 1. No extraction
numberNames.map(it => s"${it._1} is written ${it._2}" )

// 2. Extraction within a normal function
numberNames.map(it => {
    val (number, name) = it
    s"$number is written $name"
})

// 3. Extraction via a partial function (note the brackets in the parentheses)
numberNames.map({ case (number, name) => s"$number is written $name" })

Частичная функция должна соответствовать всем входам : любой случай, который не соответствует, генерирует исключение во время выполнения.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow