Работа с кортежами в Scala - PullRequest
       7

Работа с кортежами в Scala

6 голосов
/ 13 октября 2010

Я хочу сделать что-то вроде этого (очень сильно упрощено):

((1, 2, 3, 4, 5, 6), (6, 5, 4, 3, 2, 1)).zipped map (_ + _)

Игнорировать действительные значения целых чисел (хотя на самом деле важно, что это 6-кортежи :)).По сути, я хочу использовать это довольно регулярно в функции, которая поддерживает Map[String, (Int, Int, Int, Int, Int, Int)] при обновлении существующего элемента.

Как есть, Scala выкладывает это на меня:

<console>:6: error: could not find implicit value for parameter w1: ((Int, Int, Int, Int, Int, Int)) => scala.collection.TraversableLike[El1,Repr1]
   ((1, 2, 3, 4, 5, 6), (6, 5, 4, 3, 2, 1)).zipped

Если я использую Seq s вместо кортежей, все работает нормально, но я хочу применить arity 6 в системе типов (я, вероятно, type Record = (Int, Int, Int, Int, Int, Int) в качестве быстрого рефакторинга в ближайшее время).

Может кто-нибудь дать совет, что я делаю неправильно / почему Scala не справится с кодом выше?Я подумал, что это может сработать, если я использую 2- или 3-артериальный кортеж, поскольку Scala определяет Tuple2 и Tuple3 s (я понимаю, что масштабировать функции кортежа по произвольной n-арности сложно), но я получаюта же ошибка.

Заранее спасибо за любую помощь:).

Ответы [ 8 ]

8 голосов
/ 14 октября 2010

Вы хотите отображать только кортежи с одинаковыми типами - иначе карта не имела бы смысла - но Tuple не содержит этого в сигнатуре своего типа.Но если вы готовы выполнить небольшую работу, вы можете настроить ее так, чтобы кортежи работали так, как вы просили:

Основа:

class TupTup6[A,B](a: (A,A,A,A,A,A), b: (B,B,B,B,B,B)) {
  def op[C](f:(A,B)=>C) = ( f(a._1,b._1), f(a._2,b._2), f(a._3,b._3), 
                            f(a._4,b._4), f(a._5,b._5), f(a._6,b._6) )
}
implicit def enable_tuptup6[A,B](ab: ((A,A,A,A,A,A),(B,B,B,B,B,B))) = {
  new TupTup6(ab._1,ab._2)
}

Использование:

scala> ((1,2,3,4,5,6) , (6,5,4,3,2,1)) op { _ + _ }
res0: (Int, Int, Int, Int, Int, Int) = (7,7,7,7,7,7)
6 голосов
/ 13 октября 2010

Я получил это маленькое вдохновение.

class TupleZipper[T <: Product](t1: T) {
  private def listify(p: Product) = p.productIterator.toList
  def zipWith(t2: T) = (listify(t1), listify(t2)).zipped
}
implicit def mkZipper[T <: Product](t1: T) = new TupleZipper(t1)

// ha ha, it's arity magic
scala> ((1, 2, 3, 4, 5, 6)) zipWith ((6, 5, 4, 3, 2))                      
<console>:8: error: type mismatch;
 found   : (Int, Int, Int, Int, Int)
 required: (Int, Int, Int, Int, Int, Int)
       ((1, 2, 3, 4, 5, 6)) zipWith ((6, 5, 4, 3, 2))
                                     ^

scala> ((1, 2, 3, 4, 5, 6)) zipWith ((6, 5, 4, 3, 2, 1))                   
res1: (List[Any], List[Any])#Zipped[List[Any],Any,List[Any],Any] = scala.Tuple2$Zipped@42e934e

scala> res1 map ((x, y) => x.asInstanceOf[Int] + y.asInstanceOf[Int])      
res2: List[Int] = List(7, 7, 7, 7, 7, 7)

Да, группа Анис выходит на другом конце. Не очень захватывающе, но не так много, что вы можете сделать, если попытаетесь навязать себя Tuples таким образом.

Редактировать: о, и, конечно, система типов дает вам полный доход здесь.

