从列表中删除x的第一个实例

时间:2013-02-04 14:23:14

标签: list haskell

我是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

4 个答案:

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

sepp2k关于列表推导的评论是一个重要的事情要理解;列举mapfilterfoldr等操作统一处理所有列表项,了解它们的重要事项是每个步骤可用的信息,以及每个步骤的步骤结果与其他步骤相结合。

但我想强调的是,我认为你应该真正尝试在库函数方面解决这些问题。使解决方案从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版本;学习如何编写这些小型,通用和可重用的函数总是有益的。