我可以删除列表中所有元素的出现:
*Main> let d = [1, 2, 3, 4, 5, 6]
*Main> d
[1,2,3,4,5,6]
*Main> [x | x <- d, not(x == 2)]
[1,3,4,5,6]
我只是想知道是否有可能只删除列表中FIRST出现的元素,但是使用列表理解?
答案 0 :(得分:4)
不,没有。列表理解
[ x | x <- d, GUARD ]
根据定义,与以下内容相同:
let ok x = if GUARD then [x] else []
ok _ = []
in concatMap ok d
通过'if'的定义,GUARD必须是一个纯布尔表达式(即单独评估为True of False),因此当你映射到列表时它无法跟踪状态(假设你要玩规则)。
话虽如此,有一种方法可以使用理解:将状态压缩到输入列表中并在该复合列表上运行列表推导。此复合列表可能具有类似[(Int, Bool)]
的类型,其中Bool指示这是否是列表中的第一项。然后你做了类似的事情:
[ x | (x, isFirst) <- zip d (findFirsts d), not (x == 2 && isFirst)]
将findFirsts d
的实施作为练习留给读者。
但是你不想在这种特殊情况下这样做。这是一个糟糕的解决方案,因为它基本上意味着你要经历列表至少两次,一次找出哪些项目是第一,一次实际过滤掉你不想要的项目。如果你天真地实现了findFirsts
,那么你可能会看到更多的工作而不是那些工作。这不是工作的正确工具!
对于某些问题,例如检查头部或将项目的特定位置合并到结果中(如 hvr 所示),这可能是一种非常有效的技术。
另外两种方式:
当您按顺序遍历列表时,使用monadic计算来携带状态。对于你想要遍历任意或复杂结构的情况,或者你的计算会变得复杂的情况,可能会好的,但在这种情况下,如果你这样做,你会更好:
只需使用一个简单的递归函数解决方案,这是Data.List.delete
和deleteBy
所做的。
答案 1 :(得分:3)
对于记录,我想指出delete模块中的Data.List函数提供了您描述的行为。
所以你可以作弊,只需在列表理解中使用delete:
> let l = [1,2,3,2,1]
> [x | x <- delete 2 l]
[1,3,2,1]
我想这不算数。
...所以,我很好奇如何做到这一点,这是一个不使用delete的解决方案:
-- Removes the first occurrence of '2' in 'l', if any.
[x | (x,y) <- zip l [0..], let idx = elemIndex 2 l, idx == Nothing || y /= fromJust idx]
这个想法是首先将列表转换为元组列表,其中每个元组的第二个元素是元素的索引,例如, "abcba"
变为[('a',0),('b',1),('c',2),('b',3),('a',4)]
。然后我们获取所有元组的每个第一个元素,其中第二个元组元素不等于'elemIndex'返回的值(它返回给定元素的第一个出现的位置)。例如,elemIndex 'b' "abca"
产生2
,因此我们采用所有元组的第一个元素,其中第二个元素不是2
。这会产生"acba"
。
答案 2 :(得分:0)
以下仅在头部位置发生时才删除元素:
[ x | (i, x) <- zip [0..] d, if i == 0 then x /= 2 else True ]
(这不是问题)
答案 3 :(得分:0)
不直接。列表推导仅相当于使用concat
和map
。它们统一映射元素 - 如果a
更改为b
(或删除,或更改为多个元素),则a
的所有出现都会相同。
一种丑陋的方式是用数字标记元素并搜索第一个:
f r x = let x' = zip x [0..]
(_,n) = head [v | v <- x', fst v == r]
in [y | (y,m) <- x', y /= r || m /= n]
如果您使用扩展名“parallel list comprehensions”,则可以使用LC表示第一个zip
。这非常非惯用,更好地使用显式递归或Data.List.delete
。