Voici une petite session Scala qui définit et essaie certaines fonctions:
scala> def test1(str: String) = str + str;
test1: (str: String)Java.lang.String
scala> test1("ab")
res0: Java.lang.String = abab
fonctionne bien.
scala> val test2 = test1
<console>:6: error: missing arguments for method test1 in object $iw;
follow this method with `_' if you want to treat it as a partially applied function
val test2 = test1
^
oups.
scala> val test2 = test1 _
test2: (String) => Java.lang.String = <function1>
scala> test2("ab")
res1: Java.lang.String = abab
fonctionne bien!
Maintenant, j'ai vu la syntaxe _
Lors du pliage (_ + _
, Etc.). Donc, si je comprends bien, _
Signifie essentiellement "un argument". Donc test1 _
Signifie essentiellement une fonction avec un argument, qui est donné à test1
". Mais pourquoi n'est-ce pas exactement le même que simplement test1
? Pourquoi y a-t-il une différence si j'ajoute un _
?
J'ai donc continué à explorer ...
scala> val test3 = (str: String) => str + str
test3: (String) => Java.lang.String = <function1>
scala> test3("ab")
res2: Java.lang.String = abab
scala> val test4 = test3
test4: (String) => Java.lang.String = <function1>
Ici, cela fonctionne sans _
! Quelle est la différence entre une fonction def
ed et une fonction val
ed?
Il n'y a pas de différence entre une fonction définie et une fonction définie:
scala> def test1 = (str: String) => str + str
test1: (String) => Java.lang.String
scala> val test2 = test1
test2: (String) => Java.lang.String = <function1>
scala> val test3 = (str: String) => str + str
test3: (String) => Java.lang.String = <function1>
scala> val test4 = test2
test4: (String) => Java.lang.String = <function1>
Voir? Ce sont toutes des fonctions, ce qui est indiqué par le X => Y
type qu'ils ont.
scala> def test5(str: String) = str + str
test5: (str: String)Java.lang.String
Voyez-vous un X => Y
taper? Si vous le faites, allez voir un ophtalmologiste, car il n'y en a pas. Le type ici est (X)Y
, couramment utilisé pour désigner une méthode.
Réellement, test1
, test2
, test3
et test4
sont toutes les méthodes qui renvoient des fonctions. test5
est une méthode qui renvoie un Java.lang.String
. Aussi, test1
par test4
ne prend pas de paramètres (seulement test1
pourrait, de toute façon), tandis que test5
Est-ce que.
Donc, la différence est assez simple. Dans le premier cas, vous avez essayé d'affecter une méthode à un val, mais vous n'avez pas renseigné les paramètres pris par la méthode. Il a donc échoué, jusqu'à ce que vous ajoutiez un trait de soulignement de fin, ce qui signifiait transformer ma méthode en fonction.
Dans le deuxième exemple, vous aviez une fonction, vous n'aviez donc pas besoin de faire autre chose.
Une méthode n'est pas une fonction, et vice versa. Une fonction est un objet d'une des classes FunctionN
. Une méthode est une poignée pour un morceau de code associé à un objet.
Voir diverses questions sur les méthodes et les fonctions sur Stack Overflow.
def
déclare une méthode dans un objet/classe/trait environnant, similaire à la façon dont vous définissez les méthodes en Java. Vous ne pouvez utiliser que def
s dans d'autres objets/classes/traits. Dans le REPL, vous ne pouvez pas voir l'objet environnant car il est "caché", mais il existe.
Vous ne pouvez pas affecter un def
à une valeur, car le def
n'est pas une valeur - c'est une méthode dans l'objet.
Le (x: T) => x * x
Déclare et instancie un objet fonction, qui existe au moment de l'exécution. Les objets fonction sont des instances de classes anonymes qui étendent les traits FunctionN
. Les traits FunctionN
sont fournis avec une méthode apply
. Le nom apply
est spécial, car il peut être omis. L'expression f(x)
est supprimée en f.apply(x)
.
L'essentiel est - puisque les objets fonction sont des valeurs d'exécution qui existent sur le tas, vous pouvez les affecter à des valeurs, des variables et des paramètres, ou les renvoyer des méthodes en tant que valeurs de retour.
Pour résoudre le problème d'affectation de méthodes à des valeurs (ce qui peut être utile), Scala vous permet d'utiliser le caractère d'espace réservé pour créer un objet fonction à partir d'une méthode. Expression test1 _
Dans votre exemple ci-dessus crée en fait une fonction wrapper autour de la méthode test1
- elle est équivalente à x => test1(x)
.
Le trait de soulignement signifie différentes choses dans différents contextes. Mais cela peut toujours être considéré comme la chose qui irait ici, mais n'a pas besoin d'être nommée.
Lorsqu'il est appliqué à la place des paramètres, l'effet est de soulever la méthode à une fonction.
scala> def test1(str: String) = str + str;
test1: (str: String)Java.lang.String
scala> val f1 = test1 _
f1: (String) => Java.lang.String = <function1>
Notez que la méthode est devenue une fonction de type (String) => String.
La distinction entre une méthode et une fonction dans Scala est que les méthodes s'apparentent aux méthodes traditionnelles Java méthodes. Vous ne pouvez pas les transmettre en tant que valeurs. Cependant, les fonctions sont des valeurs à part entière et peuvent être utilisées comme paramètres d'entrée et valeurs de retour.
Le levage peut aller plus loin:
scala> val f2 = f1 _
f2: () => (String) => Java.lang.String = <function0>
La levée de cette fonction entraîne une autre fonction. Cette fois de type () => (String) => (String)
D'après ce que je peux dire, cette syntaxe équivaut à remplacer explicitement tous les paramètres par un trait de soulignement. Par exemple:
scala> def add(i: Int, j: Int) = i + j
add: (i: Int,j: Int)Int
scala> val addF = add(_, _)
addF: (Int, Int) => Int = <function2>
scala> val addF2 = add _
addF2: (Int, Int) => Int = <function2>