scala> ((1, 2, 3, 4, 5, 6)) zipWith ((6, 5, 4, 3, 2, "abc"))         
<console>:8: error: type mismatch;
 found   : java.lang.String("abc")
 required: Int
       ((1, 2, 3, 4, 5, 6)) zipWith ((6, 5, 4, 3, 2, "abc"))
                                                     ^
5 голосов
/ 13 октября 2010
import scala.collection._

type Record = (Int, Int, Int, Int, Int, Int)

implicit def toIterable(r: Record) = new Iterable[Int]{
  def iterator = r.productIterator.asInstanceOf[Iterator[Int]]
}

implicit def cbf[From <: Iterable[Int]] = new generic.CanBuildFrom[From, Int, Record] {
    def apply(from: From) = apply
    def apply = new mutable.Builder[Int, Record] {
      var array = Array.ofDim[Int](6)
      var i = 0

      def +=(elem: Int) = {
        array(i) += elem
        i += 1
        this
      } 

      def clear() = i = 0

      def result() = (array(0), array(1), array(2), array(3), array(4), array(5))

    }
}

использование:

scala> ((1, 2, 3, 4, 5, 6), (6, 5, 4, 3, 2, 1)).zipped.map{_ + _}
res1: (Int, Int, Int, Int, Int, Int) = (7,7,7,7,7,7)
3 голосов
/ 14 октября 2010

короткое решение:

type Record = (Int, Int, Int, Int, Int, Int)

implicit def toList(r: Record) = r.productIterator.asInstanceOf[Iterator[Int]].toList
implicit def toTuple(l: List[Int]): Record = (l(0), l(1), l(2), l(3), l(4), l(5))

использование:

scala> ((1,2,3,4,5,6), (6,5,4,3,2,1)).zipped map {_ + _}: Record
res0: (Int, Int, Int, Int, Int, Int) = (7,7,7,7,7,7)
3 голосов
/ 13 октября 2010

Tuple2 # zipped здесь вам не поможет, он работает, когда содержащиеся элементы - TraversableLike / IterableLike - а Tuples - нет.

Возможно, вы захотите определить свою собственную функцию sumRecords, которая принимает две записи и возвращает их сумму:

def sumRecord(a:Record, b:Record) = new Record(
  a._1 + b._1,
  a._2 + b._2,
  a._3 + b._3,
  a._4 + b._4,
  a._5 + b._5,
  a._6 + b._6
)

Затем использовать его с парой [Запись, Запись]:

val p : Pair[Record, Record] = ...
val summed = sumRecord(p._1, p._2)

Конечно, есть абстракции; но так как запись будет исправлена ​​во всем вашем дизайне, тогда они мало что значат.

2 голосов
/ 19 июля 2014

Теперь вы можете легко достичь этого с бесформенным , таким образом:

import shapeless._
import shapeless.syntax.std.tuple._

val a = (1, 2, 3, 4, 5, 6)
val b = (6, 5, 4, 3, 2, 1)

object sum extends Poly1 {
  implicit def f = use((t: (Int, Int)) => t._1 + t._2)
}

val r = a.zip(b) map sum // r is a (Int, Int, Int, Int, Int, Int)

Недостатком является странный синтаксис, который вы должны использовать для выражения функции sum, но все безопасно для типов и проверено.

1 голос
/ 19 сентября 2015

В качестве обновления к ответу Рекса Керра, начиная с Scala 2.10, вы можете использовать неявные классы : синтаксический сахар, который делает это решение еще короче.

implicit class TupTup6[A,B](x: ((A,A,A,A,A,A),(B,B,B,B,B,B))) {
   def op[C](f:(A,B)=>C) = ( 
       f(x._1._1,x._2._1),
       f(x._1._2,x._2._2), 
       f(x._1._3,x._2._3),
       f(x._1._4,x._2._4), 
       f(x._1._5,x._2._5), 
       f(x._1._6,x._2._6) )
}
0 голосов
/ 13 октября 2010

Вы получаете ошибку, потому что рассматриваете кортеж как коллекцию.

Возможно ли использовать списки вместо кортежей?Тогда расчет прост:

scala> List(1,2,3,4,5,6).zip(List(1,2,3,4,5,6)).map(x => x._1 + x._2 )     
res6: List[Int] = List(2, 4, 6, 8, 10, 12)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...