当您考虑如何手动构建所有组合时,实际上非常直观。例如,以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