2010-12-07 54 views
18

Pour créer une nouvelle classe qui peut être utilisé dans une Scala pour la compréhension, il semble que tout ce que vous devez faire est de définir une fonction de carte:pourquoi le filtre doit-il être défini pour la correspondance de modèle dans une boucle for dans scala?

scala> class C[T](items: T*) { 
    | def map[U](f: (T) => U) = this.items.map(f) 
    | } 
defined class C 

scala> for (x <- new C(1 -> 2, 3 -> 4)) yield x 
res0: Seq[(Int, Int)] = ArrayBuffer((1,2), (3,4)) 

Mais cela ne fonctionne que pour simple pour les boucles où il n'y a motif correspondant sur le côté gauche de <-. Si vous essayez de correspondance de motif là-bas, vous obtenez une plainte que la méthode filter n'est pas définie:

scala> for ((k, v) <- new C(1 -> 2, 3 -> 4)) yield k -> v 
<console>:7: error: value filter is not a member of C[(Int, Int)] 
     for ((k, v) <- new C(1 -> 2, 3 -> 4)) yield k -> v 

Pourquoi est-filtre nécessaire pour mettre en œuvre le modèle correspondant ici? J'aurais pensé Scala serait tout simplement traduire la boucle au-dessus dans l'appel map équivalent:

scala> new C(1 -> 2, 3 -> 4).map{case (k, v) => k -> v} 
res2: Seq[(Int, Int)] = ArrayBuffer((1,2), (3,4)) 

Mais cela semble bien fonctionner, de sorte que la boucle doit se traduire par autre chose. Qu'est-ce qui est traduit en ce qui a besoin de la méthode filter?

Répondre

18

La réponse courte: selon les spécifications Scala, vous ne devriez pas besoin de définir une méthode « filtre » pour l'exemple que vous avez donné, mais il y a un open bug que des moyens c'est actuellement requis. La réponse longue: l'algorithme de désembuage appliqué aux compréhensions est décrit dans the Scala language specification. Commençons par la section 6,19 « Pour compréhensions et boucles For » (je regarde la version 2.9 de la spécification):

Dans un premier temps, chaque générateur p < - e, où p est pas irréfutables (§ 8.1) pour le type de e est remplacé par p < - e.withFilter {case p => true; case _ => false}

Le point important pour votre question est de savoir si le modèle dans la compréhension est "irréfutable" pour l'expression donnée ou non. (Le motif est le bit avant le '< -', l'expression est le bit après.) Si c'est "irréfutable", alors le filtre ne sera pas ajouté, sinon il sera nécessaire.

Bien, mais que signifie "irréfutable"? Passez à la section 8.1.14 de la spécification ("Motifs irréversibles"). Grosso modo, si le compilateur peut prouver que le pattern ne peut pas échouer en faisant correspondre l'expression, alors le pattern est irréfutable et l'appel withFilter ne sera pas ajouté.

Maintenant, votre exemple qui fonctionne comme prévu est le premier type de motif irréfutable de la section 8.1.14, un modèle de variable. Ainsi, le premier exemple est facile pour le compilateur de déterminer que withFilter n'est pas nécessaire.

Votre deuxième exemple est potentiellement le troisième type de modèle irréfutable, un modèle de constructeur. Essayer de faire correspondre (k, v) qui est Tuple2[Any,Any] contre un Tuple2[Int,Int] (voir les sections 8.1.6 et 8.1.7 de la spécification)) réussit puisque Int est irréfutable pour Any. Par conséquent, le second modèle est également irréfutable et n'a pas (ne devrait pas) besoin d'une méthode withFilter.

Dans l'exemple de Daniel, Tuple2[Any,Any] n'est pas irréfutable contre Any, de sorte que les appels withFilter sont ajoutés.A propos, le message d'erreur parle d'une méthode filter mais la spécification parle de withFilter - elle a été modifiée avec Scala 2.8, voir this question and answer pour les détails sanglants.

12

Voir la différence:

scala> for ((k, v) <- List(1 -> 2, 3 -> 4, 5)) yield k -> v 
res22: List[(Any, Any)] = List((1,2), (3,4)) 

scala> List(1 -> 2, 3 -> 4, 5).map{case (k, v) => k -> v} 
scala.MatchError: 5 
+0

J'ai vérifié le code compilé avec et sans '5'. Lorsqu'il n'y a pas de '5', le filtre n'est pas utilisé. – IttayD

+11

Wow, je ne me suis jamais rendu compte que pour les boucles jetteraient silencieusement les éléments non-assortis. Cela ressemble à un gotcha subtile je ferais mieux de faire attention. – Steve

+0

La version de 'map' est presque aussi dangereuse, car il n'y a aucun avertissement qu'elle peut lancer. Malheureusement, il n'y a actuellement aucune syntaxe pour déstructurer les arguments dans un littéral de fonction sans utiliser de fonction partielle. –