问题描述
我正在尝试构建不同(但每个同质)类型的可遍历项的多个交叉乘积.所需的返回类型是元组的可遍历对象,其类型与输入可遍历对象中的类型相匹配.例如:
I'm trying to construct multiple cross products of traversables of different (but each homogeneous) types. The desired return type is a traversable of a tuple with the type matching the types in the input traversables. For example:
List(1, 2, 3) cross Seq("a", "b") cross Set(0.5, 7.3)
这应该给出一个 Traversable[(Int, String, Double)]
以及来自三个来源的所有可能的组合.在这里回答了仅结合两个来源的情况.给定的想法是:
This should give a Traversable[(Int, String, Double)]
with all possible combinations from the three sources. The case of combining only two sources was nicely answered here. The given idea is:
implicit class Crossable[X](xs: Traversable[X]) {
def cross[A](ys: Traversable[A]) = for { x <- xs; y <- ys } yield (x, y)
}
那里的评论简要提到了更多来源的问题,但我正在寻找一种不依赖于无形或 scalaz 的解决方案(另一方面,我不介意有一些样板来扩展到Tuple22
).我想做的是如下:
The comments there briefly mention the problem of more sources, but I'm looking to find a solution that does not depend on either shapeless or scalaz (on the other hand, I don't mind having some boilerplate to scale up to Tuple22
). What I would like to do is something like the following:
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
是右结合的).
This obviously does not work due to type erasure (and, unfortunately, would probably require to use parenthesis in the example above, because cross
would be right associative).
我的问题是:是否有可能利用 Scala 2.10 的反射特性来解决问题?通常,将 A
和 X
与各种元组类型(及其类型参数,这似乎具有挑战性)匹配并将它们合并到更大的元组应该提供满足关联的解决方案法律,对吗?
My question is: Is it somehow possible to exploit Scala 2.10's reflection features to solve the problem? In general, matching both A
and X
to the various tuple types (and their type parameters, which seems challenging) and merging them to larger tuples should provide a solution satisfying the associative law, right?
推荐答案
我试了一下,想出了这个:
I had a go at it and came up with this:
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
元组来实现所有不同的元组减一.REPL 中的一些测试:
The main idea is to defer the work to a type class (Crosser
) and implement all the different arities simply by specialising for Traversable
s of tuples with the corresponding arity minus one.Some test in the 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))
这篇关于Scala:具有多个来源和异构类型的交叉(笛卡尔)积的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!