Buscar..


Composición

Las funciones parciales se usan a menudo para definir una función total en partes:

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)

En este uso, las funciones parciales se intentan en orden de concatenación con el método orElse . Normalmente, se proporciona una función parcial final que coincide con todos los casos restantes. En conjunto, la combinación de estas funciones actúa como una función total.

Este patrón se usa normalmente para separar las preocupaciones cuando una función puede actuar efectivamente como un despachador para rutas de código dispares. Esto es común, por ejemplo, en el método de recepción de un Actor Akka .

Uso con `collect`

Si bien la función parcial se usa a menudo como una sintaxis conveniente para funciones totales, al incluir una coincidencia de comodín final ( case _ ), en algunos métodos, su parcialidad es clave. Un ejemplo muy común en Scala idiomático es el método de collect , definido en la biblioteca de colecciones de Scala. Aquí, las funciones parciales permiten que las funciones comunes de examinar los elementos de una colección para mapearlos y / o filtrarlos ocurran en una sintaxis compacta.

Ejemplo 1

Suponiendo que tenemos una función de raíz cuadrada definida como función parcial:

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

Podemos invocarlo con el combinador de collect :

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

Realizando efectivamente la misma operación que:

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

Ejemplo 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")

Hay varias cosas a tener en cuenta en el ejemplo anterior:

  • El lado izquierdo de cada coincidencia de patrón selecciona efectivamente los elementos para procesar e incluir en la salida. Cualquier valor que no tenga un case coincidente simplemente se omite.
  • El lado derecho define el procesamiento específico de caso para aplicar.
  • La coincidencia de patrones se une a la variable para su uso en declaraciones de guarda (las cláusulas if ) y el lado derecho.

Sintaxis basica

Scala tiene un tipo especial de función llamada función parcial , que se extiende a las funciones normales , lo que significa que una instancia de PartialFunction se puede usar donde se espera que Function1 . Las funciones parciales se pueden definir de forma anónima utilizando la sintaxis de case también se usa en la coincidencia de patrones :

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)

Como se ve en el ejemplo, una función parcial no necesita definirse en todo el dominio de su primer parámetro. Se supone que una instancia de Function1 estándar es total , lo que significa que se define para cada argumento posible.

Uso como una función total

Las funciones parciales son muy comunes en Scala idiomático. A menudo se usan por su conveniente sintaxis basada en case para definir funciones totales sobre rasgos :

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)

Esto guarda la sintaxis adicional de una declaración de match en una función anónima regular. Comparar:

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

También se utiliza con frecuencia para realizar una descomposición de parámetros utilizando la coincidencia de patrones, cuando se pasa una tupla o una clase de caso a una función:

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

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

Uso para extraer tuplas en una función de mapa

Estas tres funciones de mapa son equivalentes, por lo tanto, use la variación que su equipo encuentre más legible.

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" })

La función parcial debe coincidir con todas las entradas : cualquier caso que no coincida producirá una excepción en el tiempo de ejecución.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow