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 + 1
où x$0
serait l'paramètre passé par foreach
, au lieu de cela. Mais, prenons l'... foreach
, comme un paramètre, une Function1[T, Unit]
où 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' foreach
attend.
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) })