2010-02-26 12 views
43

'map' conserva el número de elementos, por lo que usarlo en un Tuple parece sensato.¿Utiliza combinadores funcionales en Scala Tuples?

Mis intentos hasta ahora:

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) 

Parece bastante doloroso ... Y yo ni siquiera han comenzado a tratar de volver a convertirlo en una tupla.
¿Lo estoy haciendo mal? ¿Se podría mejorar la biblioteca?

+0

Parece que estás usando una tupla en la que realmente deberías estar utilizando una colección. Considere usar una clase de colección real en su lugar: las tuplas no deben usarse como un tipo de colecciones. – Jesper

+1

@Jesper: no estoy de acuerdo: simplemente podría querer aplicar la misma operación DRY'ly y de manera concisa en una colección de artículos cuyo tamaño se conoce estáticamente. –

Respuesta

27

shapeless admite la asignación y plegado de tuplas a través de un HList representación intermediario, sesión REPL

Sample,

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) 

Cuando los elementos de la tupla son de diferentes tipos que puede mapa con una función polimórfica con casos específicos de tipo,

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) 

actualización

A partir del informe 2.0.0-M1 mapeo sobre las tuplas se apoya directamente. Los ejemplos anteriores ahora se ven así,

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) 
+0

¡Esto es genial! Marcaría su respuesta como aceptada, pero no sé si está bien cambiar la respuesta aceptada después de varios años. –

+2

Debe aceptar la respuesta que considere mejor. Si las circunstancias o su opinión cambian con el tiempo, entonces creo que es razonable actualizar su aceptación en consecuencia. –

+0

En mi humilde opinión, esto es demasiado abstracto. Quiero decir, necesita importar 3 espacios de nombres completos (importar xxxxx._) y para el caso genérico completo, necesita tantas implícitas adicionales como tipo de elemento en las tuplas. Sin duda, depende de los casos de uso, pero lo más probable es que un código simple y explícito sea más legible sin pérdida de eficiencia. –

35

En general, los tipos de elementos de una tupla no son los mismos, por lo que el mapa no tiene sentido. Se puede definir una función para manejar el caso especial, sin embargo:

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) 

podría utilizar el patrón de Pimp My Library para llamar a esto como p.map(_ * 2).

ACTUALIZACIÓN

Incluso cuando los tipos de los elementos no son los mismos, Tuple2[A, B] es una Bifunctor, que puede ser mapeado con la operación 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) 

ACTUALIZACIÓN 2

http://gist.github.com/454818

+0

Para el registro, estoy cambiando la respuesta aceptada de esto a la respuesta más reciente sobre la biblioteca Shapeless, que no estaba disponible en 2010. –

Cuestiones relacionadas