2011-12-28 16 views
47

Soy nuevo en Scala, estoy usando 2.9.1, y estoy tratando de entender cómo usar 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.Uso de funciones parciales en Scala: ¿cómo funciona?

Parece que en ciertos casos, como el filtrado XML, la capacidad de funciones parciales sería muy ventajosa, por lo que espero obtener una mejor comprensión de cómo usarlas.

Tengo una función que usa la estructura RewriteRule, pero necesito que trabaje 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.

¡Todos los consejos, 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é.

+4

Supongo que está hablando de la función parcialmente aplicada, es decir, convertir '(arg1, arg2 => result' en' arg2 => result' mediante el suministro de un 'arg1'? Porque" función parcial "puede significar algo bastante diferente , y ese algo (por desgracia, en este caso) también está disponible en Scala, con el mismo nombre. Además, ¿cuál es tu pregunta? El título contiene un signo de interrogación, pero el cuerpo de tu publicación realmente no encaja. – delnan

+0

En orden para ser claro, sugiero 'PartialFunction' en' Scala' rename to 'HalfFunction' :). – ssj

Respuesta

133

Una función parcial es una función que es válida solo para un subconjunto de valores de esos tipos que puede pasarle. 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 comprobar si una función está definida o no. Recoge, 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) 

Ésta es no va a ayudarle a colocar dos argumentos donde realmente se desea.

el contrario, una función aplicada parcialmente es una función donde algunos de sus argumentos ya han sido rellenado

def add(i: Int, j: Int) = i + j 
val add5 = add(_: Int,5) 

Ahora sólo necesita un argumento -. Lo que hay que añadir 5 a-- en lugar de dos:

scala> add5(2) 
res3: Int = 7 

Puede ver en este ejemplo cómo usarlo.

Pero si es necesario especificar esos dos argumentos, este todavía no lo hará - que usted quiere utilizar map, por ejemplo, y hay que darle una función de un argumento, pero que quieren es para agregar dos cosas diferentes. Bueno, a continuación, puede

val addTupled = (add _).tupled 

que se aplicará parcialmente la función (en realidad, basta con crear una función fuera del método, puesto que nada ha sido rellenado) y luego combinar los argumentos separados en una tupla. Ahora usted puede utilizar esto en lugares que requieren un único argumento (suponiendo que el tipo es correcta):

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 del formulario (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 
+0

¡gran explicación! ¡Deberías escribir un libro sobre Scala! :-) – cfischer

35

La explicación de Rex Kerr es muy buena, y no es ninguna sorpresa. La pregunta está claramente mezclando 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 las funciones parciales, me gustaría hablar un poco de ellas.

Muchas personas dicen 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 sobre todo acerca de 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 apply para funciones parciales hace algo diferente que el método apply para funciones, como solo ejecutar para la entrada que está definida, que no podría estar más lejos de la verdad. El método 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 las funciones parciales mucho mejor que isDefinedAt, porque las funciones parciales son realmente de hacer una de las siguientes cosas:

  • encadenamiento de funciones parciales (que es lo orElse hace), por lo que una entrada será probado en cada función parcial hasta que uno de ellos 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 encadena esa cosa diferente usando orElse.

No estoy diciendo que todo puede ser fácilmente implementado en términos de orElse, que conste. Solo digo que las funciones parciales consisten en hacer algo diferente cuando una entrada no está definida para él.