collect (défini sur TraversableLike et disponible dans toutes les sous-classes) fonctionne avec une collection et un PartialFunction. Aussi il arrive qu'un tas de cas des clauses définies à l'intérieur d'accolades sont une fonction partielle (Voir la section 8.5 du Scala Langage De Spécification[avertissement - PDF])
Comme dans la gestion des exceptions:
try{...do something risky ...}catch{//The contents of this catch block are a partial functioncase e:IOException=>...case e:OtherException=>...}
C'est un moyen pratique de définir une fonction qui n'accepte certaines valeurs d'un type donné.
Envisager de l'utiliser sur une liste mixte de valeurs:
val mixedList =List("a",1,2,"b",19,42.0)//this is a List[Any]val results = mixedList collect {case s:String=>"String:"+ s
case i:Int=>"Int:"+ i.toString
}
L'argument de collect méthode est un PartialFunction[Any,String]. PartialFunction parce que ce n'est pas définie pour toutes les entrées possibles de type Any (c'est le type de la List) et String parce que c'est ce que toutes les clauses de retour.
Si vous avez essayé d'utiliser map au lieu de collect, le double de la valeur à la fin de mixedList serait la cause d'une MatchError. À l'aide de collect juste ignore ce, ainsi que toute autre valeur pour laquelle la PartialFunction n'est pas défini.
Une utilisation possible serait d'appliquer des logiques différentes éléments de la liste:
var strings =List.empty[String]var ints =List.empty[Int]
mixedList collect {case s:String=> strings :+= s
case i:Int=> ints :+= i
}
Bien que c'est juste un exemple, à l'aide de variables mutables comme cela est considéré par beaucoup comme un crime de guerre - Donc merci de ne pas le faire!
Un beaucoup meilleure solution est d'utiliser collecter deux fois:
val strings = mixedList collect {case s:String=> s }val ints = mixedList collect {case i:Int=> i }
Ou si vous savez pour certain que la liste ne contient que deux types de valeurs, vous pouvez utiliser partition, qui se fend d'une des collections dans les valeurs en fonction de si oui ou non ils correspondent à certains prédicat:
//if the list only contains Strings and Ints:val(strings, ints)= mixedList partition {case s:String=>true;case _ =>false}
Le hic ici est que les deux strings et ints sont de type List[Any], mais vous pouvez facilement les contraindre à quelque chose de plus typesafe (peut-être en utilisant collect...)
Si vous avez déjà une collection de type sécurisé et souhaitez diviser sur une autre propriété des éléments, puis les choses sont un peu plus facile pour vous:
val intList =List(2,7,9,1,6,5,8,2,4,6,2,9,8)val(big,small)= intList partition (_ >5)//big and small are both now List[Int]s
Espère que résume la façon dont les deux méthodes peuvent vous aider ici!
Très belle explication, mais ce que je pense que l'OP veut, c'est une combinaison de collect et partition qui renvoie un tuple d'une liste de valeurs collectées et une liste de tout le reste. def collectAndPartition[A, B](pf: PartialFunction[A, B]): (List[B], List[A]). Ce serait probablement plus élégamment réalisé avec une bibliothèque native de la fonction, c'est à dire à la source de collect dans TraversableLike nous avons for (x <- this) if (pf.isDefinedAt(x)) b += pf(x), on pourrait simplement tack un else a += x à la fin de l', où a serait un constructeur pour la liste de tout le reste.
Je sais ce que l'OP a besoin, et je suis aussi conscient que c'est un des devoirs de la question (il a été mentionné sur un débordement de pile beaucoup récemment), je vais donc heureux de donner beaucoup de théorie, sans vraiment le résoudre. Comme pour collectAndPartition, j'ai déjà écrit que, bien que j'ai nommé la méthode collate. Si quelqu'un est enseignement de la scala au niveau où les étudiants sont appelés à travailler avec CanBuildFrom puis je vais être très surpris, c'est au-delà de la plupart des gens actuellement à l'aide de la scala dans la production.
C'est très utile. Mais je pense toujours... est-il possible de séparer par exemple les valeurs positives et négatives, sans faire un "crime de guerre" comme vous l'avez écrit erlier? Je suis juste courious parce que j'ai déjà fait ses devoirs avec l'aide de la partition. Ohhh... Et en passant, Merci pour l'aide!
collect, comme map, prend une collecte et la convertit en une autre collection. Il peut certainement aider dans le traitement des données et de la rendre plus faciles à séparer dans une suite de l'opération, mais il n'y a aucun moyen de générer deux collections en utilisant simplement collect sauf si vous comptez sur les effets secondaires (c'est à dire mutable variables). Ce genre de hack conduit à un code plus difficile à lire, et il est préférable pour les moments où il n'y a pas d'autre alternative ou il donne un indispensable coup de pouce de performance, ni de ce qui s'applique ici.
il y a un bel exemple dans cette question qui explique comment utiliser les collecter pour diviser un flux.
On pourrait aussi utiliser groupBy à définir des critères pour la division d'un nombre N de sous-listes, et puis prendre des groupes requis par leurs clés. Par exemple, dans ce cas, la clé pourrait être porteuse enum ou java instance de classe ou de type boolean, puis, vous êtes de retour avec tout à fait le même résultat que la partition offre
Pas sûr de savoir comment faire avec collect sans l'aide mutable listes, mais partition pouvez utiliser le pattern matching aussi bien (juste un peu plus verbeux)
Parce que la partition renvoie une (List[A],List[A]). C'est tout ce qu'il peut faire, car l'entrée est une List[A] et un indicateur de la fonction A => Boolean. Il n'a aucun moyen de savoir que l'indicateur de fonction peut être de type spécifique.
J'ai défini ma propre collate méthode de pimp sur les collections qui permet de résoudre simplement ce problème. Sur un List[A] le cas d'utilisation de la signature est collate[B](fn: PartialFunction[A,B]): (List(B),List(A)), évidemment, le réel la signature est un peu plus poilu que que, comme je suis également en utilisant CanBuildFrom
Donc si vous l'utilisez en mode par défaut, la réponse est non, certainement pas: vous obtenez exactement une séquence d'elle. Si vous suivez CanBuildFrom par Builder, vous voyez qu'il serait possible de faire That en fait être deux séquences, mais il n'a aucun moyen d'être dit de séquence d'un élément devrait aller, car la fonction partielle ne peut dire "oui, je fais partie" ou "non, je ne fais pas partie".
Alors que faites-vous si vous voulez avoir de multiples conditions qui en résultent dans votre liste divisé en un tas de différentes pièces? Une façon est de créer un indicateur de la fonction A => Int, où votre A est mappé dans un numéro de la classe, et ensuite utiliser groupBy. Par exemple:
def optionClass(a:Any)= a match{caseNone=>0caseSome(x)=>1case _ =>2}
scala>List(None,3,Some(2),5,None).groupBy(optionClass)
res11: scala.collection.immutable.Map[Int,List[Any]]=Map((2,List(3,5)),(1,List(Some(2))),(0,List(None,None)))
Maintenant vous pouvez regarder votre sous-listes par classe (0, 1 et 2 dans ce cas). Malheureusement, si vous souhaitez ignorer certains intrants, vous avez encore de les mettre dans une classe (par exemple, vous avez probablement ne se soucient pas des copies multiples de None dans ce cas).
Je l'utilise. Une bonne chose à ce sujet est qu'il combine de partitionnement et de la cartographie en une seule itération. Un inconvénient est qu'il ne allouer un tas d'objets temporaires (le Either.Left et Either.Right cas)
/**
* Splits the input list into a list of B's and a list of C's, depending on which type of value the mapper function returns.
*/def mapSplit[A,B,C](in:List[A])(mapper:(A)=>Either[B,C]):(List[B],List[C])={@tailrec
def mapSplit0(in:List[A], bs:List[B], cs:List[C]):(List[B],List[C])={
in match{case a :: as =>
mapper(a)match{caseLeft(b)=> mapSplit0(as, b :: bs, cs )caseRight(c)=> mapSplit0(as, bs, c :: cs)}caseNil=>(bs.reverse, cs.reverse)}}
mapSplit0(in,Nil,Nil)}val got = mapSplit(List(1,2,3,4,5)){case x if x %2==0=>Left(x)case y =>Right(y.toString * y)}
assertEquals((List(2,4),List("1","333","55555")), got)
Je ne pouvais pas trouver une solution satisfaisante à ce problème fondamental.
Je n'ai pas besoin d'une conférence sur collect et ne se soucient pas si c'est quelqu'un à faire leurs devoirs. Aussi, je ne veux pas quelque chose qui ne fonctionne que pour List.
Voici donc mon coup de poignard à elle. Efficace et compatible avec tous les TraversableOnce, même les chaînes:
implicitclassTraversableOnceHelper[A,Repr](privateval repr:Repr)(implicit isTrav:Repr=>TraversableOnce[A]){def collectPartition[B,Left](pf:PartialFunction[A, B])(implicit bfLeft:CanBuildFrom[Repr, B,Left], bfRight:CanBuildFrom[Repr, A,Repr]):(Left,Repr)={val left = bfLeft(repr)val right = bfRight(repr)val it = repr.toIterator
while(it.hasNext){val next = it.next
if(!pf.runWith(left += _)(next)) right += next
}
left.result -> right.result
}def mapSplit[B,C,Left,Right](f: A =>Either[B,C])(implicit bfLeft:CanBuildFrom[Repr, B,Left], bfRight:CanBuildFrom[Repr, C,Right]):(Left,Right)={val left = bfLeft(repr)val right = bfRight(repr)val it = repr.toIterator
while(it.hasNext){
f(it.next)match{caseLeft(next)=> left += next
caseRight(next)=> right += next
}}
left.result -> right.result
}}
De départ Scala 2.13, la plupart des collections sont maintenant fournis avec un partitionMap méthode qui partitions des éléments basés sur une fonction qui renvoie Right ou Left.
Qui nous permet de correspondance du modèle basé sur le type (qui, comme un collect permet d'avoir des types spécifiques dans le partitionnée listes) ou tout autre motif:
collect
(défini sur TraversableLike et disponible dans toutes les sous-classes) fonctionne avec une collection et unPartialFunction
. Aussi il arrive qu'un tas de cas des clauses définies à l'intérieur d'accolades sont une fonction partielle (Voir la section 8.5 du Scala Langage De Spécification [avertissement - PDF])Comme dans la gestion des exceptions:
C'est un moyen pratique de définir une fonction qui n'accepte certaines valeurs d'un type donné.
Envisager de l'utiliser sur une liste mixte de valeurs:
L'argument de
collect
méthode est unPartialFunction[Any,String]
.PartialFunction
parce que ce n'est pas définie pour toutes les entrées possibles de typeAny
(c'est le type de laList
) etString
parce que c'est ce que toutes les clauses de retour.Si vous avez essayé d'utiliser
map
au lieu decollect
, le double de la valeur à la fin demixedList
serait la cause d'uneMatchError
. À l'aide decollect
juste ignore ce, ainsi que toute autre valeur pour laquelle la PartialFunction n'est pas défini.Une utilisation possible serait d'appliquer des logiques différentes éléments de la liste:
Bien que c'est juste un exemple, à l'aide de variables mutables comme cela est considéré par beaucoup comme un crime de guerre - Donc merci de ne pas le faire!
Un beaucoup meilleure solution est d'utiliser collecter deux fois:
Ou si vous savez pour certain que la liste ne contient que deux types de valeurs, vous pouvez utiliser
partition
, qui se fend d'une des collections dans les valeurs en fonction de si oui ou non ils correspondent à certains prédicat:Le hic ici est que les deux
strings
etints
sont de typeList[Any]
, mais vous pouvez facilement les contraindre à quelque chose de plus typesafe (peut-être en utilisantcollect
...)Si vous avez déjà une collection de type sécurisé et souhaitez diviser sur une autre propriété des éléments, puis les choses sont un peu plus facile pour vous:
Espère que résume la façon dont les deux méthodes peuvent vous aider ici!
collect
etpartition
qui renvoie un tuple d'une liste de valeurs collectées et une liste de tout le reste.def collectAndPartition[A, B](pf: PartialFunction[A, B]): (List[B], List[A])
. Ce serait probablement plus élégamment réalisé avec une bibliothèque native de la fonction, c'est à dire à la source decollect
dans TraversableLike nous avonsfor (x <- this) if (pf.isDefinedAt(x)) b += pf(x)
, on pourrait simplement tack unelse a += x
à la fin de l', oùa
serait un constructeur pour la liste de tout le reste.collate
. Si quelqu'un est enseignement de la scala au niveau où les étudiants sont appelés à travailler avec CanBuildFrom puis je vais être très surpris, c'est au-delà de la plupart des gens actuellement à l'aide de la scala dans la production.collect
, commemap
, prend une collecte et la convertit en une autre collection. Il peut certainement aider dans le traitement des données et de la rendre plus faciles à séparer dans une suite de l'opération, mais il n'y a aucun moyen de générer deux collections en utilisant simplementcollect
sauf si vous comptez sur les effets secondaires (c'est à dire mutable variables). Ce genre de hack conduit à un code plus difficile à lire, et il est préférable pour les moments où il n'y a pas d'autre alternative ou il donne un indispensable coup de pouce de performance, ni de ce qui s'applique ici.Pas sûr de savoir comment faire avec
collect
sans l'aide mutable listes, maispartition
pouvez utiliser le pattern matching aussi bien (juste un peu plus verbeux)(List[A],List[A])
. C'est tout ce qu'il peut faire, car l'entrée est uneList[A]
et un indicateur de la fonctionA => Boolean
. Il n'a aucun moyen de savoir que l'indicateur de fonction peut être de type spécifique.collate
méthode de pimp sur les collections qui permet de résoudre simplement ce problème. Sur unList[A]
le cas d'utilisation de la signature estcollate[B](fn: PartialFunction[A,B]): (List(B),List(A))
, évidemment, le réel la signature est un peu plus poilu que que, comme je suis également en utilisantCanBuildFrom
La signature de l'normalement utilisé
collect
sur, disons,Seq
, estqui est vraiment un cas particulier de
Donc si vous l'utilisez en mode par défaut, la réponse est non, certainement pas: vous obtenez exactement une séquence d'elle. Si vous suivez
CanBuildFrom
parBuilder
, vous voyez qu'il serait possible de faireThat
en fait être deux séquences, mais il n'a aucun moyen d'être dit de séquence d'un élément devrait aller, car la fonction partielle ne peut dire "oui, je fais partie" ou "non, je ne fais pas partie".Alors que faites-vous si vous voulez avoir de multiples conditions qui en résultent dans votre liste divisé en un tas de différentes pièces? Une façon est de créer un indicateur de la fonction
A => Int
, où votreA
est mappé dans un numéro de la classe, et ensuite utilisergroupBy
. Par exemple:Maintenant vous pouvez regarder votre sous-listes par classe (0, 1 et 2 dans ce cas). Malheureusement, si vous souhaitez ignorer certains intrants, vous avez encore de les mettre dans une classe (par exemple, vous avez probablement ne se soucient pas des copies multiples de
None
dans ce cas).Je l'utilise. Une bonne chose à ce sujet est qu'il combine de partitionnement et de la cartographie en une seule itération. Un inconvénient est qu'il ne allouer un tas d'objets temporaires (le
Either.Left
etEither.Right
cas)Je ne pouvais pas trouver une solution satisfaisante à ce problème fondamental.
Je n'ai pas besoin d'une conférence sur
collect
et ne se soucient pas si c'est quelqu'un à faire leurs devoirs. Aussi, je ne veux pas quelque chose qui ne fonctionne que pourList
.Voici donc mon coup de poignard à elle. Efficace et compatible avec tous les
TraversableOnce
, même les chaînes:Exemple d'utilisation:
De départ
Scala 2.13
, la plupart des collections sont maintenant fournis avec unpartitionMap
méthode qui partitions des éléments basés sur une fonction qui renvoieRight
ouLeft
.Qui nous permet de correspondance du modèle basé sur le type (qui, comme un
collect
permet d'avoir des types spécifiques dans le partitionnée listes) ou tout autre motif: