组合列表功能

时间:2015-03-15 23:34:06

标签: scala

下面的函数计算List元素的可能组合。

不返回相同的列表元素,也只返回唯一的元素:

def combinations[T](l: List[T]): List[(T,T)] = l match {
    case Nil => Nil
    case h::Nil => Nil
    case h::t => t.map(x=>(h,x)) ++ combinations(t)
}

列表(1,2,3)返回列表((1,2),(1,3),(2,3))

这个解决方案(不是我的)很优雅,但我想知道它背后的直觉。代码中是否包含我不知道的列表元素的泛型属性?我知道为什么这个解决方案有效,但我不确定如何达到这个解决方案?

2 个答案:

答案 0 :(得分:3)

当您考虑如何手动构建所有组合时,它实际上非常直观。例如,取List(1, 2, 3, 4)。为了有条不紊地创建所有组合,我将获取列表1中的第一个元素,然后将其与所有剩余元素组合。

(1, 2), (1, 3), (1, 4)

这些是包含1的所有可能组合。现在让我们找到包含2的所有组合,但我们不需要包含1的组合,因为我们已经拥有它们。这意味着我们将采用与列表中其余元素的组合。

(2, 3), (3, 4)

然后使用3

(3, 4)

你看到了这种模式吗?我们取列表的第一个元素,然后将它与列表中的所有剩余元素(尾部)配对。这就是代码的这一部分:

case h :: t => t.map(x => (h, x))
 //  1 :: List(2, 3, 4) => List((1, 2), (1, 3), (1, 4))

然后我们移动到列表的下一个元素,并执行相同的操作。这是递归步骤:++ combinations(t),并使用++汇总结果。

如果我们从1开始,那么第一次递归调用是combinations(List(2, 3, 4)),我们重复逻辑:

case h :: t => t.map(x => (h, x))
 //  2 :: List(3, 4) => List((2, 3), (3, 4))

最后:

case h :: t => t.map(x => (h, x))
 //  3 :: List(4) => List((3, 4))

所以我们以及List((1, 2), (1, 3), (1, 4)) ++ List((2, 3), (3, 4)) ++ List((3, 4))

当然,存在零个或一个元素的其他情况不能产生更多组合:

case Nil => Nil
case h :: Nil => Nil

正如@ 0__所述,h :: Nil可以真正由h :: t处理,因为我们会有:

case h :: t => t.map(x => (h, x)) ++ combinations(t)
//        ^ Nil    ^ Nil maps to Nil          ^ Will hit the first case on the next call, which is also Nil

答案 1 :(得分:1)

使用List定义功能/递归解决方案时,最简单的方法是覆盖发生的基本情况。然后,您可以定义部分解决方案并将它们添加到一起。

你想要输出中的元素对,所以你首先要找出一个空列表(Nil)或一个只包含一个元素(h :: Nil)的列表,它没有部分解决方案,因此这两个案例的结果为Nil。最后一种情况是你有一个头元素h和一个非空尾。因此,使用h函数为每个尾部元素生成所有map对,并为尾部递归重复。

请注意,从技术上讲,中间案例无关紧要。以下就足够了:

def combinations[A](xs: List[A]): List[(A, A)] = xs match {
  case Nil => Nil
  case h :: t => t.map(h -> _) ++ combinations(t)
}
相关问题