2010-10-12 14 views
6

ich so etwas wie dies tun wollen (vereinfacht sehr stark):Arbeiten mit Tupeln in Scala

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

die tatsächlichen Werte der ganzen Zahlen ignorieren (obwohl es wichtig ist, dass diese 6-Tupel sind, eigentlich :)) . Im Wesentlichen möchte ich dies ziemlich regelmäßig in einer Funktion verwenden, die ein Map[String, (Int, Int, Int, Int, Int, Int)] beibehält, wenn ein vorhandenes Element aktualisiert wird.

Wie es ist, Scala spuckt das bei mir aus:

<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 

Wenn ich Seq s anstelle von Tupeln, alles funktioniert gut, aber ich möchte ein arity von 6 in der Art System (I erzwingen werde wohl bald type Record = (Int, Int, Int, Int, Int, Int) als schnellen refactor).

Kann mir jemand Ratschläge geben, was ich falsch mache/warum wird Scala nicht mit dem obigen Code umgehen? Ich dachte, es könnte funktionieren, wenn ich ein 2- oder 3-Arity-Tupel verwende, da Scala Tuple2 und Tuple3 s definiert (ich verstehe, dass die Skalierung von Tupelfunktionen über eine beliebige N-Arität schwierig ist), aber ich bekomme denselben Fehler.

Vielen Dank im Voraus für jede Hilfe angeboten :).

Antwort

8

Sie nur über Tupel abbilden möchten, die identische Typen haben - sonst wird die Karte würde keinen Sinn machen - aber Tuple enthalten nicht, dass in seine Typ-Signatur.Aber wenn Sie bereit sind, ein wenig Arbeit zu tun, können Sie es so einrichten, dass Tupel arbeiten, wie Sie angefordert:

Grundlegung:

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) 
} 

Verbrauch:

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

Ich mag das. Ich muss mehr auf Implicits schauen, da es aussieht, als wären sie ein sehr mächtiges Konstrukt, das ich verpasse (ich lerne gerade Scala, wie ich es gerade mache). Vielen Dank! – frio

+0

oh, das ist ziemlich elegant. Mag ich! –

3

Tuple2 # ZIPPED wird Ihnen hier nicht helfen, es funktioniert, wenn die enthaltenen Elemente TraversableLike/IterableLike sind - was Tuples nicht sind.

Sie werden wahrscheinlich wollen Ihre eigene sumRecords Funktion definieren, die zwei Datensätze nimmt und ihre Summe:

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 
) 

Dann, es zu benutzen mit einem Paar [Record, Record]:

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

Sicher, es sind Abstraktionen verfügbar; aber da Record in deinem Design immer wieder korrigiert wird, haben sie wenig Wert.

+0

Das ist eine ziemlich saubere Lösung, denke ich. Ich könnte ein bisschen warten, bevor ich diese Antwort annehme, für den Fall, dass etwas mehr in Einklang mit dem steht, wonach ich gesucht habe, aber das würde definitiv funktionieren :). Ich hatte gehofft, einfach map/sum/zip zu benutzen, um das zu tun. Kennen Sie die Gründe dafür, dass zip nicht mit Tupeln arbeiten kann? – frio

+0

Dies liegt daran, dass Tuple2.zip Delegaten in die Sammlung in _1 verschoben hat und ein Tupel keine Sammlung ist. Es sollte in der Theorie möglich sein, aber für fast jeden vorstellbaren Gebrauch wäre es besser, wenn man richtige Sammlungen benutzt. –

+1

Es liegt daran, dass Record für das Design von zentraler Bedeutung ist, dass es sich lohnt, auf einer Handvoll Utility-Methoden zu bestehen. – IttayD

0

Sie erhalten den Fehler, weil Sie das Tupel als eine Sammlung behandeln.

Ist es möglich, Listen anstelle von Tupeln zu verwenden? Dann ist die Berechnung einfach:

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

Ich würde es vorziehen, keine Listen zu verwenden ("Seq" erstellt standardmäßig Listen), weil ich dann nicht im Typsystem einchecken kann, dass die Datensätze die Länge 6 haben; Ich bin gezwungen, das zur Laufzeit zu tun, was ich vermeiden möchte. Trotzdem danke :). – frio

6

Ich erhielt diese kleine Inspiration.

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] = [email protected] 

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

Ja, ein Haufen Anys kommt am anderen Ende heraus. Nicht wirklich aufregend, aber nicht viel, was du tun kannst, wenn du dich auf diese Weise auf Tuples drängen willst.

Edit: oh, und natürlich gibt das System geben Sie den vollen monty hier.

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

Es zeigt definitiv die Notwendigkeit einer homogenen Sammlung fester Größe. Nun, wenn wir nur die Anzahl der Elemente als Peano-Nummer im Sammlungs-Typ codieren können ... (http://jim-mcbeath.blogspot.com/2008/11/practical-church-numerals-in-scala.html) . Total Overkill für dieses Problem! –

5
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)) 

    } 
} 

Nutzung:

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

kurze Lösung :

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

Nutzung:

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

Danke dafür. Sehr kurz und sauber. – frio

2

Sie können nun diese leicht erreichen mit shapeless, auf diese Weise:

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) 

Der Nachteil ist die seltsame Syntax Sie die sum Funktion auszudrücken verwenden müssen, aber alles ist typsicher und Typ-geprüft .

+0

Ich liebe diese Lösung, denn in einem sich entwickelnden Projekt könnte sich die Tupelgröße im Laufe der Zeit ändern und der einmal geschriebene Code bleibt unberührt. –

0

Als Update auf Rex Kerr Antwort, ab Scala 2.10 können Sie implicit classes verwenden: syntaktischer Zucker, der diese Lösung noch kürzer macht.

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)) 
}