functions scala methods function

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) e println(f5()) dan unit ? ¿No es el último artículo en el bloque 10 ? ¿Cómo es diferente de println(f3()) , que da 10 ?

  • Si println(f5) da la unit , ¿no debería ser println(f5()) , ya que la unit no es una función? Lo mismo se aplica a println(f6) e println(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 y f5() devuelven la Unit porque scala toma cualquier def sin un " = " para ser una función que devuelve la Unit , 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 devuelve Unit porque en scala Unit 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 y f9 son en realidad funciones que devuelven funciones que devuelven 10 , en lugar de devolver 10 directamente. Cuando declara def f8 = () => {10} , está declarando una función f8 que no toma argumentos y devuelve una función que no toma argumentos y devuelve un solo entero. Cuando println(f8) , f8 te devuelve dilligentemente esa función. Cuando llama a println(f8()) devuelve la función, luego la invoca inmediatamente.
  • Las funciones f1 , f3 , f4 y f6 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:

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.