scala - name - ¿El diseño de PartialFunction es ineficiente?
metatags (1)
Hay una conversación sobre esto ahora mismo en la lista de correo de scala-internals . Martin Odersky ha sugerido un nuevo tipo: FunctionWithDefault
. Martin habla no solo de una penalización de tiempo de ejecución, sino también de una penalización en tiempo de compilación (de hinchazón de archivo de clase) al usar PartialFunction
:
Primero, necesitamos generar el código de coincidencia de patrón dos veces, una vez en la aplicación y luego otra vez en isDefinedAt. Segundo, también necesitamos ejecutar el código dos veces, primero para probar si la función es aplicable, y luego para aplicarla realmente.
La respuesta a su pregunta es esencialmente "sí" y este comportamiento (de PartialFunction
) no cambiará debido a problemas de compatibilidad con versiones anteriores (por ejemplo, ¿qué isDefinedAt
si isDefinedAt
tiene efecto secundario?).
El nuevo tipo propuesto, FunctionWithDefault
no tiene isDefinedAt
y tiene un método:
trait FunctionWithDefault[-I, +O] {
def applyOrElse[OO >: O](i : I, default : I => OO) : OO
}
que actúa un poco como el método de Option
s getOrElse
.
Tengo que decir que, como de costumbre, no puedo imaginar que esta ineficiencia presente algún tipo de problema de rendimiento en la abrumadora mayoría de los casos.
Esto es algo que me he preguntado por un tiempo. Veo este patrón mucho:
if (pf.isDefinedAt(in)) pf(in)
Al dividir esto en dos llamadas separadas, todos los patrones que se evaluaron en #isDefinedAt también se evalúan en #apply. Por ejemplo:
object Ex1 {
def unapply(in: Int) : Option[String] = {
println("Ex1")
if (in == 1) Some("1") else None
}
}
object Ex2 {
def unapply(in: Int) : Option[String] = {
println("Ex2")
if (in == 2) Some("2") else None
}
}
val pf : PartialFunction[Int,String] = {
case Ex1(result) => result
case Ex2(result) => result
}
val in = 2
if (pf.isDefinedAt(in)) pf(in)
Qué impresiones
Ex1
Ex2
Ex1
Ex2
res52: Any = 2
En el peor de los casos, donde el patrón coincide con el último, ha evaluado sus patrones / extractores dos veces cuando llama a una función parcial. Esto podría volverse ineficiente cuando se combina con extractores personalizados que hicieron más que una simple coincidencia de patrón de clase o lista (por ejemplo, si tenía un extractor que analizaba un documento XML y devolvía algunos objetos de valor)
PartialFunction # lift sufre de la misma doble evaluación:
scala> pf.lift(2)
Ex1
Ex2
Ex1
Ex2
res55: Option[String] = Some(2)
¿Hay alguna manera de llamar condicionalmente una función si está definida sin llamar potencialmente a todos sus extractores dos veces?