web-dev-qa-db-fra.com

Utiliser des combinateurs fonctionnels sur les tubes Scala?

'map' préserve le nombre d'éléments, son utilisation sur un tuple semble donc judicieuse.

Mes tentatives jusqu'ici:

scala> (3,4).map(_*2)    
error: value map is not a member of (Int, Int)
       (3,4).map(_*2)
             ^
scala> (3,4).productIterator.map(_*2)
error: value * is not a member of Any
       (3,4).productIterator.map(_*2)
                                  ^
scala> (3,4).productIterator.map(_.asInstanceOf[Int]*2)
res4: Iterator[Int] = non-empty iterator

scala> (3,4).productIterator.map(_.asInstanceOf[Int]*2).toList
res5: List[Int] = List(6, 8)

Cela semble assez douloureux ... Et je n'ai même pas commencé à essayer de le reconvertir en Tuple.
Est-ce que je me trompe? La bibliothèque pourrait-elle être améliorée?

43
Eldritch Conundrum

informe supporte le mapping et le repliement des n-uplets via une représentation intermédiaire HList,

Exemple de session REPL,

scala> import shapeless._ ; import Tuples._
import shapeless._
import Tuples._

scala> object double extends (Int -> Int) (_*2)
defined module double

scala> (3, 4).hlisted.map(double).tupled
res0: (Int, Int) = (6,8)

Lorsque les éléments du tuple sont de types différents, vous pouvez mapper avec une fonction polymorphe avec des cas spécifiques au type,

scala> object frob extends Poly1 {
     |   implicit def caseInt     = at[Int](_*2)
     |   implicit def caseString  = at[String]("!"+_+"!")
     |   implicit def caseBoolean = at[Boolean](!_)
     | }
defined module frob

scala> (23, "foo", false, "bar", 13).hlisted.map(frob).tupled
res1: (Int, String, Boolean, String, Int) = (46,!foo!,true,!bar!,26)

Mise à jour

À partir de sans forme 2.0.0-M1 , le mappage sur des nuplets est directement pris en charge. Les exemples ci-dessus ressemblent maintenant à ceci,

scala> import shapeless._, poly._, syntax.std.Tuple._
import shapeless._
import poly._
import syntax.std.Tuple._

scala> object double extends (Int -> Int) (_*2)
defined module double

scala> (3, 4) map double
res0: (Int, Int) = (6,8)

scala> object frob extends Poly1 {
     |   implicit def caseInt     = at[Int](_*2)
     |   implicit def caseString  = at[String]("!"+_+"!")
     |   implicit def caseBoolean = at[Boolean](!_)
     | }
defined module frob

scala> (23, "foo", false, "bar", 13) map frob
res1: (Int, String, Boolean, String, Int) = (46,!foo!,true,!bar!,26)
28
Miles Sabin

En général, les types d'éléments d'un tuple ne sont pas identiques, ainsi la carte n'a pas de sens. Vous pouvez définir une fonction pour gérer le cas particulier, cependant:

scala> def map[A, B](as: (A, A))(f: A => B) = 
     as match { case (a1, a2) => (f(a1), f(a2)) } 
map: [A,B](as: (A, A))(f: (A) => B)(B, B)

scala> val p = (1, 2)    
p: (Int, Int) = (1,2)

scala> map(p){ _ * 2 }
res1: (Int, Int) = (2,4)

Vous pouvez utiliser le modèle Pimp My Library pour appeler ceci sous la forme p.map(_ * 2).

UPDATE

Même lorsque les types des éléments ne sont pas identiques, Tuple2[A, B] est un Bifunctor , qui peut être mappé avec l'opération bimap.

scala> import scalaz._
import scalaz._

scala> import Scalaz._
import Scalaz._

scala> val f = (_: Int) * 2
f: (Int) => Int = <function1>

scala> val g = (_: String) * 2
g: (String) => String = <function1>

scala> f <-: (1, "1") :-> g
res12: (Int, String) = (2,11)

UPDATE 2

http://Gist.github.com/454818

35
retronym

la fonction map obtient une A et retourne F[B].

def map[A, B](f: A => F[B]) : F[B]

Comme l'a écrit retronym, Tuple2 [A, B] est un bifunctor, vous pouvez donc rechercher la fonction bimap dans scalaz ou chez les chats.
bimap est une fonction qui mappe les deux côtés du tuple:

def bimap[A, B, C, D](fa: A => C, fb: B => D): Tuple2[C, D]

Étant donné que Tuple [A, B] contient 2 valeurs et qu’une seule valeur peut être mappée (par convention, la valeur correcte), vous pouvez simplement renvoyer la même valeur pour le côté gauche et utiliser la fonction droite Pour mapper sur la bonne valeur du tuple.

(3, 4).bimap(identity, _ * 2)
0
Sagi