在 Scala 中组合嵌套列表 - 扁平化的 Carthesian 产品



我有一个有趣的问题,对于刚接触Scala的人来说,这个问题很困难。
我需要合并 2 个列表:

listA : List[List[Int]]
listB : List[Int]

通过以下方式:

val listA = List(List(1,1), List(2,2))
val listB = List(3,4)
val listC = ???
// listC: List[List[Int]] = List(List(1,1,3),List(1,1,4),List(2,2,3),List(2,2,4)

在 Java 中,我会使用几个嵌套循环:

for(List<Integer> list : listA) {
    for(Integer i: listB) {
        subList = new ArrayList<Integer>(list);
        subList.add(i);
        listC.add(subList);
    }
}

我猜这是 Scala 中的一句话,但到目前为止,它让我无法理解。

你想要执行一个扁平的笛卡尔乘积。理解是执行此操作的最简单方法,可能看起来类似于您的 Java 解决方案:

val listC = for (list <- listA; i <- listB) yield list :+ i

scand1sk的答案几乎可以肯定是你应该在这里使用的方法,但作为旁注,还有另一种思考这个问题的方法。您正在执行的操作实际上是将追加操作提升到列表的应用函子中。这意味着使用 Scalaz 您可以编写以下内容:

import scalaz._, Scalaz._
val listC = (listA |@| listB)(_ :+ _)

我们可以将(_ :+ _)视为一个函数,它接受事物列表和相同类型的单个事物,并返回一个新列表:

(_ :+ _): ((List[Thing], Thing) => List[Thing])

Scalaz 为列表提供了一个应用函子实例,因此我们实际上可以创建一个新函数,为上述每种类型添加一个额外的列表层。奇怪的(x |@| y)(_ :+ _)语法说:创建这样一个函数并将其应用于xy。结果就是你要找的。

就像在理解的情况下一样,如果你不关心顺序,你可以通过使用::和翻转参数的顺序来提高操作效率。

有关更多信息,请参阅我关于 Haskell 中的笛卡尔积的类似答案,Scala 中应用函子的介绍,或者这篇关于在 Scala 中使这种事情的语法不那么丑陋的博客文章。当然,如果您不在乎,请随时忽略上述所有内容。

最新更新