Работа с кортежами в Scala
Я хочу сделать что-то вроде этого (упрощенный довольно сильно):
((1, 2, 3, 4, 5, 6), (6, 5, 4, 3, 2, 1)).zipped map (_ + _)
игнорировать фактические значения целых чисел (хотя важно, что это 6-Кортежи, на самом деле :)). По сути, я хочу использовать это довольно регулярно в функции, которая поддерживает Map[String, (Int, Int, Int, Int, Int, Int)]
при обновлении существующего элемента.
как есть, скала выплевывает это на меня:
<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-arity, видя, как Scala определяет Tuple2
и Tuple3
s (Я понимаю, что масштабирование кортежных функций через произвольную N-арность сложно), но я получаю ту же ошибку.
заранее спасибо за любую помощь предложил :).
8 ответов
вы хотите отобразить только кортежи, которые имеют идентичные типы-иначе карта не имела бы смысла-но кортеж не содержит этого в своей сигнатуре типа. Но если вы готовы немного поработать, вы можете настроить его так, чтобы кортежи работали так, как вы просили:
основа:
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)
Я получил это небольшое вдохновение.
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)
да, куча аниса выходит с другого конца. Не очень захватывающе, но не так много вы можете сделать, когда пытаетесь заставить себя на Кортежи таким образом.
Edit: о, и, конечно, система типов дает вам полный Монти здесь.
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"))
^
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)
tuple2#zipped не поможет вам здесь, он работает, когда содержащиеся элементы TraversableLike/IterableLike - какие кортежи не являются.
вы, вероятно, захотите определить свою собственную функцию 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)
конечно, есть доступные абстракции; но поскольку запись будет исправлена во всем вашем дизайне, тогда у них мало значение.
короткое решение:
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)
теперь вы можете легко достичь этого с бесформенные таким образом:
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
функция, но все тип-безопасно и тип-проверено.
вы получаете ошибку, потому что вы рассматриваете кортеж как коллекцию.
возможно ли использовать списки вместо кортежей? Тогда расчет прост:
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)
в качестве обновления для ответа Rex Kerr, начиная с 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) )
}