2

How to implement insert using foldr in haskell. I tried:

insert'' :: Ord a => a -> [a] -> [a]
insert'' e xs = foldr (\x -> \y -> if x<y then x:y else y:x) [e] xs

No dice. I have to insert element e in list so that it goes before first element that is larger or equal to it.

Example:

insert'' 2.5 [1,2,3] => [1.0,2.0,2.5,3.0]
insert'' 2.5 [3,2,1] => [2.5,3.0,2.0,1.0]
insert'' 2 [1,2,1]   => [1,2,2,1]

In last example first 2 is inserted one.

EDIT: Thanks @Lee.

I have this now:

insert'' :: Ord a => a -> [a] -> [a]
insert'' e xs = insert2 e (reverse xs)
insert2 e = reverse .  snd . foldr (\i (done, l) -> if (done == False) && (vj e i) then (True, e:i:l) else (done, i:l)) (False, [])
    where vj e i = e<=i

But for this is not working:

insert'' 2 [1,3,2,3,3] => [1,3,2,2,3,3]
insert'' 2 [1,3,3,4]   => [1,3,2,3,4]
insert'' 2 [4,3,2,1]   => [4,2,3,2,1]

SOLUTION:

insert'' :: Ord a => a -> [a] -> [a]
insert'' x xs = foldr pom poc xs False
  where
    pom y f je
      | je || x > y = y : f je
      | otherwise   = x : y : f True
    poc True = []
    poc _    = [x]

Thanks @Pedro Rodrigues (It just nedded to change x>=y to x>y.)

(How to mark this as answered?)

jvinkovic
  • 35
  • 3
  • 7
  • 4
    folds are normally used for reducing lists, not expanding them. Also, your description and your third example are conflicting. By your description it should be inserted before the 3, like in your first example. – bheklilr Dec 13 '13 at 14:11
  • @bheklilr: What is the best way of solving this ? Is it explicit recursion or any higher order function exists for solving this ? – Sibi Dec 13 '13 at 14:35
  • Quite an important note: `if x –  Dec 13 '13 at 15:32
  • 1
    @excrucio To mark as answered, you have to click on the check of your preferred answer. – Pedro Rodrigues Dec 13 '13 at 15:43

3 Answers3

3

You need paramorphism for that:

para  :: (a -> [a] -> r -> r) -> r -> [a] -> r
foldr :: (a ->        r -> r) -> r -> [a] -> r

para  c n (x : xs) = c x xs (para c n xs)
foldr c n (x : xs) = c x    (foldr c n xs)
para  _ n []       = n
foldr _ n []       = n

with it,

insert v xs = para (\x xs r -> if v <= x then (v:x:xs) else (x:r)) [v] xs

We can imitate paramorphisms with foldr over init . tails, as can be seen here: Need to partition a list into lists based on breaks in ascending order of elements (Haskell).

Thus the solution is

import Data.List (tails)

insert v xs = foldr g [v] (init $ tails xs)
  where
    g xs@(x:_) r | v <= x    = v : xs
                 | otherwise = x : r

Another way to encode paramorphisms is by a chain of functions, as seen in the answer by Pedro Rodrigues, to arrange for the left-to-right information flow while passing a second copy of the input list itself as an argument (replicating the effect of tails):

insert v xs = foldr g (\ _ -> [v]) xs xs
  where
    g x r xs | v > x     = x : r (tail xs)   -- xs =@= (x:_)
             | otherwise = v : xs

-- visual aid to how this works, for a list [a,b,c,d]:
-- g a (g b (g c (g d (\ _ -> [v])))) [a,b,c,d]

Unlike the version in his answer, this does not copy the rest of the list structure after the insertion point (which is possible because of paramorphism's "eating the cake and having it too").

Will Ness
  • 70,110
  • 9
  • 98
  • 181
2

Here's my take at it:

insert :: Ord a => a -> [a] -> [a]
insert x xs = foldr aux initial xs False
  where
    aux y f done
      | done || x > y = y : f done
      | otherwise = x : y : f True
    initial True = []
    initial _ = [x]

However IMHO using foldr is not the best fit for this problem, and for me the following solution is easier to understand:

insert :: Int -> [Int] -> [Int]
insert x [] = [x]
insert x z@(y : ys)
  | x <= y = x : z
  | otherwise = y : insert x ys
Will Ness
  • 70,110
  • 9
  • 98
  • 181
Pedro Rodrigues
  • 1,732
  • 11
  • 17
  • your last solution is explicitly recursive. The point of using `fodlr` is to be *implicitly* recursive, to let the `foldr` to encapsulate the recursion. The very use of `foldr` indicates recursion; the explicitly recursive code needs to be interpreted by a (human) mind, so "more clean" is in the eyes of the beholder. :) – Will Ness Dec 13 '13 at 16:10
  • @WillNess Granted a "cleaner" solution is very subjective, so I've changed the wording to state that it's just my opinion. The problem could also be solved with using implicit recursion using `unfoldr`, but so what? I still find the explicitly recursive solution easier to understand that any of the `foldr` solutions. – Pedro Rodrigues Dec 13 '13 at 16:53
  • of course, I never claimed you *should* be doing this or that. :) -- BTW `unfoldr` encapsulates *corecursion*. Just nitpicking. :) – Will Ness Dec 13 '13 at 17:49
  • @WillNess Hmm nitpicking... are you acquainted with [exercism](http://exercism.io/)? – Pedro Rodrigues Dec 13 '13 at 18:28
  • (you didn't use it, so I went ahead and edited it into my answer). :) – Will Ness Dec 13 '13 at 19:53
1

I suppose fold isn't handy here. It always processes all elements of list, but you need to stop then first occurence was found. Of course it is possible, but you probable don't want to use this:

insert' l a = snd $ foldl (\(done, l') b -> if done then (True, l'++[b]) else if a<b then (False, l'++[b]) else (True, l'++[a,b])) (False, []) l
user3974391
  • 701
  • 4
  • 14
  • This is with foldl and I do not get it. And why my if is not working? – jvinkovic Dec 13 '13 at 14:27
  • @excrucio Your program doesn't even type check so it is too soon to debug it. – user3974391 Dec 13 '13 at 14:37
  • Ok, I know, but what I am doing wrong. My "if" seems to me ok, but when I look at your, you use "\\(done, l') b" and I do not get why is that. Maybe a stupid question, but I am new to Haskell. – jvinkovic Dec 13 '13 at 14:48
  • @excrucio For example in your code there are `x:y` and `y:x`. Thus if x has type a then y must to have type [a]. But second bit of code requires vice versa types. Try to read compiler errors carefully. – user3974391 Dec 13 '13 at 14:53
  • @excrucio `done` is used to track whether the element was already inserted, so it's not inserted multiple times. – Pedro Rodrigues Dec 13 '13 at 14:55
  • This doesn't work correctly. Example: `insert' [1,2] 6 == [6,1,2]` – Pedro Rodrigues Dec 13 '13 at 15:11