在Scala中组合嵌套列表 - 扁平化笛卡尔积

时间:2013-09-19 10:42:59

标签: scala

我有一个有趣的问题,对Scala的新手来说很难证明 我需要结合两个列表:

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中的一个内容,但到目前为止它还在逃避我。

2 个答案:

答案 0 :(得分:9)

您想要执行扁平笛卡尔积。 For-comprehensions是最简单的方法,可能看起来与您的Java解决方案类似:

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

答案 1 :(得分:7)

scand1skanswer几乎肯定是你应该在这里使用的方法,但作为旁注,还有另一种思考这个问题的方法。你正在做的是提取追加操作添加到列表的applicative functor中。这意味着使用Scalaz可以编写以下内容:

import scalaz._, Scalaz._

val listC = (listA |@| listB)(_ :+ _)

我们可以将(_ :+ _)视为一个获取事物列表的函数,以及一个相同类型的函数,并返回一个新列表:

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

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

与for-comprehension案例一样,如果你不关心秩序,你可以通过使用::并翻转参数的顺序来提高操作效率。

有关详细信息,请参阅我在Haskell中有关笛卡尔积的相似答案here,Scala中的这个introduction to applicative functorsthis blog post关于使此类事情的语法少一些斯卡拉丑陋。如果你不在乎,当然可以随意忽略上述所有内容。