5

異なるトラバースの複数のクロス積を構築しようとしています。望ましい戻り値の型は、入力traversablesの型と一致する型のタプルのトラバーサブルです。たとえば:Scala:複数のソースと異種の型を持つクロス(デカルト)プロダクト

List(1, 2, 3) cross Seq("a", "b") cross Set(0.5, 7.3) 

これは、次の3つのソースからのすべての可能な組み合わせでTraversable[(Int, String, Double)]を与える必要があります。 2つのソースのみを組み合わせた場合は、うまくanswered hereでした。

implicit class Crossable[X](xs: Traversable[X]) { 
    def cross[A](ys: Traversable[A]) = for { x <- xs; y <- ys } yield (x, y) 
} 

コメントが簡単に複数のソースの問題に言及したが、私はどちらか一方(型崩れやscalaz、私は「ドンに依存しない解決策を見つけるために探しています:与えられたアイデアがありますTuple22までスケールアップするための定型文を持っている)。私は何をしたいのは、次のようなものです:

implicit class Crossable[X](xs: Traversable[X]) { 
    def cross[A](ys: Traversable[A]) = for { x <- xs; y <- ys } yield (x, y) 
    def cross[A,B](ys: Traversable[(A,B)]) = // ... extend all Tuple2's in ys with x in xs to Tuple3's 
    def cross[A,B,C](ys: Traversable[(A,B,C)]) = // ... 
    // ... 
} 

これは明らかに伴う型消去に動作しません(と、残念ながら、おそらくcrossは右結合になるので、上記の例では、括弧を使用する必要があります)。

私の質問です:問題を解決するためにScala 2.10のリフレクション機能を利用することはどういうことですか?一般に、AXの両方をさまざまなタプルタイプ(およびそのタイプパラメータ、これは難しいと思われる)にマッチさせ、それらを大きなタプルにマージすると、連想法を満たす解決策が提供されるはずです。

答えて

5

私はそれで行くを持っていたし、この思い付いた:

trait Crosser[A,B,C] { 
    def cross(as: Traversable[A], bs: Traversable[B]): Traversable[C] 
} 

trait LowPriorityCrosserImplicits { 
    private type T[X] = Traversable[X] 

    implicit def crosser2[A,B] = new Crosser[A,B,(A,B)] { 
    def cross(as: T[A], bs: T[B]): T[(A,B)] = for { a <- as; b <- bs } yield (a, b) 
    } 
} 

object Crosser extends LowPriorityCrosserImplicits { 
    private type T[X] = Traversable[X] 

    implicit def crosser3[A,B,C] = new Crosser[(A,B),C,(A,B,C)] { 
    def cross(abs: T[(A,B)], cs: T[C]): T[(A,B,C)] = for { (a,b) <- abs; c <- cs } yield (a, b, c) 
    } 

    implicit def crosser4[A,B,C,D] = new Crosser[(A,B,C),D,(A,B,C,D)] { 
    def cross(abcs: T[(A,B,C)], ds: T[D]): T[(A,B,C,D)] = for { (a,b,c) <- abcs; d <- ds } yield (a, b, c, d) 
    } 

    // and so on ... 
} 

implicit class Crossable[A](xs: Traversable[A]) { 
    def cross[B,C](ys: Traversable[B])(implicit crosser: Crosser[A,B,C]): Traversable[C] = crosser.cross(xs, ys) 
} 

主なアイデアは、型クラス(Crosser)に作業を延期し、単にTraversable秒間特化して、すべての異なるアリティを実装することです対応するアリティから1を引いたタプルの数。 REPLでのいくつかのテスト:

scala> List(1, 2, 3) cross Seq("a", "b") cross Set(0.5, 7.3) 
res10: Traversable[(Int, String, Double)] = List((1,a,0.5), (1,a,7.3), (1,b,0.5), (1,b,7.3), (2,a,0.5), (2,a,7.3), (2,b,0.5), (2,b,7.3), (3,a,0.5), (3,a,7.3), (3,b,0.5), (3,b,7.3)) 
+0

うわー、それはかなりクールです!どうもありがとうございました!右結合バージョンを追加することも、このアプローチでは簡単です。私はtrait(それ以外の場合は常に一致する)に 'crosser2'を導入することで、"あいまいな暗黙の "コンパイラエラーを取り除いたことに気付きました。私は暗黙のためにある種のクラス階層依存優先ルールがなければならないと思いますか?私がまだ困惑しているもの:なぜcrosser2、crosser3、...が実際に範囲内にあるのですか?私は、「Crosser._」をインポートする必要があると予想していましたが、それはそうではないようです。 – bluenote10

+0

他の誰かがこれを使いたい場合:私はちょっとしたコードジェネレーター(マクロをいつか学ばなければならない)を書いて、[Gist](https://gist.github.com/bluenote10/5465957#file-crossproduct-scalaすべての定型文を適度に高いレベルまで含んでいます(19番目の型パラメータから、私は奇妙なコンパイラエラーが発生しますが、18は私にとっては十分以上のものです)。 – bluenote10

+1

あなたが 'Crosser._'を実行する必要がないのは、' Crosser'が暗黙的に 'Crossable.cross'に渡されているからです。暗黙の解決規則は' T型の暗黙の値'があれば、コンパイラは自動的に' T 'のコンパニオンオブジェクト(もしあれば)のメンバーを調べます。 SLS 7.2を参照してください –

関連する問題