在Data.List模块中,有一个函数可以转换为例如[1,2,3,4] - > [[],[1],[1,2],[1,2,3],[1,2,3,4]]
我正在尝试使用递归来定义类似的函数,但是我想不出按正确顺序执行的方法。我得到的最接近的是向后的列表,结果= [[],[4],[3,4],[2,3,4],[1,2,3,4]]:
inits' :: [Int] -> [[Int]]
inits' [] = [[]]
inits' (x:xs) = inits' xs ++ [(x:xs)]
我不确定如何通过按正确顺序追加一个元素来创建列表?有人指向正确的方向,还是不可能通过递归?
答案 0 :(得分:4)
尝试这样一个函数最简单的方法就是在函数方程的RHS上查看所需的结果和“反向模式匹配”。
你已经拥有了
inits' [] = [[]]
现在使用inits (x:xs)
,例如inits (1:[2,3,4])
,您知道结果应该是[[],[1],[1,2],[1,2,3],[1,2,3,4]]
,它与模式[]:_
匹配。所以
inits' (x:xs) = [] : _
现在,最简单的递归就是在inits'
再次呼叫xs
,就像
inits' (x:xs) = [] : inits' xs
然而,这并没有给出正确的结果:假设递归调用正常,你有
inits' (1:[2,3,4]) = [] : [[],[2],[2,3],[2,3,4]]
= [[],[],[2],[2,3],[2,3,4]]
1
完全缺失,显然是,因为我们在定义中并没有真正使用它。我们需要使用它,实际上它应该在递归结果中的所有列表块之前加上它。您可以使用map
。
答案 1 :(得分:3)
我们可以预先添加所有剩余inits
的数据,例如:
inits' :: [a] -> [[a]]
inits' [] = [[]]
inits' (x:xs) = [] : map (x:) (inits' xs)
作为基类,当输入为空列表时,我们返回带有空列表的单例列表。
在递归的情况下,我们首先产生空列表,然后是列表尾部的inits'
,但所有这些元素都是前置x
(与map (x:)
)。
然后我们有:
Prelude> inits' [1,4,2,5]
[[],[1],[1,4],[1,4,2],[1,4,2,5]]
由于(不是评估顺序):
inits' [1,4,2,5]
-> [] : map (1:) (inits' [4,2,5])
-> [] : map (1:) ([] : map (4:) (inits' [2,5]))
-> [] : map (1:) ([] : map (4:) ([] : map (2:) (inits' [5])))
-> [] : map (1:) ([] : map (4:) ([] : map (2:) ([] : map (5:) (inits' []))))
-> [] : map (1:) ([] : map (4:) ([] : map (2:) ([] : map (5:) [[]])))
-> [] : map (1:) ([] : map (4:) ([] : map (2:) ([] : [[5]])))
-> [] : map (1:) ([] : map (4:) ([] : map (2:) [[],[5]]))
-> [] : map (1:) ([] : map (4:) ([] : [[2],[2,5]]))
-> [] : map (1:) ([] : map (4:) [[],[2],[2,5]])
-> [] : map (1:) ([] : [[4],[4,2],[4,2,5]])
-> [] : map (1:) [[],[4],[4,2],[4,2,5]]
-> [] : [[1],[1,4],[1,4,2],[1,4,2,5]]
-> [[],[1],[1,4],[1,4,2],[1,4,2,5]]
答案 2 :(得分:3)
我认为你应该改变你的功能定义:
inits' :: [Int] -> [[Int]]
为:
inits' :: [a] -> [[a]]
由于inits
中的Data.List
类型为[a] -> [[a]]
,因此它并不关心列表中的实际内容。它需要是多态的并接受任何类型的列表。
此外,由于其他人已经展示了最简单的递归方法,您也可以在这里使用foldr
。
以下是基本代码:
inits' :: [a] -> [[a]]
inits' = foldr (\x acc -> [] : (map (x:) acc)) [[]]
[[]]
是基本情况,就像你的函数一样。对于实际的递归部分,以下是调用inits' [1, 2, 3, 4]
:
4
的右侧开始折叠,并创建[[], [4]]
3
,并创建[[], [3], [3, 4]
2
,并创建[[], [2], [2, 3], [2, 3, 4]]
1
,并创建[[], [1], [1, 2], [1, 2, 3], [1, 2, 3, 4]]
这给出了最终的嵌套列表,类似于函数调用:
*Main> inits' [1,2,3,4]
[[],[1],[1,2],[1,2,3],[1,2,3,4]]
根据上述行为,您只需关注[] : (map (x:) acc)
,您可以将当前值x
映射到累积列表acc
,同时还会预先列出一个空列表在每个折叠。
如果你仍然无法理解foldr
,你可以看一下这个折叠如何从右边起作用的最小例子:
foldr f x [a, b, c] = a `f` (b `f` (c `f` x))