Search code examples
listhaskellsyntaxfoldtype-mismatch

Take elements on even positions from the list


Problem: using fold, take from the list elements which are on the even positions:

GHCi> evenOnly [1..10]
 [2,4,6,8,10]
GHCi> evenOnly ['a'..'z']
 "bdfhjlnprtvxz"

evenOnly :: [a] -> [a]
evenOnly = undefined

I decided at first to get a list of alternating 0-es and 1-s: [0,1,0,1..]

Prelude> let g = iterate (\x -> (x + 1) `mod` 2) 0
Prelude> take 10 $ g
[0,1,0,1,0,1,0,1,0,1]

Then zip it with the original list, getting a list of pairs: [(x1, 0), (x2,1), (x3,0) .. (xn, ?)]:

Prelude> zip g [1,2,3,4,5]
[(0,1),(1,2),(0,3),(1,4),(0,5)]

After that, foldr the list of pairs with a filtering function and an empty list as initialization value.

So I thought that this would work:

evenOnly :: [a] -> [a]
evenOnly xs = let g = iterate (\x -> (x + 1) `mod` 2) 0
  in
  foldr (\ (x, n) s -> if n == 1 then x : s else s) [] . (zip g xs)

But it gives an error, that I don't understand:

foldr.hs:44:59: error:
    • Couldn't match expected type ‘a0 -> t0 (a1, Integer)’
                  with actual type ‘[(Integer, a)]’
    • Possible cause: ‘zip’ is applied to too many arguments
      In the second argument of ‘(.)’, namely ‘(zip g xs)’
      In the expression:
        foldr (\ (x, n) s -> if n == 1 then x : s else s) [] . (zip g xs)
      In the expression:
        let g = iterate (\ x -> (x + 1) `mod` 2) 0
        in
          foldr (\ (x, n) s -> if n == 1 then x : s else s) [] . (zip g xs)
    • Relevant bindings include
        xs :: [a] (bound at foldr.hs:42:10)
        evenOnly :: [a] -> [a] (bound at foldr.hs:42:1)

I think my idea is correct, I just did something wrong with the syntax.


Solution

  • (.) is function composition but zip g xs is a list not a function. You can just apply the resulting list as the argument to foldr directly. Note you have the arguments g and xs in the wrong order:

    evenOnly :: [a] -> [a]
    evenOnly xs = let g = iterate (\x -> (x + 1) `mod` 2) 0
      in
      foldr (\ (x, n) s -> if n == 1 then x : s else s) [] (zip xs g)