2

I'm having trouble writing this function that takes a predicate and a list of integers, then eliminates the last occurrence of the integer that satisfies the predicate in the list. I was able to take out the first occurrence of the predicate in the list with my function below:

fun :: (Int -> Bool) -> [Int] -> [Int]
fun check (s:ss)
 |check s = ss
 |otherwise = s : fun check ss

What I need help on is how I should modify this function to take out the last occurrence of the integer, instead of the first. For example, fun (<2) [3,4,1,5,0,-3,9] would return [3,4,1,5,0,9].

dfeuer
  • 48,079
  • 5
  • 63
  • 167
T-Bird
  • 187
  • 1
  • 4
  • 20
  • 1
    How about writing it as `reverse . removeFirstOccurance predicate . reverse` ? – ErikR Nov 13 '15 at 02:53
  • I've tried doing a reverse, I'm trying to keep it within the same function. – T-Bird Nov 13 '15 at 03:01
  • @user3237465 - can you explain what you mean? – ErikR Nov 13 '15 at 03:10
  • @ErikR, `reverse` always traverses the whole list, so your function doesn't work with infinite lists. See my answer for an alternative approach. – effectfully Nov 13 '15 at 03:17
  • @T-Bird near-duplicate near-verbatim copy of https://stackoverflow.com/questions/33683079/haskell-function-that-takes-out-last-occurrence-of-input-character, asked 3 hours prior by yourself. just replace `c1 == c2` with `p s` or something. – Will Ness Nov 13 '15 at 03:49
  • @T-Bird, so basically what you want to do is: take out the LAST element of an INFINITE list which satisfies a given predicate? I'm not sure that makes a lot of sense to me.... – babon Nov 13 '15 at 04:33
  • Possible duplicate of [Haskell - Filter Last Element](http://stackoverflow.com/questions/31864951/haskell-filter-last-element) – amalloy Nov 13 '15 at 07:46
  • @amalloy, ah, I was sure I already wrote this obfuscated code as in the accepted answer below. Anyway, **dfeuer**'s solution is nicer than any in the thread you mentioned. – effectfully Nov 13 '15 at 08:05

3 Answers3

4

(I couldn't use where due to some indentation problems)

removeLast :: (a -> Bool) -> [a] -> [a]
removeLast p xs =
    let
        go c  []    = tail (c [])
        go c (x:xs)
            | p x       = c (go (x:) xs)
            | otherwise = go (c . (x:)) xs
    in case break p xs of
        (ok, [])   -> ok
        (ok, x:xs) -> ok ++ go (x:) xs

go collects elements for which the predicate doesn't hold in a difference list and prepends this list to the result once a new satisfying the predicate element is found. Pattern matching on break p xs ensures that difference lists always start with an element that satisfies the predicate and we can drop it if it's the last.

Works with infinite lists:

main = do
    print $ removeLast (< 2) [3,4,1,5,0,-3,9]        -- [3,4,1,5,0,9]
    print $ removeLast (== 2) [1,3]                  -- [1,3]
    print $ take 10 $ removeLast (< 2) (cycle [1,3]) -- [1,3,1,3,1,3,1,3,1,3]

Here is an obfuscated version:

removeLast :: (a -> Bool) -> [a] -> [a]
removeLast p xs = case break p xs of
    (ok, [])   -> ok
    (ok, x:xs) -> ok ++ foldr step (tail . ($[])) xs (x:) where
        step x r c = if p x then c (r (x:)) else r (c . (x:))
effectfully
  • 12,325
  • 2
  • 17
  • 40
3

If you want to have fun with it, try this version.

removeLast :: (a -> Bool) -> [a] -> [a]
removeLast p = fst . foldr go ([], False) where
  go x ~(r, more)
    | p x = (if more then x : r else r, True)
    | otherwise = (x : r, more)

This seems to be almost as lazy as it can be, and it gets to the point pretty quickly. It could produce the list spine more lazily with some effort, but it produces list elements maximally lazily.


After some more thought, I realize that there is some tension between different aspects of laziness in this case. Consider

removeLast p (x : xs)

There are two ways we can try to find out whether to produce a [] or (:) constructor.

  1. We can check xs; if xs is not [], then we can produce (:).

  2. We can check p x. If p x is False, then we can produce (:).

These are the only ways to do it, and their strictness is not comparable. The only "maximally lazy" approach would be to use parallelism to try it both ways, which is not the most practical approach.

dfeuer
  • 48,079
  • 5
  • 63
  • 167
0

How about this:

fun :: (Num a) => (a -> Bool) -> [a] -> [a]
fun check (s:ss)
    |check s = ss
    |otherwise = s : fun check ss

Then, apply your fun function like this:

reverse $ fun (\ x -> x `mod` 3 == 0) (reverse [1..10])

HTH

babon
  • 3,615
  • 2
  • 20
  • 20
  • `take 10 $ removeLast (\x -> rem x 3 == 0) [1..]` with your version `removeLast p = reverse . fun p . reverse` results in out of memory. – effectfully Nov 13 '15 at 05:12
  • Yep, it does. The problem here is to take out the LAST element (which satisfies a predicate) from an INFINITE list... this doesn't make a lot of sense to me... For example, how do you take out the last number having % 3 == 0 from an infinite list of random numbers (although the list is ordered in my answer above, let's consider a general list of random numbers)? – babon Nov 13 '15 at 07:55
  • My and **dfeuer**'s versions (two other answers) give [1..10] for this example. – effectfully Nov 13 '15 at 08:16
  • @user3237465 : BTW your version is still printing numbers even as I type this comment. Have you tried putting this line in your main? print $ removeLast (\ x -> x `mod` 3 == 0) [1..] – babon Nov 14 '15 at 06:16
  • Sure, the list is infinite. The point is that for any number you know that it's not the last that satisfies `rem x 3 == 0`, and hence you can yield this number. Your version doesn't yield, mine yields. If you try to print `[1..]`, you'll get an infinite loop as well. – effectfully Nov 14 '15 at 06:25