列表推导在某种程度上定义了嵌套循环。因此,在定义中
combinations n xs =
我们可以阅读代码
[ y:ys | y:t <- tails xs, ys <- combinations (n-1) t]
作为
for each (y:t) in (tails xs)
for each ys in (combinations (n-1) t)
produce (y:ys) as a new element of the resulting list
换句话说,从列表中选择n 元素意味着选择某个元素,然后在列表中选择n-1 元素。
这个定义的不确定性通过生成一个所有可能解决方案的列表来表示,作为结果。我们只选择元素右侧的n-1元素,只生成在排列下唯一的解决方案。
我们以xs = [1,2,3] 为例。 tails [1,2,3] 产生什么?
当然是[[1,2,3], [2,3], [3], []]。现在,这相当于
[ r | r <- [[1,2,3], [2,3], [3], []] ]
这意味着,从该列表中抽取的r 会连续获取其元素的值。 r 是一个无可辩驳的模式; (y:t) 是一个可反驳的模式,即它将无法匹配 [] 元素:
[ (y,t) | (y:t) <- tails [1,2,3]]
=> [(1,[2,3]), (2,[3]), (3,[])]
所以你看,t 不是“还不知道”。它是已知的,它只是给定列表的尾部。而当y匹配3时,t匹配[]。
此外,
[ (y,q) | (y:t) <- tails [1,2,3], q <- [10,20]]
=> [(1,10), (1,20), (2,10), (2,20), (3,10), (3,20)]
这足以说明问题,希望能解决您的第一个问题:对于每个匹配的 (y:t) 模式,q 是从 [10,20] 中提取的,即它也采用列表中的值(此处为 [10,20] ) 连续,对于每个y,就像在一个嵌套循环中。
对于您的combinations 2 [1,2,3] 示例,我们有
combinations 2 [1,2,3]
=
for each (y,t) in [ (1,[2,3]), (2,[3]), (3,[]) ]
for each ys in (combinations 1 t)
produce (y:ys)
=
for y = 1, t = [2,3]
for each ys in (combinations 1 [2,3]) produce (1:ys) , and then
for y = 2, t = [3]
for each ys in (combinations 1 [3]) produce (2:ys) , and then
for y = 3, t = []
for each ys in (combinations 1 []) produce (3:ys)
combinations 1 [] 是 [],因为 tails [] 是 [[]] 并且模式匹配 (y:t) 和 [] 作为生成器 (y:t) <- [[]] 的一部分将失败;所以第三行 for 不会产生任何解决方案 (解决方案的头部将有 3 - 因为它的右边没有更多元素可以从中选择第二个元素,因为我们需要从整体上选择 2 个元素;3 确实参与了其他解决方案的尾部,它也应该这样做)。
第二行 for 显然只产生了一种解决方案,[2,3]。第一行 for 产生了什么?
for each ys in (combinations 1 [2,3]) produce (1:ys)
combinations 1 只需要一个元素,因此它会产生[2] 和[3];所以第一行for 产生两个解决方案,[1,2] 和[1,3]。或者更详细一点,
combinations 1 [2,3]
=
for y = 2, t = [3]
for each ys in (combinations 0 [3]) produce (2:ys) , and then
for y = 3, t = []
for each ys in (combinations 0 []) produce (3:ys)
而combinations 0 总是产生一个单一的解决方案,它是一个空列表(一个以空列表作为其唯一元素的单例列表,[ [] ],表示从列表中选择 0 个元素的解决方案)。
所以总的来说,返回了三个解决方案的列表,[[1,2], [1,3], [2,3]]。