简体   繁体   中英

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.

(.) 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)

Pattern matching is a perfectly reasonable approach:

evenOnly :: [a] -> [a]
evenOnly (_ : a : as) = a : evenOnly as
evenOnly _ = []

Another option is to use a list comprehension:

evenOnly as = [a | (a, True) <- zip as (cycle [False, True])]

The list comprehension version will likely be a bit more efficient if it fuses with other list processing functions.

One ought not to calculate anything that doesn't need calculating. The choice is positional, it is already known in advance. Calculating the modulos, comparing with Booleans, is all superfluous work.

Instead, do this , then do that , and go on switching like that; using foldr , as asked:

evenly :: [t] -> [t]
evenly xs = foldr c z xs f g
   where
   c x r f g = f x (r g f)

Next we finish up the definitions, according to how each is used:

   z _ _  = []
   f _ xs = xs
   g x xs = x : xs

You can just zip the above together with a sequence of numbers, like:

evenOnly :: [a] -> [a]
evenOnly = foldr (\(c, x) -> if c then (x:) else id) [] . zip (cycle [False, True])

Here cycle [False, True] thus generates an infinite list of [False, True, False, True, …] . In the foldr we check the corresponding value c that originates from the cycle [False, True] . If it is True , then we prepend the list with x , otherwise we just pass the result of the recursive call with id .

Or we can omit this, and use:

evenOnly :: [a] -> [a]
evenOnly = foldr (uncurry ($)) [] . zip (cycle [const id, (:)])

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM