49 votes

Scala - un paramètre lambda peut-il correspondre à un tuple ?

Supposons que j'aie une liste du type

val l = List((1, "blue"), (5, "red"), (2, "green"))

Et si je veux filtrer l'un d'entre eux, je peux faire quelque chose comme

val m = l.filter(item => {
    val (n, s) = item          // "unpack" the tuple here
    n != 2
}

Existe-t-il un moyen de "décompresser" le tuple en tant que paramètre de l'algorithme lambda directement, au lieu d'avoir cet intermédiaire ? item variable ?

Quelque chose comme ce qui suit serait idéal, mais Eclipse me dit wrong number of parameters; expected=1

val m = l.filter( (n, s) => n != 2 )

Toute aide serait appréciée - j'utilise la version 2.9.0.1

84voto

Kipton Barros Points 12445

C'est à peu près ce qui s'en rapproche le plus :

 val m = l.filter { case (n, s) => n != 2 }

Il s'agit essentiellement d'une syntaxe de recherche de motifs à l'intérieur d'un fichier anonyme Fonction partielle . Il y a aussi les tupled méthodes en Function mais il s'agit simplement d'une enveloppe autour de cette expression de recherche de motifs.

18voto

Amir Raminfar Points 17939

Hmm, bien que Kipton ait une bonne réponse. Vous pouvez en fait raccourcir le texte en faisant.

val l = List((1, "blue"), (5, "red"), (2, "green"))
val m = l.filter(_._1 != 2)

10voto

comonad Points 1852
val m = l.filter( (n, s) => n != 2 )

... est une erreur de type parce que cette lambda définit un objet de type

  • Function2[String,Int,Boolean] avec deux paramètres au lieu de
  • Function1[(String,Int),Boolean] avec un Tuple2[String,Int] comme paramètre.

Vous pouvez faire la conversion comme suit :

val m = l.filter( ((n, s) => n != 2).tupled )

9voto

Rex Kerr Points 94401

Il existe un grand nombre d'options :

for (x <- l; (n,s) = x if (n != 2)) yield x
l.collect{ case x @ (n,s) if (n != 2) => x }
l.filter{ case (n,s) => n != 2 }
l.unzip.zipped.map((n,s) => n != 2).zip   // Complains that zip is deprecated

1voto

akauppi Points 3125

J'ai réfléchi à la même chose et je suis arrivé à votre question aujourd'hui.

Je n'aime pas beaucoup les approches basées sur les fonctions partielles (tout ce qui a une fonction partielle). case ) puisqu'ils impliquent qu'il pourrait y avoir plus de points d'entrée pour le flux logique. Pour moi en tout cas, ils tendent à brouiller l'intention du code. D'un autre côté, je veux vraiment aller directement aux champs de tuple, comme vous.

Voici une solution que j'ai ébauchée aujourd'hui. Elle semble fonctionner, mais je ne l'ai pas encore essayée en production.

object unTuple {
  def apply[A, B, X](f: (A, B) => X): (Tuple2[A, B] => X) = {
    (t: Tuple2[A, B]) => f(t._1, t._2)
  }
  def apply[A, B, C, X](f: (A, B, C) => X): (Tuple3[A, B, C] => X) = {
    (t: Tuple3[A, B, C]) => f(t._1, t._2, t._3)
  }
  //...
}

val list = List( ("a",1), ("b",2) )
val list2 = List( ("a",1,true), ("b",2,false) )

list foreach unTuple( (k: String, v: Int) =>
  println(k, v)
)

list2 foreach unTuple( (k: String, v: Int, b: Boolean) =>
  println(k, v, b)
)

Sortie :

(a,1)
(b,2)
(a,1,true)
(b,2,false)

Peut-être cela s'avérera-t-il utile. Les unTuple devrait naturellement être mis de côté dans un espace de noms d'outils.

Addendum :

Appliquée à votre cas :

val m = l.filter( unTuple( (n:Int,color:String) =>
    n != 2
))

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