¿Cuál es la diferencia entre "def" y "val" para definir una función


¿Cuál es la diferencia entre:

def even: Int => Boolean = _ % 2 == 0

Y

val even: Int => Boolean = _ % 2 == 0

Ambos pueden ser llamados como even(10).

 185
Author: Amir Karimi, 2013-09-19

7 answers

Método def even evalúa en llamada y crea nueva función cada vez (nueva instancia de Function1).

def even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = false

val even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = true

Con def puede obtener una nueva función en cada llamada:

val test: () => Int = {
  val r = util.Random.nextInt
  () => r
}

test()
// Int = -1049057402
test()
// Int = -1049057402 - same result

def test: () => Int = {
  val r = util.Random.nextInt
  () => r
}

test()
// Int = -240885810
test()
// Int = -1002157461 - new result

val evalúa cuando se define, def - cuando se llama:

scala> val even: Int => Boolean = ???
scala.NotImplementedError: an implementation is missing

scala> def even: Int => Boolean = ???
even: Int => Boolean

scala> even
scala.NotImplementedError: an implementation is missing

Tenga en cuenta que hay una tercera opción: lazy val.

Se evalúa cuando se llama la primera vez:

scala> lazy val even: Int => Boolean = ???
even: Int => Boolean = <lazy>

scala> even
scala.NotImplementedError: an implementation is missing

Pero devuelve el mismo resultado (en este caso la misma instancia de FunctionN) cada tiempo:

lazy val even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = true

lazy val test: () => Int = {
  val r = util.Random.nextInt
  () => r
}

test()
// Int = -1068569869
test()
// Int = -1068569869 - same result

Rendimiento

val evalúa cuando se define.

def evalúa en cada llamada, por lo que el rendimiento podría ser peor que val para múltiples llamadas. Obtendrás el mismo rendimiento con una sola llamada. Y sin llamadas no obtendrá sobrecarga de def, por lo que puede definirlo incluso si no lo usará en algunas ramas.

Con un lazy val obtendrá una evaluación perezosa: puede definirla incluso si no la usará en algunas ramas, y evalúa una vez o nunca, pero obtendrá un poco de sobrecarga de doble comprobación de bloqueo en cada acceso a su lazy val.

Como señaló @SargeBorsch, podría definir el método, y esta es la opción más rápida:

def even(i: Int): Boolean = i % 2 == 0

Pero si necesita una función (no un método) para la composición de la función o para funciones de orden superior (como filter(even)), el compilador generará una función a partir de su método cada vez que lo use como función, por lo que el rendimiento podría ser ligeramente peor que con val.

 289
Author: senia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-25 22:23:11

Considere esto:

scala> def even: (Int => Boolean) = {
             println("def"); 
             (x => x % 2 == 0)
       }
even: Int => Boolean

scala> val even2: (Int => Boolean) = {
             println("val");
             (x => x % 2 == 0)
       }
val //gets printed while declaration. line-4
even2: Int => Boolean = <function1>

scala> even(1)
def
res9: Boolean = false

scala> even2(1)
res10: Boolean = false

¿ves la diferencia? En resumen:

Def : Por cada llamada a even, llama de nuevo al cuerpo del método even. Pero con even2 es decir, val, la función se inicializa solo una vez mientras que la declaración (y por lo tanto imprime val en la línea 4 y nunca más) y la misma salida se utiliza cada vez que se accede. Por ejemplo, intente hacer esto:

scala> import scala.util.Random
import scala.util.Random

scala> val x = { Random.nextInt }
x: Int = -1307706866

scala> x
res0: Int = -1307706866

scala> x
res1: Int = -1307706866

Cuando se inicializa x, el valor devuelto por Random.nextInt se establece como el valor final de x. La próxima vez que se vuelva a usar x, siempre devolverá el mismo valor.

También puedes inicializar perezosamente x. es decir, la primera vez que se utiliza se inicializa y no mientras declaración. Por ejemplo:

