【问题标题】:Cartesian product of two lists两个列表的笛卡尔积
【发布时间】:2012-01-03 07:16:44
【问题描述】:

给定一个数字与多个字符相关联的地图

scala> val conversion = Map("0" -> List("A", "B"), "1" -> List("C", "D"))
conversion: scala.collection.immutable.Map[java.lang.String,List[java.lang.String]] =
  Map(0 -> List(A, B), 1 -> List(C, D))

我想根据数字序列生成所有可能的字符序列。例子:

"00" -> List("AA", "AB", "BA", "BB")
"01" -> List("AC", "AD", "BC", "BD")

我可以通过理解来做到这一点

scala> val number = "011"
number: java.lang.String = 011

为每个索引创建一系列可能的字符

scala> val values = number map { case c => conversion(c.toString) }
values: scala.collection.immutable.IndexedSeq[List[java.lang.String]] =
  Vector(List(A, B), List(C, D), List(C, D))

生成所有可能的字符序列

scala> for {
     | a <- values(0)
     | b <- values(1)
     | c <- values(2)
     | } yield a+b+c
res13: List[java.lang.String] = List(ACC, ACD, ADC, ADD, BCC, BCD, BDC, BDD)

这里的事情变得丑陋,它只适用于三位数的序列。有什么方法可以对任何序列长度达到相同的结果?

【问题讨论】:

  • 严格来说,你想要得到的不是笛卡尔积。如果你有List[T],你不保证大小,而且列表应该是同质的。

标签: scala for-comprehension


【解决方案1】:

以下建议不使用理解。但我认为这毕竟不是一个好主意,因为正如您所注意到的,您会被绑定到一定长度的笛卡尔积。

scala> def cartesianProduct[T](xss: List[List[T]]): List[List[T]] = xss match {
     |   case Nil => List(Nil)
     |   case h :: t => for(xh <- h; xt <- cartesianProduct(t)) yield xh :: xt
     | }
cartesianProduct: [T](xss: List[List[T]])List[List[T]]

scala> val conversion = Map('0' -> List("A", "B"), '1' -> List("C", "D"))
conversion: scala.collection.immutable.Map[Char,List[java.lang.String]] = Map(0 -> List(A, B), 1 -> List(C, D))

scala> cartesianProduct("01".map(conversion).toList)
res9: List[List[java.lang.String]] = List(List(A, C), List(A, D), List(B, C), List(B, D))

为什么不使用尾递归?

请注意,上述递归函数不是尾递归的。这不是问题,因为xss 会很短,除非您在xss 中有很多单例列表。之所以如此,是因为结果的大小随着xss的非单元素数量呈指数增长。

【讨论】:

    【解决方案2】:

    我可以想出这个:

    val conversion = Map('0' -> Seq("A", "B"), '1' -> Seq("C", "D"))
    
    def permut(str: Seq[Char]): Seq[String] = str match {
      case Seq()  => Seq.empty
      case Seq(c) => conversion(c)
      case Seq(head, tail @ _*) =>
        val t = permut(tail)
        conversion(head).flatMap(pre => t.map(pre + _))
    }
    
    permut("011")
    

    【讨论】:

    • 谢谢 Sciss,我接受了 ziggystar 的回答,因为他之前发布过,而且两个回答都很好。我特别喜欢你在flatMaps 链中分解for 的想法。
    • @Mark Sciss 的回答仅在语法上与我的不同(至少对于构建组合的部分而言)。他使用mapflatMap,我使用用于理解的语法糖,编译器将其转换为Sciss 的代码。唯一不同的是我提取了笛卡尔积的方法。
    • @ziggystar 太好了,现在我更了解幕后发生的事情了。
    【解决方案3】:

    我只是按照下面的方法做了,它可以工作

        def cross(a:IndexedSeq[Tree], b:IndexedSeq[Tree]) = {
            a.map (p => b.map( o => (p,o))).flatten
        }
    

    没有看到正在处理的 $Tree 类型也适用于任意集合..

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-01-25
      • 2015-07-29
      • 1970-01-01
      • 2012-03-24
      • 2017-03-05
      • 2019-08-26
      • 1970-01-01
      • 2011-05-06
      相关资源
      最近更新 更多