web-dev-qa-db-fra.com

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).

195
Amir Karimi

La méthode def even est évaluée à l'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 quand appelé la première fois:

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

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 lorsqu'il est défini.

def évalue chaque appel. Les performances risquent donc d'être pires que val pour plusieurs appels. Vous obtiendrez les mêmes performances avec un seul appel. Et sans appel, vous ne recevez pas de temps système avec def. Vous pouvez donc le définir même si vous ne l'utilisez pas dans certaines succursales.

Avec un lazy val, vous obtiendrez une évaluation paresseuse: vous pourrez le définir même si vous ne l'utilisez pas dans certaines branches. L'évaluation est effectuée une fois ou jamais, mais vous obtiendrez un peu plus de temps de verrouillage en double contrôle sur chaque accès à votre accès. lazy val.

Comme @SargeBorsch l'a noté, vous pouvez définir une méthode. 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 les 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. Les performances risquent donc d'être légèrement inférieures à celles de val.

301
senia

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 = <function1>

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 c'est-à-dire val , la fonction est initialisée une seule fois pendant la déclaration (et par conséquent, elle affiche val à la ligne 4 et plus jamais) et le même résultat est utilisé à chaque accès. 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 sera utilisé à nouveau, la même valeur sera toujours renvoyée.

Vous pouvez également initialiser par la suite x. c'est-à-dire que la première fois qu'il est utilisé, il est initialisé et non pendant la déclaration. Par exemple:

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

scala> y
res4: Int = 323930673

scala> y
res5: Int = 323930673
23
Jatin

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 attribué.
Maintenant, changez val en def .. cela affichera 9! Def est un appel de fonction, il sera évalué à chaque appel.

4
Apurva Singh

val i.e. "sq" correspond à la définition de Scala. Il est évalué juste au moment de la déclaration, vous ne pouvez pas changer plus tard. Dans d’autres exemples, où même2 valait aussi, mais il déclarait avec la signature de fonction, c’est-à-dire "(Int => Boolean)", de sorte que ce n’est pas du type Int. C'est une fonction et sa valeur est définie par l'expression suivante

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

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

Pourquoi appeler la fonction val eval2 ne pas imprimer "val" encore et encore?

Code d'origine:

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

Nous savons que dans Scala, la dernière déclaration du type d’expression ci-dessus (à l’intérieur de {..}) renvoie en réalité au côté gauche. Vous finissez donc par définir la fonction even2 sur la fonction "x => x% 2 == 0", qui correspond au type que vous avez déclaré pour le type even2 val, à savoir (Int => Boolean), ce qui rend le compilateur heureux. Maintenant, even2 pointe uniquement sur la fonction "(x => x% 2 == 0)" (pas n'importe quelle autre instruction avant ie println ("val") etc. Invoquer un événement2 avec des paramètres différents invoquera en fait "(x => x% 2 == 0) "code, car seul celui-ci est enregistré avec event2.

scala> even2(2)
res7: Boolean = true

scala> even2(3)
res8: Boolean = false

Pour clarifier cela davantage, voici une version différente du code.

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

Qu'est-ce qui va arriver? nous voyons ici "inside final fn" imprimé encore et encore, lorsque vous appelez even2 ().

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

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

scala> 
1
Sandi

L'exécution d'une définition telle que def x = e n'évaluera pas l'expression e. Au lieu de cela, e est évalué chaque fois que x est appelé. 

Scala propose également une définition de la valeur val x = e, qui évalue le côté droit dans le cadre de l'évaluation de la définition. Si x est 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 évaluée à nouveau.

1
Gaurav Khare

Outre les 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 paramètres d'argument nul) qui renvoie une autre fonction «Int => Int» lorsqu'elle est invoquée. 

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

0
prateek

Dans REPL,

scala> def even: Int => Boolean = { _% 2 == 0 }
even: Int => Boolean

scala> val even: Int => Boolean = { _% 2 == 0 }
even: Int => Boolean = $$Lambda$1157/1017502292@57a0aeb8

def signifie call-by-name, évalué à la demande

val signifie call-by-value, évalué pendant l'initialisation

0
GraceMeng

aussi, 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 l'évaluation par son nom. Il n'évaluera pas jusqu'à ce qu'il soit utilisé.

0
Sandipan Ghosh