function - orden - Dos formas de definir funciones en Scala. ¿Cuál es la diferencia?
funciones de orden superior scala (3)
Aquí hay una pequeña sesión de Scala que define y prueba algunas funciones:
scala> def test1(str: String) = str + str;
test1: (str: String)java.lang.String
scala> test1("ab")
res0: java.lang.String = abab
funciona bien
scala> val test2 = test1
<console>:6: error: missing arguments for method test1 in object $iw;
follow this method with `_'' if you want to treat it as a partially applied function
val test2 = test1
^
Uy.
scala> val test2 = test1 _
test2: (String) => java.lang.String = <function1>
scala> test2("ab")
res1: java.lang.String = abab
¡funciona bien!
Ahora, he visto la sintaxis _
al plegar ( _ + _
, etc.). Entonces, según lo entiendo, básicamente significa "un argumento". Así que test1 _
básicamente significa una función con un argumento, que se da a test1
". ¿Pero por qué no es exactamente lo mismo que test1
? ¿Por qué hay una diferencia si agrego un _
?
Así que seguí explorando ...
scala> val test3 = (str: String) => str + str
test3: (String) => java.lang.String = <function1>
scala> test3("ab")
res2: java.lang.String = abab
scala> val test4 = test3
test4: (String) => java.lang.String = <function1>
Aquí funciona sin _
! ¿Cuál es la diferencia entre una función def
ed y una función val
ed?
El guión bajo significa diferentes cosas en diferentes contextos. Pero siempre se puede pensar que es lo que iría aquí, pero no necesita ser nombrado .
Cuando se aplica en lugar de parámetros, el efecto es elevar el método a una función.
scala> def test1(str: String) = str + str;
test1: (str: String)java.lang.String
scala> val f1 = test1 _
f1: (String) => java.lang.String = <function1>
Tenga en cuenta que el método se ha convertido en una función de tipo (String) => String.
La distinción entre un método y una función en Scala es que los métodos son similares a los métodos tradicionales de Java. No puedes pasarlos como valores. Sin embargo, las funciones son valores por derecho propio y se pueden usar como parámetros de entrada y valores de retorno.
El levantamiento puede ir más allá:
scala> val f2 = f1 _
f2: () => (String) => java.lang.String = <function0>
Al levantar esta función, se obtiene otra función. Esta vez de tipo () => (String) => (String)
Por lo que puedo decir, esta sintaxis es equivalente a sustituir todos los parámetros con un guión bajo explícitamente. Por ejemplo:
scala> def add(i: Int, j: Int) = i + j
add: (i: Int,j: Int)Int
scala> val addF = add(_, _)
addF: (Int, Int) => Int = <function2>
scala> val addF2 = add _
addF2: (Int, Int) => Int = <function2>
La def
declara un método dentro de un objeto / clase / rasgo circundante, similar a la forma en que defines los métodos en Java. Solo puede usar def
s dentro de otros objetos / clases / rasgos. En el REPL, no puede ver el objeto circundante porque está "oculto", pero sí existe.
No puede asignar una def
a un valor, porque la def
no es un valor; es un método en el objeto.
El (x: T) => x * x
declara y crea una instancia de un objeto de función , que existe en tiempo de ejecución. Los objetos de función son instancias de clases anónimas que extienden rasgos de FunctionN
. FunctionN
rasgos de FunctionN
vienen con un método de apply
. El nombre apply
es especial, porque puede ser omitido. La expresión f(x)
se f.apply(x)
en f.apply(x)
.
La línea de fondo es que, dado que los objetos de función son valores de tiempo de ejecución que existen en el montón, puede asignarlos a valores, variables y parámetros, o devolverlos de métodos como valores de retorno.
Para resolver el problema de asignar métodos a valores (que pueden ser útiles), Scala le permite usar el carácter de marcador de posición para crear un objeto de función a partir de un método. Expression test1 _
en su ejemplo anterior realmente crea una función de envoltura alrededor del método test1
- es equivalente a x => test1(x)
.
No hay diferencia entre una función definida y una función valuada:
scala> def test1 = (str: String) => str + str
test1: (String) => java.lang.String
scala> val test2 = test1
test2: (String) => java.lang.String = <function1>
scala> val test3 = (str: String) => str + str
test3: (String) => java.lang.String = <function1>
scala> val test4 = test2
test4: (String) => java.lang.String = <function1>
¿Ver? Todas estas son funciones, que se indican con el tipo X => Y
que tienen.
scala> def test5(str: String) = str + str
test5: (str: String)java.lang.String
¿Ves un tipo X => Y
? Si lo hace, vaya a ver a un oftalmólogo, porque no hay ninguno. El tipo aquí es (X)Y
, comúnmente usado para denotar un método .
En realidad, test1
, test2
, test3
y test4
son todos métodos que devuelven funciones. test5
es un método que devuelve un java.lang.String
. Además, test1
a test4
no toman parámetros (solo test1
podría, de todos modos), mientras test5
hace.
Entonces, la diferencia es bastante simple. En el primer caso, trataste de asignar un método a un val, pero no completó los parámetros que tomó el método. Así que falló, hasta que agregó un guión bajo final, lo que significaba que convertiría mi método en una función .
En el segundo ejemplo, usted tenía una función, por lo que no tenía que hacer nada más.
Un método no es una función, y viceversa. Una función es un objeto de una de las clases de FunctionN
. Un método es un identificador de algún código asociado a un objeto.
Vea varias preguntas sobre métodos vs funciones en .