scala> lazy val y = { Random.nextInt }
y: Int = <lazy>

scala> y
res4: Int = 323930673

scala> y
res5: Int = 323930673
 23
Author: Jatin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-13 08:48:44

Vea esto:

  var x = 2 // using var as I need to change it to 3 later
  val sq = x*x // evaluates right now
  x = 3 // no effect! sq is already evaluated
  println(sq)

Sorprendentemente, esto imprimirá 4 y no 9! val (incluso var) se evalúa inmediatamente y se asigna.
Ahora cambia val a def.. imprimirá 9! Def es una llamada a una función.. se evaluará cada vez que se llama.

 3
Author: Apurva Singh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-07 20:26:15

Val es decir, "sq" es por definición Scala es fijo. Se evalúa justo en el momento de la declaración, no se puede cambiar más tarde. En otros ejemplos, donde even2 también val, pero se declara con la firma de la función es decir, "(Int = > Boolean)", por lo que no es tipo Int. Es una función y su valor se establece siguiendo la expresión

   {
         println("val");
         (x => x % 2 == 0)
   }

Según la propiedad Scala val, no puede asignar otra función a even2, la misma regla que sq.

Acerca de por qué llamar a la función eval2 val no imprimir "val" de nuevo y otra vez ?

Código original:

val even2: (Int => Boolean) = {
             println("val");
             (x => x % 2 == 0)
       }

Sabemos, en Scala última declaración de arriba tipo de expresión (dentro { .. }) es en realidad volver al lado izquierdo. Así que termina configurando even2 a la función "x = > x % 2 = = 0", que coincide con el tipo que declaró para el tipo val even2, es decir (Int => Booleano), por lo que el compilador está contento. Ahora even2 solo apunta a la función" (x => x % 2 == 0)"(no a ninguna otra instrucción anterior, por ejemplo, println ("val"), etc. Invocar event2 con diferentes parámetros realmente invoque el código "(x => x % 2 == 0)", ya que solo eso se guarda con event2.

scala> even2(2)
res7: Boolean = true

scala> even2(3)
res8: Boolean = false

Solo para aclarar esto más, a continuación hay una versión diferente del código.

scala> val even2: (Int => Boolean) = {
     |              println("val");
     |              (x => { 
     |               println("inside final fn")
     |               x % 2 == 0
     |             })
     |        }

¿Qué pasará ? aquí vemos" inside final fn " impreso una y otra vez, cuando llamas a even2().

scala> even2(3)
inside final fn
res9: Boolean = false

scala> even2(2)
inside final fn
res10: Boolean = true

scala> 
 1
Author: Sandi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-14 04:21:55

También, Val es una evaluación por valor. Lo que significa que la expresión del lado derecho se evalúa durante la definición. Donde Def es por nombre de evaluación. No se evaluará hasta que se use.

 0
Author: Sandipan Ghosh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-20 09:18:57

Ejecutar una definición como def x = e no evaluará la expresión e. En lugar de e se evaluará cada vez que se invoque x.

Alternativamente, Scala ofrece una definición de valor val x = e, que evalúa el lado derecho como parte de la evaluación de la definición. Si x se usa posteriormente, se reemplaza inmediatamente por el valor pre-calculado de e, de modo que la expresión no necesita ser evaluada de nuevo.

 0
Author: Gaurav Khare,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-26 13:08:39

Además de las respuestas útiles anteriores, mis hallazgos son:

def test1: Int => Int = {
x => x
}
--test1: test1[] => Int => Int

def test2(): Int => Int = {
x => x+1
}
--test2: test2[]() => Int => Int

def test3(): Int = 4
--test3: test3[]() => Int

Lo anterior muestra que "def" es un método (con cero parámetros de argumento) que devuelve otra función "Int => Int" cuando se invoca.

La conversión de métodos a funciones está bien explicada aquí: https://tpolecat.github.io/2014/06/09/methods-functions.html

 0
Author: prateek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-11 08:41:21