3 votes

Quelle est la différence entre "Hello" foreach (x = x * _.toLong ) et "Hello" foreach (x *= _.toLong ) en scala ?

Quelle est la différence entre "Hello" foreach (x = x * _.toLong) et "Hello" foreach (x *= _.toLong) en Scala ?

Ça ne marche pas :

scala> var x : Long = 1
x: Long = 1
scala> "Hello" foreach (x = x * _.toLong )
<console>:13: error: missing parameter type for expanded function ((x$1) => x.$times(x$1.toLong))
   "Hello" foreach (x = x * _.toLong )

Travaux :

scala> "Hello" foreach (x *= _.toLong )
scala> xbtebh
res89: Long = 9415087488

4voto

slouc Points 5393

Ceci :

"Hello" foreach (x = x * _.toLong)

est en fait développé par le compilateur en ceci :

"Hello" foreach (x = x * (x$1 => x$1.toLong))

De toute évidence, la multiplication d'un Long avec une fonction anonyme qui invoque toLong() sur son argument n'a pas beaucoup de sens. Bien sûr, écrire soi-même la version expansive fonctionne bien, par exemple "Hello" foreach (y => x = x * y.toLong) .

Dans votre deuxième expression "Hello" foreach (x *= _.toLong ) il y a deux expansions qui doivent être faites par le compilateur : une qui expanse le trait de soulignement comme dans l'exemple précédent et une qui expanse x *= y en x = x * y . Évidemment, le premier se produit avant le second, donc le compilateur voit (x *= _.toLong) comme une expression unique. Ainsi, au lieu de se développer en (x *= (x$1 => x$1.toLong)) il s'étend à x$1 => (x *= x$1.toLong) . Je ne peux pas vraiment pointer du doigt car il faudrait que je creuse dans les spécifications de Scala et les internes du compilateur, mais maintenant vous avez une idée de ce qui cause ce comportement.

Mon conseil personnel est d'utiliser le trait de soulignement uniquement dans des situations triviales, telles que List(1, 2, 3).map(_.toLong) et, dans des situations comme la vôtre, écrivez toujours la fonction complète, par ex. "Hello" foreach (arg => x = x * arg.toLong) .

Notez également que l'utilisation d'effets secondaires et de valeurs mutables est un gros non-sens en Scala. Voici une version améliorée de votre code :

val result = "Hello".foldLeft(1: Long)((x, c) => x * c.toLong)

1voto

flavian Points 11275

La différence est que dans le premier cas vous utilisez l'attribution directe avec = qui échouera évidemment.

Si vous regardez la signature de foreach vous pouvez voir qu'il attend une fonction.

Dans le premier scénario, vous n'en fournissez tout simplement pas, car vous ne respectez pas la syntaxe lambda. La syntaxe lambda du f: A => U attendu par le foreach La fonction est x => x * 5 ou quelque chose de similaire.

Vous ne le faites manifestement pas, la vraie syntaxe serait là :

"Hello" foreach (ch => x = x * ch.toLong )

Dans le second cas, vous utilisez la forme courte lambda, à savoir le fait que col.foreach(x => x + 5) peut être réécrit comme col.foreach(_ + 5) sauf que vous l'inversez pour col.foreach(5 + _) qui est également une forme correcte d'écriture des lambdas.

El _ dans votre deuxième exemple prend correctement la forme du caractère courant de votre chaîne.

Donc, ceci "Hello" foreach (x *= _.toLong ) est en fait "Hello" foreach (ch => x *= ch.toLong ) mais en utilisant la forme abrégée pour le lambda, c'est pourquoi ça fonctionne.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X