Scala:具有多个来源和异构类型的交叉(笛卡尔)积

标签 scala reflection type-erasure cartesian-product cross-product

我正在尝试构建不同(但每个同质)类型的可遍历项的多个交叉产品。所需的返回类型是元组的可遍历对象,其类型与输入可遍历对象中的类型相匹配。例如:

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

这应该给出 Traversable[(Int, String, Double)]来自三个来源的所有可能的组合。仅结合两个来源的情况很好 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到各种元组类型(及其类型参数,这似乎具有挑战性)并将它们合并到更大的元组应该提供满足关联律的解决方案,对吗?

最佳答案

我试了一下,想出了这个:

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 来实现所有不同的参数。 s 元组与相应的元数减一。
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:具有多个来源和异构类型的交叉(笛卡尔)积,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16219545/

相关文章:

java - 如何在scala中获取任何纪元的一天的开始?

java - 使用反射调用字段上的方法

c++ - 用于模拟运行时数字模板参数的成语?

java - 通配符类型、类型删除和运行时签名 : what happens on <T extends A & B> where A and B inherit a common ancestor?

scala - 如何将Specs2与Scalacheck结合使用以自动测试String参数?

json - 如果给出了额外的字段, jackson 反序列化应该成功,但如果缺少字段则失败

java - 类型删除是在 Java 编译器进行类型检查之前完成的吗?

c# - IKVM.net 不支持泛型(类型参数)吗?

scala - 如何一次到 "find"序列元素和谓词结果?

c# - 查明一个类型是否被引用,因此可能会在某个时间点被加载