开发者

remove first occurence of element in the list using list comprehension

开发者 https://www.devze.com 2023-02-21 21:39 出处:网络
I can remove all occurences of element in the list: *Main> let d = [1, 2, 3, 开发者_开发技巧4, 5, 6]

I can remove all occurences of element in the list:

*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]

I just wondering if there is any possibility to remove only FIRST occurence of element in the list, but using list comprehension?


No, there isn't. The list comprehension

[ x | x <- d, GUARD ]

is equivalent by definition to the following:

let ok x = if GUARD then [x] else []
    ok _ = []
in concatMap ok d

By the definition of 'if', GUARD must be a pure boolean expression (i.e. evaluate to True of False alone), so it cannot keep track of state as you map over the list (assuming you're going to play by the rules).

Having said that, there is one way around this that uses comprehensions: zip state into your input list and run a list comprehension on that composite list. This composite list might have a type of something like [(Int, Bool)] where the Bool indicates whether this is the first item in the list. You then do something like:

[ x | (x, isFirst) <- zip d (findFirsts d), not (x == 2 && isFirst)]

where the implementation of findFirsts d is left as an exercise to the reader.

But you wouldn't want to do this in this particular case. It's a bad solution here because it basically means you're going to go through the list at least twice, once to figure out which items are the Firsts, and once to actually filter out the item(s) you don't want. If you implemented findFirsts naively, you might be looking at a bunch more work than even that. Not the right tool for the job!

For certain problems, though, like checking for the head or incorporating the specific position of an item into your results (as hvr has demonstrated), this can be a very effective technique.

Two other ways:

  1. Use monadic computations to carry state as you sequentially iterate through the list. Might be OK for cases where you want to traverse arbitrary or complicated structures, or where your computation is going to be complicated, but in this case, you're better off if you:

  2. Just use a simple recursive function solution, which is what Data.List.delete and deleteBy do.


For the record I wanted to point out that the delete function in the Data.List module provides exactly the behaviour you describe.

So you could cheat a bit and just use delete in your list comprehension:

> let l = [1,2,3,2,1]
> [x | x <- delete 2 l]
[1,3,2,1]

I guess this doesn't count.

...so, I was curious how to do this and here's a solution which doesn't use 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]

The idea is to first turn the list into a list of tuples where the second element of each tuple is the index of the element, e.g. "abcba" becomes [('a',0),('b',1),('c',2),('b',3),('a',4)]. Then we take each first element of all tuples for which the second tuple element does not equal the value returned by 'elemIndex' (which returns the position of the first occurance of the given element). For instance, elemIndex 'b' "abca" yields 2, so we take the first elements of all tuples where the second element is not 2. And that yields "acba".


The following removes the element only if occuring in head position:

[ x | (i, x) <- zip [0..] d, if i == 0 then x /= 2 else True ]

(which wasn't the question)


Not directly. List comprehensions are equivalent to using concat and map only. They map elements uniformly - if a is changed to b (or removed, or changed into several elements) then all occurences of a will do the same.

An ugly way would be to tag elements with numbers and search for the first one:

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]

First zip can be expressed with LC if you use extension "parallel list comprehensions". This is extremely nonidiomatic, better use explicit recursion or Data.List.delete.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号