29 votes

Scala foreach étrange comportement

Je veux parcourir une liste de valeurs en utilisant un beau one-liner à Scala.

Par exemple, celui-ci fonctionne bien:

 scala> val x = List(1,2,3,4)
x: List[Int] = List(1, 2, 3, 4)

scala> x foreach println
1
2
3
4
 

Mais si j'utilise l'espace réservé _ , cela me donne une erreur:

 scala> x foreach println(_ + 1)
<console>:6: error: missing parameter type for expanded function ((x$1) =>x$1.$plus(1))
       x foreach println(_ + 1)
                         ^
 

Pourquoi donc? Le compilateur ne peut pas déduire le type ici?

51voto

Daniel C. Sobral Points 159554

Ce:

x foreach println(_ + 1)

est équivalent à ceci:

x.foreach(println(x$1 => x$1 + 1))

Il n'y a aucune indication quant à ce qui pourrait être le type d' x$1, et, pour être honnête, il n'a pas de sens pour imprimer une fonction.

Vous avez évidemment (pour moi) destiné à imprimer x$0 + 1x$0 serait l'paramètre passé par foreach, au lieu de cela. Mais, prenons l'... foreach , comme un paramètre, une Function1[T, Unit]T est le paramètre de type de la liste. Ce que vous êtes de passage à foreach plutôt println(_ + 1), ce qui est une expression qui renvoie Unit.

Si vous avez écrit, au lieu x foreach println, vous seriez de passage est une chose complètement différente. Vous seriez de passage de la fonction(*) println, qui prend en Any et retours Unit, le montage, par conséquent, les exigences de l' foreach.

Cela devient un peu confus à cause des règles de l'expansion de l' _. Il s'étend à l'intime délimiteur d'expression (entre parenthèses ou accolades), sauf s'ils sont à la place d'un paramètre, dans ce cas il signifie autre chose: fonction partielle de l'application.

Pour expliquer ce mieux, regardez ces exemples:

def f(a: Int, b: Int, c: Int) = a + b + c
val g: Int => Int = f(_, 2, 3) // Partial function application
g(1)

Ici, on applique la deuxième et troisième arguments au f, et est retourné une fonction nécessitant juste le restant de l'argument. Notez qu'il n'a travaillé que c'est parce que j'indiqué le type d' g, sinon je serais obligé d'indiquer le type de l'argument que je n'étais pas l'application. Nous allons continuer:

val h: Int => Int = _ + 1 // Anonymous function, expands to (x$1: Int => x$1 + 1)
val i: Int => Int = (_ + 1) // Same thing, because the parenthesis are dropped here
val j: Int => Int = 1 + (_ + 1) // doesn't work, because it expands to 1 + (x$1 => x$1 + 1), so it misses the type of `x$1`
val k: Int => Int = 1 + ((_: Int) + 1) // doesn't work, because it expands to 1 + (x$1: Int => x$1 + 1), so you are adding a function to an `Int`, but this operation doesn't exist

Laissez discuter k plus en détail, parce que c'est un point très important. Rappelons qu' g est une fonction Int => Int, droite? Donc, si je ont été de type 1 + g, aurait aucun sens? C'est ce qui a été fait en k.

Ce qui confond les gens, c'est que ce qu'ils voulaient vraiment était:

val j: Int => Int = x$1 => 1 + (x$1 + 1)

En d'autres termes, ils veulent l' x$1 remplaçant _ de sauter à l'extérieur de la parenthèse, et à l'endroit approprié. Le problème ici est que, même s'il peut sembler évident pour eux l'endroit approprié à l'est, il n'est pas évident pour le compilateur. Considérons cet exemple, par exemple:

def findKeywords(keywords: List[String], sentence: List[String]) = sentence.filter(keywords contains _.map(_.toLowerCase))

Maintenant, si nous étions à l'étendre cela à l'extérieur de la parenthèse, on obtient ceci:

def findKeywords(keywords: List[String], sentence: List[String]) = (x$1, x$2) => sentence.filter(keywords contains x$1.map(x$2.toLowerCase))

Qui n'est certainement pas ce que nous voulons. En fait, si l' _ n'ont pas obtenu délimitée par le plus intime délimiteur d'expression, on ne pourrait jamais l'utiliser _ avec imbriqué map, flatMap, filter et foreach.

Maintenant, retour à la confusion entre la fonction anonyme et l'application partielle, regardez ici:

List(1,2,3,4) foreach println(_) // doesn't work
List(1,2,3,4) foreach (println(_)) // works
List(1,2,3,4) foreach (println(_ + 1)) // doesn't work

La première ligne ne fonctionne pas en raison de la façon dont l'opération de notation fonctionne. Scala voit juste qu' println retours Unit, ce qui n'est pas ce qu' foreachattend.

La deuxième ligne fonctionne parce que la parenthèse laisser Scala évaluer println(_) ensemble. C'est une fonction partielle de l'application, de sorte qu'il retourne Any => Unit, ce qui est acceptable.

La troisième ligne ne fonctionne pas parce qu' _ + 1 est fonction anonyme, qui vous passant comme paramètre d' println. Vous êtes de ne pas faire de println le cadre d'une fonction anonyme, qui est ce que tu voulais.

Enfin, ce que peu de gens s'attendre à:

List(1,2,3,4) foreach (Console println _ + 1)

Les travaux de cette. Pourquoi il ne est laissé comme exercice au lecteur. :-)

(*) En fait, println est une méthode. Lorsque vous écrivez x foreach println, vous n'êtes pas en passant d'une méthode, parce que les méthodes ne peuvent pas être transmis. Au lieu de cela, Scala crée une fermeture et la passe. Il développe comme ceci:

x.foreach(new Function1[Any,Unit] { def apply(x$1: Any): Unit = Console.println(x$1) })

11voto

davetron5000 Points 9622

Le trait de soulignement est un peu délicat. Selon la spécification, la phrase:

_ + 1

est équivalent à

x => x + 1

Essayer

x foreach println (y => y + 1)

rendements:

<console>:6: error: missing parameter type
           x foreach println (y => y + 1)

Si vous ajoutez quelques types:

x foreach( println((y:Int) => y + 1))
<console>:6: error: type mismatch;
 found   : Unit
 required: (Int) => Unit
           x foreach( println((y:Int) => y + 1))

Le problème, c'est que vous êtes de passage à une fonction anonyme de println et il n'est pas en mesure de traiter avec elle. Ce que vous voulez vraiment faire (si vous essayez d'imprimer le successeur de chaque élément de la liste) est:

x map (_+1) foreach println

5voto

Viktor Klang Points 14826
scala> for(x <- List(1,2,3,4)) println(x + 1)
2
3
4
5

-3voto

Alexey Points 2956

Il existe une étrange limitation dans Scala pour la profondeur d'imbrication des expressions avec trait de soulignement. C'est bien vu sur l'exemple suivant:

  scala> List(1) map(1+_)
 res3: List[Int] = List(2)

 scala> Some(1) map (1+(1+_))
 <console>:5: error: missing parameter type for expanded function ((x$1) => 1.+(x$1))
        Some(1) map (1+(1+_))
                     ^
 

Ça ressemble à un bug pour moi.

-3voto

Randall Schulz Points 18820
Welcome to Scala version 2.8.0.Beta1-prerelease (Java HotSpot(TM) Client VM, Java 1.6.0_17).
Type in expressions to have them evaluated.
Type :help for more information.

scala> val l1 = List(1, 2, 3)
l1: List[Int] = List(1, 2, 3)

scala>

scala> l1.foreach(println(_))
1
2
3

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