por - Uso de funciones parciales en Scala: ¿cómo funciona?
integrales integracion por fracciones parciales (2)
Soy nuevo en Scala, estoy usando 2.9.1, y estoy tratando de entender cómo usar las funciones parciales. Tengo una comprensión básica de las funciones curried, y sé que las funciones parciales son algo así como funciones curried donde solo son 2nary o algo así. Como puedes ver, soy un poco verde en esto.
Parece que en ciertos casos, como el filtrado XML, la capacidad de realizar funciones parciales sería muy ventajosa, así que espero obtener una mejor comprensión de cómo usarlas.
Tengo una función que usa la estructura RewriteRule, pero necesito que funcione con dos argumentos, mientras que la estructura RewriteRule solo toma una, O una función parcial. Creo que este es uno de los casos en los que pienso que es útil.
Cualquier consejo, enlaces, palabras de sabiduría, etc. bienvenidos!
Las respuestas hasta ahora son excelentes, y han aclarado algunos conceptos erróneos fundamentales que tengo. Creo que también explican dónde estoy luchando: creo que quizás sea útil publicar una nueva pregunta un poco más específica, así que también lo haré.
La explicación de Rex Kerr es muy buena, y tampoco sorprende. La pregunta es claramente mezclar funciones parciales con funciones parcialmente aplicadas . Por lo que sea, hice la misma confusión cuando aprendí a Scala.
Sin embargo, dado que la pregunta llama la atención sobre funciones parciales, me gustaría hablar un poco de ellas.
Mucha gente dice que las funciones parciales son funciones que no están definidas para todas las entradas, y eso es cierto para las matemáticas, pero no para Scala. En Scala, una función no puede definirse para todas las entradas tampoco. De hecho, dado que la función parcial hereda de la función, entonces la función también incluye todas las funciones parciales, lo que la hace inevitable.
Otros mencionan el método isDefinedAt
, que es, de hecho, una diferencia, pero principalmente sobre la implementación. Eso es tan cierto que Scala 2.10 probablemente se lanzará con una "función parcial rápida", que no depende de isDefinedAt
.
Y algunas personas incluso implican que el método de apply
para funciones parciales hace algo diferente al método de apply
para funciones, como solo ejecutar para la entrada que se define, que no podría estar más lejos de la verdad. El método de apply
es exactamente el mismo .
A qué funciones parciales realmente se reduce es a otro método: orElse
. Eso resume todos los casos de uso para funciones parciales mucho mejor que isDefinedAt
, porque las funciones parciales se tratan realmente de hacer una de las siguientes cosas:
- Encadenamiento de funciones parciales (que es lo que
orElse
hace), de modo que se probará una entrada en cada función parcial hasta que una de ellas coincida. - Hacer algo diferente si una función parcial no coincide, en lugar de lanzar una excepción, que es lo que sucedería si encadenas esa cosa diferente usando
orElse
.
No digo que todo se pueda implementar fácilmente en términos de orElse
, fíjate. Solo digo que las funciones parciales consisten en hacer algo diferente cuando una entrada no está definida para él.
Una función parcial es una función que es válida solo para un subconjunto de valores de los tipos que puede ingresar. Por ejemplo:
val root: PartialFunction[Double,Double] = {
case d if (d >= 0) => math.sqrt(d)
}
scala> root.isDefinedAt(-1)
res0: Boolean = false
scala> root(3)
res1: Double = 1.7320508075688772
Esto es útil cuando tiene algo que sabe cómo verificar si una función está definida o no. Recoger, por ejemplo:
scala> List(0.5, -0.2, 4).collect(root) // List of _only roots which are defined_
res2: List[Double] = List(0.7071067811865476, 2.0)
Esto no te ayudará a colocar dos argumentos donde realmente quieres uno.
Por el contrario, una función parcialmente aplicada es una función donde algunos de sus argumentos ya se han completado.
def add(i: Int, j: Int) = i + j
val add5 = add(_: Int,5)
Ahora solo necesitas un argumento: el de agregar 5 a - en lugar de dos:
scala> add5(2)
res3: Int = 7
Puede ver en este ejemplo cómo usarlo.
Pero si necesita especificar esos dos argumentos, esto aún no lo hará; digamos que quiere usar el map
, por ejemplo, y necesita darle una función de un argumento, pero quiere que agregue dos cosas diferentes . Bueno, entonces puedes
val addTupled = (add _).tupled
que aplicará parcialmente la función (realmente, simplemente crea una función fuera del método, ya que no se ha completado nada) y luego combina los argumentos por separado en una tupla. Ahora puede usar esto en lugares que requieren un único argumento (suponiendo que el tipo sea correcto):
scala> List((1,2), (4,5), (3,8)).map(addTupled)
res4: List[Int] = List(3, 9, 11)
Por el contrario, currying es diferente una vez más; convierte las funciones de la forma (A,B) => C
en A => B => C
Es decir, dada una función de argumentos múltiples, producirá una cadena de funciones que cada una toma un argumento y devuelve una cadena más corta (se puede pensar que se aplica parcialmente un argumento a la vez).
val addCurried = (add _).curried
scala> List(1,4,3).map(addCurried)
res5: List[Int => Int] = List(<function1>, <function1>, <function1>)
scala> res5.head(2) // is the first function, should add 1
res6: Int = 3
scala> res5.tail.head(5) // Second function should add 4
res7: Int = 9
scala> res5.last(8) // Third function should add 3
res8: Int = 11