今年我的一个模块是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
我无法推断出原因!任何有关逻辑的帮助将不胜感激!
答案 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"