Cross product in Scala

后端 未结 7 2025
滥情空心
滥情空心 2020-12-01 09:04

I want to have a binary operator cross (cross-product/cartesian product) that operates with traversables in Scala:

val x = Seq(1, 2)
val y = Lis         


        
相关标签:
7条回答
  • 2020-12-01 09:31

    You can do this pretty straightforwardly with an implicit class and a for-comprehension in Scala 2.10:

    implicit class Crossable[X](xs: Traversable[X]) {
      def cross[Y](ys: Traversable[Y]) = for { x <- xs; y <- ys } yield (x, y)
    }
    
    val xs = Seq(1, 2)
    val ys = List("hello", "world", "bye")
    

    And now:

    scala> xs cross ys
    res0: Traversable[(Int, String)] = List((1,hello), (1,world), ...
    

    This is possible before 2.10—just not quite as concise, since you'd need to define both the class and an implicit conversion method.

    You can also write this:

    scala> xs cross ys cross List('a, 'b)
    res2: Traversable[((Int, String), Symbol)] = List(((1,hello),'a), ...
    

    If you want xs cross ys cross zs to return a Tuple3, however, you'll need either a lot of boilerplate or a library like Shapeless.

    0 讨论(0)
  • 2020-12-01 09:33

    Here is the implementation of recursive cross product of arbitrary number of lists:

    def crossJoin[T](list: Traversable[Traversable[T]]): Traversable[Traversable[T]] =
      list match {
        case xs :: Nil => xs map (Traversable(_))
        case x :: xs => for {
          i <- x
          j <- crossJoin(xs)
        } yield Traversable(i) ++ j
      }
    
    crossJoin(
      List(
        List(3, "b"),
        List(1, 8),
        List(0, "f", 4.3)
      )
    )
    
    res0: Traversable[Traversable[Any]] = List(List(3, 1, 0), List(3, 1, f), List(3, 1, 4.3), List(3, 8, 0), List(3, 8, f), List(3, 8, 4.3), List(b, 1, 0), List(b, 1, f), List(b, 1, 4.3), List(b, 8, 0), List(b, 8, f), List(b, 8, 4.3))
    
    0 讨论(0)
  • 2020-12-01 09:39

    Here is something similar to Milad's response, but non-recursive.

    def cartesianProduct[T](seqs: Seq[Seq[T]]): Seq[Seq[T]] = {
      seqs.foldLeft(Seq(Seq.empty[T]))((b, a) => b.flatMap(i => a.map(j => i ++ Seq(j))))
    }
    

    Based off this blog post.

    0 讨论(0)
  • 2020-12-01 09:39

    Similar to other responses, just my approach.

    def loop(lst: List[List[Int]],acc:List[Int]): List[List[Int]] = {
      lst match {
        case head :: Nil => head.map(_ :: acc)
        case head :: tail => head.flatMap(x => loop(tail,x :: acc))
        case Nil => ???
      }
    }
    val l1 = List(10,20,30,40)
    val l2 = List(2,4,6)
    val l3 = List(3,5,7,9,11)
    
    val lst = List(l1,l2,l3)
    
    loop(lst,List.empty[Int])
    
    0 讨论(0)
  • 2020-12-01 09:45
    class CartesianProduct(product: Traversable[Traversable[_ <: Any]]) {
      override def toString(): String = {
        product.toString
      }
    
      def *(rhs: Traversable[_ <: Any]): CartesianProduct = {
          val p = product.flatMap { lhs =>
            rhs.map { r =>
              lhs.toList :+ r
            }
          }
    
          new CartesianProduct(p)
      }
    }
    
    object CartesianProduct {
      def apply(traversable: Traversable[_ <: Any]): CartesianProduct = {
        new CartesianProduct(
          traversable.map { t =>
            Traversable(t)
          }
        )
      }
    }
    
    // TODO: How can this conversion be made implicit?
    val x = CartesianProduct(Set(0, 1))
    val y = List("Alice", "Bob")
    val z = Array(Math.E, Math.PI)
    
    println(x * y * z) // Set(List(0, Alice, 3.141592653589793), List(0, Alice, 2.718281828459045), List(0, Bob, 3.141592653589793), List(1, Alice, 2.718281828459045), List(0, Bob, 2.718281828459045), List(1, Bob, 3.141592653589793), List(1, Alice, 3.141592653589793), List(1, Bob, 2.718281828459045))
    
    // TODO: How can this conversion be made implicit?
    val s0 = CartesianProduct(Seq(0, 0))
    val s1 = Seq(0, 0)
    
    println(s0 * s1) // List(List(0, 0), List(0, 0), List(0, 0), List(0, 0))
    
    0 讨论(0)
  • 2020-12-01 09:47

    cross x_list and y_list with:

    val cross = x_list.flatMap(x => y_list.map(y => (x, y)))
    
    0 讨论(0)
提交回复
热议问题