Différences entre ces trois façons de définir une fonction dans Scala

92

Étant donné trois manières d'exprimer la même fonction f(a) := a + 1:

val f1 = (a:Int) => a + 1
def f2 = (a:Int) => a + 1
def f3:(Int => Int) = a => a + 1

En quoi ces définitions diffèrent-elles? Le REPL n'indique aucune différence évidente:

scala> f1
res38: (Int) => Int = <function1>
scala> f2
res39: (Int) => Int = <function1>
scala> f3
res40: (Int) => Int = <function1>
qrest
la source
11
Vous devez noter que dans le 2ème bloc ci-dessus, l'évaluation f1dans la REPL montre la valeur liée statiquement f1lors de l'évaluation f2et f3affiche le résultat de l' appel de ces méthodes. En particulier, une nouvelle Function1[Int, Int]instance est produite à chaque fois que f2ou f3est invoquée, alors qu'elle f1est la même Function1[Int, Int]pour toujours.
Randall Schulz
@RandallSchulz étant donné que la version val ne nécessite pas de nouvelle instance de fonction, pourquoi utiliserait-on def dans ce cas?
virtualeyes
2
@virtualeyes La seule situation dont je me souvienne où l'on voit defs donner des valeurs FunctionN [...] est dans la bibliothèque de l'analyseur combinateur. Il n'est pas très courant d'écrire des méthodes qui produisent des fonctions et pratiquement jamais on n'utiliserait une def pour produire de nombreuses copies d'une fonction sémantiquement / fonctionnellement immuable.
Randall Schulz

Réponses:

112

f1 est une fonction qui prend un entier et renvoie un entier.

f2est une méthode avec une arité nulle qui renvoie une fonction qui prend un entier et renvoie un entier. (Lorsque vous tapez f2à REPL plus tard, cela devient un appel à la méthode f2.)

f3est le même que f2. Vous n'employez tout simplement pas l'inférence de type ici.

manquant
la source
6
Pourquoi f1a functionet f2est-ce un method?
Freewind
17
@Freewind, une fonction est un objet avec une méthode nommée apply. Une méthode, eh bien, est une méthode.
missingfaktor
Réponse géniale. Question: vous dites que f2 a une arité nulle, mais n'est-ce pas unaire? en.wikipedia.org/wiki/Arity "Une fonction nullaire ne prend aucun argument. Une fonction unaire prend un argument." Juste curieux!
Matthew Cornell
5
@MatthewCornell, f2lui - même n'accepte aucun argument. L'objet fonction qu'il renvoie le fait.
missingfaktor
122

À l'intérieur d'une classe, valest évalué à l'initialisation tandis que defn'est évalué que lorsque, et à chaque fois , la fonction est appelée. Dans le code ci-dessous, vous verrez que x est évalué la première fois que l'objet est utilisé, mais pas à nouveau lors de l'accès au membre x. En revanche, y n'est pas évalué lorsque l'objet est instancié, mais est évalué à chaque fois que le membre est accédé.

  class A(a: Int) {
    val x = { println("x is set to something"); a }
    def y = { println("y is set to something"); a }
  }

  // Prints: x is set to something
  val a = new A(1)

  // Prints: "1"
  println(a.x)

  // Prints: "1"                               
  println(a.x)

  // Prints: "y is set to something" and "1"                                  
  println(a.y)

  // Prints: "y is set to something" and "1"                                                                                   
  println(a.y)
Jack
la source
@JacobusR est-ce vrai seulement à l'intérieur d'une classe?
Andrew Cassidy
par exemple: scala> var b = 5 b: Int = 5 scala> val a: (Int => Int) = x => x + ba: Int => Int = <function1> scala> a (5) res48: Int = 10 scala> b = 6 b: Int = 6 scala> a (5) res49: Int = 11 Je m'attendais à ce que a (5) renvoie 10 et que la valeur de b ait été insérée
Andrew Cassidy
@AndrewCassidy la fonction aest immuable et évaluée à l'initialisation, mais breste une valeur modifiable. La référence à best donc définie lors de l'initialisation, mais la valeur stockée par breste mutable. Pour le plaisir, vous pouvez maintenant créer un nouveau fichier val b = 123. Après cela, vous a(5)donnerez toujours 11, car la valeur best maintenant complètement nouvelle.
Jack
@JacobusR merci ... cela a du sens. Cela coïncide avec la définition de "portée lexicale" puisque la fonction a porte une référence à "var b" d'origine. Je suppose que ce qui m'a rendu confus, c'est que dire: var b = 5; val c = b; b = 6; agit différemment. Je suppose que je ne devrais pas m'attendre à ce qu'une définition de fonction qui contienne des références à la portée "lexicale" originale se comporte de la même manière qu'un Int.
Andrew Cassidy
3

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 utilisé. Alternativement, Scala propose une définition de valeur val x = e , qui évalue le côté droit e 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 évaluée à nouveau.

Scala par exemple par Martin Odersky

Alexandre
la source