3

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

4 回答 4

6

不,不可能使用列表理解。在列表理解中,您仅根据该元素决定保留哪个元素。在您的示例中,您希望将遇到的前 3 个与其他 3 区别对待(因为您只想删除第一个),因此该决定不仅仅取决于元素。所以列表理解是行不通的。

您使用递归函数的尝试已经非常接近了,只是,正如您所说,它删除了所有实例。为什么它会删除所有实例?因为在删除第一个实例后,您deleteFirst再次调用列表的其余部分,这将删除下一个实例,依此类推。deleteFirst要解决此问题,请在删除第一个实例后不要再次调用。所以在那种情况下只使用bc而不是。deleteFirst a bc

于 2013-02-04T14:27:16.213 回答
4

由于其他已经提到的列表理解不是此任务的适当解决方案(很难在一个步骤中终止执行)。

您几乎已经编写了正确的解决方案,只是在与匹配值相等的情况下,您必须通过返回没有匹配元素的列表的其余部分来终止计算:

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]
于 2013-02-04T16:06:03.970 回答
2

我不相信可以通过列表理解来做到这一点(至少不是以任何惯用的方式)。

你的deleteFirst作品差不多。您需要更改的只是在第一次匹配后停止删除,即deleteFirst a bc在第一个子句中替换为bc.

于 2013-02-04T14:26:37.673 回答
0

sepp2k 关于列表推导的评论很重要。像map, filter,foldr等等这样的列表操作对所有列表项进行统一处理,了解它们的重要一点是每个步骤可以获得哪些信息,以及每个步骤的结果如何与其他步骤的结果相结合。

但我想强调的一点是,我认为你真的应该尝试从库函数的角度来解决这些问题。将我的旧答案中的解决方案改编为您的问题:

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; 学习如何编写这些小型、通用和可重用的函数总是很有启发性的。

于 2013-02-04T21:31:30.383 回答