我是Haskell的新手,一直在努力学习基础知识。
假设我有以下列表y:
3:3:2:1:9:7:3:[]
我试图找到一种方法来删除列表y中第一次出现的3。这是否可以使用简单的列表理解?
我尝试了什么(此方法从列表中删除所有实例):
deleteFirst _ [] = []
deleteFirst a (b:bc) | a == b = deleteFirst a bc
| otherwise = b : deleteFirst a bc
答案 0 :(得分:6)
不,使用列表理解是不可能的。在列表推导中,您只能根据该元素决定保留哪个元素。在您的示例中,您希望以不同于其他3的方式处理您遇到的前3个(因为您只想删除第一个),因此决策不依赖于单独的元素。因此列表理解不起作用。
您使用递归函数的尝试已经非常接近了,除了正如您所说,它会删除所有实例。为什么删除所有实例?因为在删除第一个后,再次在列表的其余部分调用deleteFirst
,这将删除下一个实例,依此类推。要解决此问题,请在删除第一个实例后再次调用deleteFirst
。因此,在这种情况下,只需使用bc
代替deleteFirst a bc
。
答案 1 :(得分:3)
正如其他已经提到的列表理解不是这个任务的合适解决方案(难以一步终止执行)。
您几乎已经编写了正确的解决方案,只是在与匹配值相等的情况下,您必须通过返回没有匹配元素的列表的其余部分来终止计算:
deleteFirst _ [] = []
deleteFirst a (b:bc) | a == b = bc
| otherwise = b : deleteFirst a bc
> print $ deleteFirst 3 (3:3:2:1:9:7:3:[])
> [3,2,1,9,7,3]
答案 2 :(得分:2)
我不相信列表理解能做到这一点(至少不是以任何惯用的方式)。
您的deleteFirst
几乎可以正常工作。您需要更改为修复的所有内容是在第一次匹配后停止删除,即在第一个子句中将deleteFirst a bc
替换为bc
。
答案 3 :(得分:0)
map
,filter
,foldr
等操作统一处理所有列表项,了解它们的重要事项是每个步骤可用的信息,以及每个步骤的步骤结果与其他步骤相结合。
但我想强调的是,我认为你应该真正尝试在库函数方面解决这些问题。使解决方案从this older answer of mine适应您的问题:
deleteFirst x xs = beforeX ++ afterX
-- Split the list into two pieces:
-- * prefix = all items before first x
-- * suffix = all items after first x
where (beforeX, xAndLater) = break (/=x) xs
afterX = case xAndLater of
[] -> []
(_:xs) -> xs
诀窍是break
已经具有内置的“达到第一击”行为。作为进一步的练习,您可以尝试编写自己的break
版本;学习如何编写这些小型,通用和可重用的函数总是有益的。