2009-09-28 10 views
4

Si je veux réduire, par exemple, un Iterable[A] pour tous les éléments d'un type particulier (par exemple String) que je peux faire:Existe-t-il une méthode intégrée plus élégante pour filtrer et mapper une collection par type d'élément?

as filter { _.isInstanceOf[String] } 

Cependant, il est évidemment souhaitable de utiliser cela comme une Iterable[String] qui peut être fait via un map:

as filter { _.isInstanceOf[String] } map { _.asInstanceOf[String] } 

Ce qui est assez laid. Bien sûr, je pourrais utiliser flatMap à la place:

as flatMap[String] { a => 
    if (a.isInstanceOf[String]) 
    Some(a.asInstanceOf[String]) 
    else 
    None 
} 

Mais je ne suis pas sûr que ce soit plus lisible! J'ai écrit une fonction, narrow, qui peut être utilisé par implicit conversions:

as.narrow(classOf[String]) 

Mais je me demandais s'il y avait un mécanisme mieux intégré que j'ai oublié. D'autant plus que ce serait sympa de pouvoir réduire un List[A] à un List[String], plutôt qu'à un Iterable[String] comme ce sera le cas avec ma fonction.

Répondre

8

Le sucre de syntaxe Scala pour isInstanceOf/asInstanceOf est correspondance de motif:

as flatMap { case x: String => Some(x); case _ => None } 

Parce que utilise flatMap, il doit retourner en général la même collection, il fallait commencer.

Sur Scala 2.8, il existe une fonction expérimentale qui effectue ce type de motif, défini à l'intérieur de l'objet PartialFunction. Ainsi, le Scala 2.8 que vous pouvez faire:

as flatMap (PartialFunction.condOpt(_ : Any) { case x: String => x }) 

qui ressemble plus surtout parce que je ne l'ai pas importer cette fonction première. Mais, là encore, sur Scala 2.8 il y a une façon plus directe de le faire:

as collect { case x: String => x } 
+0

Neat, est là nulle part ailleurs, vous pouvez utiliser la correspondance de motif qui est pas bien connu? – ryeguy

+0

La correspondance de modèle s'applique aux instructions 'case' et aux affectations' var'/'var'. Les instructions 'Case' existent dans les instructions' match' et en tant que sucre syntaxique partiel, ce dernier étant ce qui est utilisé dans l'exemple. –

+0

Il semble que 'partialMap' a été remplacé par' collect': http://www.scala-lang.org/old/node/5690.html –

3

Pour mémoire, voici une mise en œuvre complète de narrow. Contrairement à la signature dans la question, il utilise un Manifest implicite pour éviter certains caractères:

implicit def itrToNarrowSyntax[A](itr: Iterable[A]) = new { 
    def narrow[B](implicit m: Manifest[B]) = { 
    itr flatMap { x => 
     if (Manifest.singleType(x) <:< m) 
     Some(x) 
     else 
     None 
    } 
    } 
} 

val res = List("daniel", true, 42, "spiewak").narrow[String] 
res == Iterable("daniel", "spiewak") 

Malheureusement, le rétrécissement à un type spécifique (par exemple List[String]) plutôt que Iterable[String] est un peu plus difficile. Cela peut être fait avec l'API des nouvelles collections dans Scala 2.8.0 en exploitant les classes supérieures, mais pas dans le framework actuel.

+0

Daniel - as-tu essayé par exemple avec 'narrow [Int]'? Cela ne fonctionne pas - je suppose que le type de la chose dans la liste est encadré comme 'java.lang.Integer'. Y at-il un travail autour? –

+0

Vous pourriez avoir des cas spéciaux pour les types primitifs. C'est la seule solution de rechange à laquelle je peux penser. –

1

forme la conservation: Je suis un peu trop vite en ce moment donc je repars le casting là-bas, mais je suis sûr qu'il peut être éliminé .Cela fonctionne dans le coffre:

import reflect.Manifest 
import collection.Traversable 
import collection.generic.CanBuildFrom 
import collection.mutable.ListBuffer 

object narrow { 
    class Narrower[T, CC[X] <: Traversable[X]](coll: CC[T])(implicit m1: Manifest[CC[T]], bf: CanBuildFrom[CC[T], T, CC[T]]) { 
    def narrow[B: Manifest]: CC[B] = { 
     val builder = bf(coll)  
     def isB(x: T): Option[T] = if (Manifest.singleType(x) <:< manifest[B]) Some(x) else None 

     coll flatMap isB foreach (builder += _) 
     builder mapResult (_.asInstanceOf[CC[B]]) result 
    } 
    } 

    implicit def toNarrow[T, CC[X] <: Traversable[X]](coll: CC[T])(implicit m1: Manifest[CC[T]], bf: CanBuildFrom[CC[T], T, CC[T]]) = 
    new Narrower[T,CC](coll) 

    def main(args: Array[String]): Unit = { 
    println(Set("abc", 5, 5.5f, "def").narrow[String]) 
    println(List("abc", 5, 5.5f, "def").narrow[String]) 
    } 
} 

Son exécution:

Set(abc, def) 
List(abc, def) 
Questions connexes