从列表中删除n个元素

时间:2014-10-22 13:34:45

标签: haskell

今年我的一个模块是Haskell编程。我很难用这么简单的方式表达复杂性,特别是来自C#等其他语言。

熟悉过程的一部分涉及我们实施一个下降过程。从列表中删除n个元素的函数。

写一个函数drop':: Int - > [a] - > [a],其中drop'n xs返回xs,其前n个元素被删除。

到目前为止,我想出的是这个。

drop' :: Int -> [a] -> [a]
drop' x [] = []
drop' x (y : ys) = if x == 0 then ys else drop' (x-1) ys  

我知道我需要递归调用drop'在列表的尾部有效删除一个元素,但我不知道如何计算我删除了多少元素,所以我可以确保删除n个元素。

我知道上面没有给出我期望的结果,因为它比我预期的要多1次,但有趣的是这有效:

drop' :: Int -> [a] -> [a]
drop' x [] = []
drop' x (y : ys) = if x == 1 then ys else drop' (x-1) ys

我无法推断出原因!任何有关逻辑的帮助将不胜感激!

3 个答案:

答案 0 :(得分:4)

在您的代码中:

drop' x (y : ys) = If x == 0 then ys else drop' (x-1) ys  

这是你的一个错误的来源:如果x为0,你想要保持整个列表不变(y:ys),而不仅仅是尾部(ys })。等效地,您可以检查1而不是0,然后返回ys

使用模式匹配而不是if会更加惯用,并且您应该将_无关心变量用于您不使用的值:

drop' :: Int -> [a] -> [a]
drop' _ [] = []            -- dropping any number of items from [] is still empty
drop' 0 lst = lst          -- dropping nothing returns the list unchanged
drop' n (_:xs) = drop' (n-1) xs  -- otherwise remove head and recurse on the tail

答案 1 :(得分:3)

到目前为止,您无需跟踪已删除的已删除的元素数量,只需删除尚未删除的元素,这是x参数。所以你已经有了这个部分。

你的第一个版本的函数问题是当x为0时你返回ys,但这只是列表的尾部,因为你已经通过模式删除了第一个元素 - 在参数列表中匹配。您需要使用if x == 0 then (y:ys)返回整个输入列表。

或者,您可以在递归情况下使用tail函数而不是模式匹配:

drop' x ys = if x == 0 then ys else drop' (x-1) (tail ys)

你可以将零个案放在一个单独的行上,而不是使用if,所以整个定义是:

drop' :: Int -> [a] -> [a]
drop' _ [] = []
drop' 0 ys = ys
drop' x ys = drop' (x-1) (tail ys)

答案 2 :(得分:0)

即使没有使用递归,以下方法也可能具有指导性。考虑一下这种理解,

drop' :: Int -> [a] -> [a]
drop' n xs = [ v | (_,v)  <- filter (\(x,_) -> x > n) $ zip [1..] xs ]

此示例中感兴趣的品质,

  • 我们(懒惰地)邮政列表xs,其索引值从1开始,直至xs中的元素数量;
  • filter包含一个lambda函数,用于提取压缩元组中的第一个元素,并将该值与要删除的元素数进行比较;
  • 对于每个已过滤的元组,我们提取第二个元素,即原始列表中的值。

然后例如

drop' 4 ['a'..'t']
"efghijklmnopqrst"