Использовать функциональные комбинаторы в Scala Tuples? - PullRequest
43 голосов
/ 26 февраля 2010

'map' сохраняет количество элементов, поэтому использование его в кортеже представляется разумным.

Мои попытки пока:

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)

Это выглядит довольно болезненно ... И я даже не начал пытаться преобразовать его обратно в кортеж.
Я делаю это неправильно? Может ли библиотека быть улучшена?

Ответы [ 3 ]

35 голосов
/ 26 февраля 2010

В общем, типы элементов кортежа не одинаковы, поэтому map не имеет смысла. Вы можете определить функцию для обработки особого случая:

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)

Вы можете использовать шаблон Pimp My Library, чтобы назвать это p.map(_ * 2).

UPDATE

Даже если типы элементов не совпадают, Tuple2[A, B] является Bifunctor , который может быть отображен с помощью операции 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)

ОБНОВЛЕНИЕ 2

http://gist.github.com/454818

28 голосов
/ 07 мая 2012

бесформенный Поддерживает отображение и складывание кортежей через посредническое представление HList,

Пример сеанса 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)

Если элементы кортежа имеют разные типы, вы можете отобразить их с помощью полиморфной функции с тип-специфическими случаями,

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)

Обновление

Начиная с бесформенного 2.0.0-M1 отображение напрямую поддерживается для кортежей. Приведенные выше примеры теперь выглядят так:

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)
1 голос
/ 14 декабря 2018

функция карты получает A и возвращает F[B].

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

Как писал ретроним, Tuple2 [A, B] является Bifunctor, поэтому вы можете искать функцию bimap у скалаза или кошки.
bimap - это функция, которая отображает обе стороны кортежа:

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

Поскольку Tuple [A, B] содержит 2 значения, и только одно значение может быть отображено (согласно соглашению - правильное значение), вы можете просто вернуть то же значение для левой стороны и использовать правое функция для отображения правильного значения кортежа.

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