Quelle est la différence entre «def» et «val» pour définir une fonction

Quelle est la différence entre:

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

et

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

Les deux peuvent être appelés comme even(10) .

La méthode def even évalue def even appel et crée une nouvelle fonction à chaque fois (nouvelle instance de Function1 ).

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

Avec def vous pouvez obtenir une nouvelle fonction à chaque appel:

 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 évalue lorsqu’il est défini, def – lorsqu’il est appelé:

 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 

Notez qu’il existe une troisième option: lazy val .

Il évalue lorsqu’il est appelé la première fois:

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

Mais renvoie le même résultat (dans ce cas la même instance de FunctionN ) à chaque fois:

 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 

Performance

val évalue une fois défini.

def évalue chaque appel, de sorte que la performance peut être pire que val pour plusieurs appels. Vous obtiendrez les mêmes performances avec un seul appel. Et avec aucun appel, vous n’aurez aucune surcharge de la part de def , vous pouvez donc le définir même si vous ne l’utiliserez pas dans certaines twigs.

Avec une valeur lazy val vous obtenez une évaluation paresseuse: vous pouvez la définir même si vous ne l’utilisez pas dans certaines twigs, et l’évalue une fois ou jamais, mais vous obtiendrez un peu de temps lors du double contrôle du locking sur chaque access. ton lazy val .

Comme @SargeBorsch a noté que vous pourriez définir la méthode, et c’est l’option la plus rapide:

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

Mais si vous avez besoin d’une fonction (pas de méthode) pour la composition de fonctions ou pour des fonctions d’ordre supérieur (comme filter(even) ), le compilateur générera une fonction à partir de votre méthode chaque fois que vous l’utiliserez comme fonction. val .

Considère ceci:

 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 =  scala> even(1) def res9: Boolean = false scala> even2(1) res10: Boolean = false 

Voyez-vous la différence? En bref:

def : Pour chaque appel à even , il appelle à nouveau le corps de la méthode even . Mais avec even2 ie val , la fonction n’est initialisée qu’une seule fois pendant que la déclaration (et donc elle affiche val à la ligne 4 et plus jamais) et la même sortie est utilisée à chaque fois qu’elle accède. Par exemple essayez ceci:

 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 

Lorsque x est initialisé, la valeur renvoyée par Random.nextInt est définie comme la valeur finale de x . La prochaine fois que x est utilisé à nouveau, il retournera toujours la même valeur.

Vous pouvez également initialiser paresseusement x . C’est-à-dire que la première fois qu’il est utilisé, il est initialisé et non pas pendant la déclaration. Par exemple:

 scala> lazy val y = { Random.nextInt } y: Int =  scala> y res4: Int = 323930673 scala> y res5: Int = 323930673 

Regarde ça:

  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) 

Étonnamment, cela imprimera 4 et non 9! val (même var) est évalué immédiatement et assigné.
Maintenant changez val pour def .. il imprimera 9! Def est un appel de fonction. Il sera évalué à chaque appel.

val ie “sq” est par définition Scala est fixe. Il est évalué juste au moment de la déclaration, vous ne pouvez pas changer plus tard. Dans d’autres exemples, où even2 valait aussi, mais il a déclaré avec la signature de la fonction, par exemple “(Int => Boolean)”, donc ce n’est pas le type Int. C’est une fonction et sa valeur est définie par l’expression suivante

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

Comme pour la propriété Scala val, vous ne pouvez pas affecter une autre fonction à even2, même règle que sq.

Pourquoi la fonction eval2 val n’imprime pas “val” encore et encore?

Code d’origine:

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

Nous soaps que dans Scala, la dernière expression du type d’expression ci-dessus (à l’intérieur de {..}) est en fait retournée à gauche. Donc, vous finissez par définir even2 à la fonction “x => x% 2 == 0”, qui correspond au type que vous avez déclaré pour le même type val, c.-à-d. Désormais, even2 ne pointe que vers la fonction “(x => x% 2 == 0)” (pas d’autre instruction avant ie println (“val”), etc. Invoquer event2 avec des parameters différents invoquera réellement “(x => x% 2 == 0) “code, comme seul qui est enregistré avec event2.

 scala> even2(2) res7: Boolean = true scala> even2(3) res8: Boolean = false 

Juste pour clarifier cela, voici une version différente du code.

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

Que va-t-il se passer? nous voyons ici “inside final fn” imprimé encore et encore, quand vous appelez even2 ().

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

De plus, Val est une évaluation par valeur. Ce qui signifie que l’expression de droite est évaluée lors de la définition. Où Def est par nom évaluation. Il n’évaluera pas jusqu’à ce qu’il soit utilisé.

L’exécution d’une définition telle que def x = e n’évaluera pas l’expression e. Au lieu de cela, on évalue chaque fois que x est invoqué.

Scala propose également une définition de valeur val x = e , qui évalue la partie droite dans le cadre de l’évaluation de la définition. Si x est ensuite utilisé par la suite, il est immédiatement remplacé par la valeur pré-calculée de e, de sorte que l’expression n’a pas besoin d’être réévaluée.

En plus des réponses utiles ci-dessus, mes conclusions sont les suivantes:

 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 

Ce qui précède montre que «def» est une méthode (avec des parameters d’argument zéro) qui renvoie une autre fonction «Int => Int» lorsqu’elle est appelée.

La conversion des méthodes en fonctions est bien expliquée ici: https://tpolecat.github.io/2014/06/09/methods-functions.html