Haskell从列表中删除1个元素的所有可能方法

时间:2017-01-10 19:06:15

标签: list haskell recursion

这是我到目前为止所写的内容,但我有点失落:

removeone :: [a] -> [[a]]
removeone [] = []
removeone (a:as) = [as] -- I'm lost here

这是我正在寻找的那种输出:

removeone [1,2,3] = [[2,3],[1,3],[1,2]]
removeone [1,2]   = [[1],[2]]

解决此问题的最佳方法是什么?在Java中,我只是循环这个并且每次生成一个新列表,我将附加到预先存在的列表。我很想把它翻译成Haskell。

2 个答案:

答案 0 :(得分:5)

让我们看一个稍长的例子,弄清楚如何分解它:

> removeone [1,2,3,4]
[[2,3,4],[1,3,4],[1,2,4],[1,2,3]]

正如您已经猜到的那样,您只需在某些列表的前面放置as(从输入中删除第一个元素的结果),但是那个列表是什么?

removeone (a:as) = as : ...

仔细观察,您可以看到a == 1位于每个人的前面。让我们考虑一下如何构建它:

[[1,3,4],[1,2,4],[1,2,3]] == [1:[3,4],1:[2,4],1:[2,3]]
                          == map (1:) [[3,4],[2,4],[2,3]]

第二个参数应该看起来很熟悉:这是你应该期望removeone [2,3,4]的结果。就像那样,你有你的递归案例:

removeone (a:as) = as : map (a:) (removeone as)

答案 1 :(得分:3)

惯用的递归解决方案由@сhepner提供。然而,每天的Haskell不是关于递归,而是关于递归模式和问题的精确表达。如果我得到它,我最终会得到以下解决方案

import Data.List (inits, tails)

removeone :: [a] -> [[a]]
removeone lst = zipWith (++) (inits lst) (tail (tails lst))

它比显式递归快2倍,看起来更像是问题的表达。使用“危险”功能tail在这里是安全的,因为tails [] = [[]]。琐碎案件由zipWithinitstails举行。

如果生成的补充列表应该是非平凡的处理(比如,它们代表组中每个游戏人物的视图),请考虑拉链并利用它们的共同属性(它实际上并不像听起来那样吓人:))。