【发布时间】:2019-08-12 10:29:50
【问题描述】:
我试图理解这段代码,它返回传递给它的[a] 的所有可能组合:
-- Infinite list of all combinations for a given value domain
allCombinations :: [a] -> [[a]]
allCombinations [] = [[]]
allCombinations values = [] : concatMap (\w -> map (:w) values)
(allCombinations values)
我在这里尝试了这个示例输入:
ghci> take 7 (allCombinations [True,False])
[[],[True],[False],[True,True],[False,True],[True,False],[False,False]]
在这里,我似乎无法理解,递归最终将如何停止并返回[ [ ] ],因为allCombinations 函数肯定没有任何指针在每次调用时在列表中移动,并且当它满足基本情况[ ] 时,它返回[ [ ] ]。据我说,它会调用allCombinations 函数无限并且永远不会自行停止。或者我可能错过了什么?
另一方面,在完成递归调用后返回执行所有计算后,take 仅返回来自final list 的第一个7 元素。那么实际上递归是如何在这里满足基本情况的呢?
其次这里concatMap的目的是什么,这里我们也可以使用Map函数只是将函数应用到列表中,内部函数我们可以排列列表吗? concatMap 到底在做什么。从定义上看,concatMap 告诉我们它首先映射函数,然后连接列表,正如我所见,我们已经在这里的函数内部这样做了?
任何有价值的意见将不胜感激?
【问题讨论】:
-
只有在将空列表作为参数传递给
allCombinations时才会使用基本情况。它从不用于递归情况。 -
即它不是基本情况,而是一种特殊情况,这不是递归而是核心递归,它从不停止。不错的代码/问题。 :)
-
旁注:如果你使用值递归而不是函数递归,这个函数可能会变得更快:
allCombinations values = let r = [] : concatMap (\w -> map (w:) values) r in r。尚未考虑过,但这是更强大的优化机会之一。 -
@luqui 实际上,我在(更新的)答案中提出了相反的主张。不过还没有测试过。