以递归方式定义inits函数

时间:2018-01-16 14:11:47

标签: list haskell recursion

在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)]

我不确定如何通过按正确顺序追加一个元素来创建列表?有人指向正确的方向,还是不可能通过递归?

3 个答案:

答案 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))

How does foldr work?