functions - Nueve formas de definir un método en Scala?
scala function syntax (4)
Así que he estado tratando de descifrar las diversas formas en que puedes definir cosas en Scala, complicado por mi falta de comprensión de la forma en que se tratan {}
bloques:
object NewMain extends Thing{
def f1 = 10
def f2 {10}
def f3 = {10}
def f4() = 10
def f5() {10}
def f6() = {10}
def f7 = () => 10
def f8 = () => {10}
def f9 = {() => {10}}
def main(args: Array[String]){
println(f1) // 10
println(f2) // ()
println(f3) // 10
println(f4) // 10
println(f4()) // 10
println(f5) // ()
println(f5()) // ()
println(f6) // 10
println(f6()) // 10
println(f7) // <function0>
println(f7()) // 10
println(f8) // <function0>
println(f8()) // 10
println(f9) // <function0>
println(f9()) // 10
}
}
Es de suponer que algunos de estos son equivalentes, algunos de estos son azúcar sintáctica para otros, y algunos son cosas que no debo usar, pero no puedo por la vida de mí resolverlo. Mis preguntas específicas son:
¿Cómo es que
println(f2)
eprintln(f5())
danunit
? ¿No es el último artículo en el bloque10
? ¿Cómo es diferente deprintln(f3())
, que da10
?Si
println(f5)
da launit
, ¿no debería serprintln(f5())
, ya que launit
no es una función? Lo mismo se aplica aprintln(f6)
eprintln(f6())
De todos los que imprimen 10:
f1
,f3
,f4
,f4()
,f6
,f6()
,f7()
,f8()
,f9()
, ¿hay alguna diferencia funcional entre ellos (en términos de lo que hace? ) o diferencias de uso (en términos de cuándo debería usar cuál)? ¿O son todos equivalentes?
Para responder a sus preguntas en orden:
-
f2
yf5()
devuelven laUnit
porque scala toma cualquierdef
sin un "=
" para ser una función que devuelve laUnit
, independientemente de cuál sea el último elemento en un bloque. Esto es algo bueno, ya que de lo contrario no sería muy detallado definir una función que no devuelva nada. -
println(f5())
es válido, aunque devuelveUnit
porque en scalaUnit
es un objeto válido, aunque no es una instancia que pueda instanciar.Unit.toString()
es una declaración válida, si no útil en general, por ejemplo. - No todas las versiones que imprimen
10
son iguales. Lo más importante,f7
,f8
yf9
son en realidad funciones que devuelven funciones que devuelven10
, en lugar de devolver10
directamente. Cuando declaradef f8 = () => {10}
, está declarando una funciónf8
que no toma argumentos y devuelve una función que no toma argumentos y devuelve un solo entero. Cuandoprintln(f8)
,f8
te devuelve dilligentemente esa función. Cuando llama aprintln(f8())
devuelve la función, luego la invoca inmediatamente. - Las funciones
f1
,f3
,f4
yf6
son esencialmente equivalentes en términos de lo que hacen, solo varían en términos de estilo.
Como indica "usuario desconocido", las llaves son solo importantes para el alcance y no hacen ninguna diferencia en su caso de uso aquí.
Seis años después, en una futura versión de Scala que se lanzará aún más en el futuro, las cosas han mejorado:
- Las definiciones
f2
yf5
se han eliminado como " sintaxis de procedimiento " - La capacidad de llamar a
f4
f5
yf6
sin parens ha sido eliminada .
Eso trae nuestras 9 formas de definir una función y 15 formas de llamarlas a 7 formas de definir una función y 10 formas de llamarlas:
object NewMain extends Thing{
def f1 = 10
def f3 = {10}
def f4() = 10
def f6() = {10}
def f7 = () => 10
def f8 = () => {10}
def f9 = {() => {10}}
def main(args: Array[String]){
println(f1) // 10
println(f3) // 10
println(f4()) // 10
println(f6()) // 10
println(f7) // <function0>
println(f7()) // 10
println(f8) // <function0>
println(f8()) // 10
println(f9) // <function0>
println(f9()) // 10
}
}
Ver también lampepfl/dotty2570 lampepfl/dotty#2571
Como resultado, es relativamente claro qué sintaxis es opcional (por ejemplo, {}
s) y qué definiciones son equivalentes (por ejemplo, def f4() = 10
y def f7 = () => 10
). Con suerte, algún día cuando se publique Dotty / Scala-3.0, los novatos que aprendan el idioma ya no enfrentarán la misma confusión que yo tuve hace seis años.
def f() {...}
es azúcar sintáctico para
def f(): Unit = {...}
Entonces, si omite el "=", el método siempre devolverá un objeto de tipo Unidad. En Scala, los métodos y expresiones siempre devuelven algo.
def f() = 10
is sytactic sugar for
def f() = {
10
}
Si escribe def f () = () => 10, es lo mismo que escribir
def f() = {
() => 10
}
Entonces eso significa que f está devolviendo un objeto de función. Sin embargo, podrías escribir
val f = () => 10
Cuando llama a eso con f (), devuelve 10 objetos Function y los métodos se pueden usar de forma intercambiable en la mayoría de los casos, pero hay algunas diferencias sintácticas. por ejemplo, cuando escribes
def f() = 10
println(f)
obtienes "10", pero cuando escribes
val f = () => 10
println(f)
usted obtiene
<function0>
Por otro lado, cuando tienes esto
val list = List(1,2,3)
def inc(x: Int) = x+1
val inc2 = (x: Int) => x+1
println(list.map(inc))
println(list.map(inc2))
Ambos imprimirán lo mismo
List(2,3,4)
Cuando utiliza el nombre de un método en un lugar donde se espera un objeto de función y la firma del método coincide con la firma del objeto de función esperado, se convierte automáticamente. Entonces list.map(inc)
se convierte automáticamente por el compilador scala en
list.map(x => inc(x))
def f1 = 10
def f2 {10}
El segundo formulario no usa una asignación. Por lo tanto, puedes pensar en ello como un Procedimiento. No está destinado a devolver algo, y devuelve, por lo tanto, la unidad, incluso si la última declaración podría utilizarse para devolver algo específico (pero podría ser una instrucción if, que solo tendría algo específico en una rama).
def f1 = 10
def f3 = {10}
No necesitas llaves aquí. Los necesita, por ejemplo, si define un valor val, por lo que el alcance de este valor se limita al bloque circundante.
def sqrGtX (n:Int, x: Int) = {
val sqr = n * n
if (sqr > x)
sqr / 2
else x / 2
}
Necesitas las llaves para definir val sqr aquí. Si el val se declara en una rama interna, las llaves no necesitan estar en el nivel superior del método:
def foo (n:Int, x: Int) =
if (n > x) {
val bar = x * x + n * n
println (bar)
bar - 2
} else x - 2
Para una mayor investigación cuando dos métodos devuelven el mismo resultado, puede compilarlos y comparar el bytecode. Dos métodos idénticos binarios serán idénticos.