简体   繁体   中英

Reverse every even element in Haskell list

In Haskell a list is given, but you should reverse every even element. For example the list

f ["rehtruF", "dooG", kcuL"]

should be changed into

["Further", "Good" "Luck"]

We tried this function:

f [] = []
f [x] = []
f (xs:ys:xss) = (reverse xs):(f xss)

but unfortunately, it only reversed the first element and prints it out. Do you have any idea how we could change the code, so that every even element is reserved and the output is as demonstrated above?

TL;DR - this is the solution (based on this answer ) that Thomas M. DuBuisson alluded to in his comment. Oops.


You don't even need to explicitly iterate over the input. Imagine you had a list of functions fs = [f0, f1, f2, ...] and a list of values xs = [x0, x1, x2, ...] , and you want to get [f0 x0, f1 x1, f2 x2, ...] . You do that with zipWith ($) :

zipWith ($) fs xs  -- == [f0 $ x0, f1 $ x1, f2 $ x2, ...]
                   -- == [f0 x0, f1 x1, f2 x2, ...]

For your problem, your fs would be an alternating sequence of reverse and id , which you can produce with cycle [reverse, id] == [reverse, id, reverse, id, ...]

Putting this together,

f :: [String] -> [String]
f strings = zipWith ($) (cycle [reverse, id]) strings
        -- == [reverse strings0, id strings0, reverse strings2, id strings3, ...]

or simply

f :: [String] -> [String]
f = zipWith ($) (cycle [reverse, id])

The problem is that you completely drop the ys element. What you actually want is keep it as-is , ie put it as-is in the result list.

f (xs:ys:xss) = reverse xs :  f xss

Note that GHC would have warned you about the unused ys binding, if you had turned on the -Wall option (which is generally a good idea for beginners). You can do that by executing the compiler/interpreter with ghc -Wall YOURFILE.hs , or by typing :set -Wall in a GHCi session.

To reverse the even elements in the list, the first thing is to find even elements, one of the ways to do that is to index each elements in the list firstly like:

zip [0..] xs

and then, we reverse the elements has even index:

if index `mod` 2 == 0 then reverse x else x

put them all as

f xs = map reverseEven (zip [0..] xs)
    where reverseEven (index, x) = if index `mod` 2 == 0 
                                   then reverse x else x

One ought not to calculate anything that doesn't need calculating. No zipping, no cycling, no mod taking, just pure function calling :

foo :: [[a]] -> [[a]]
foo xs = foldr (\ x r f g -> f x (r g f)) 
               (\ _ _ -> []) 
               xs 
               ((:) . reverse) 
               (:)

First element is considered to be at index 0, ie at even position, as is in the question.

> foo ["Further","Good","Luck"]
["rehtruF","Good","kcuL"]

> foo $ foo ["Further","Good","Luck"]
["Further","Good","Luck"]

With plain recursion, what the above foldr -based definition is doing is:

foo = f   where 
      f (x:xs)  =  reverse x : g xs
      g (x:xs)  =  x : f xs 

, plus the obvious [] -handling cases.

With foldr , [] is checked for by foldr itself; with mutual recursion, it must be done by each of the two cooperating functions.

If you add an extra variable, you can keep track if you are on an even element or an odd element. For example, here we are using 1 to mean odd and 0 to mean even:

f = f' 1
  where
    f' _ []     = []
    f' 1 (x:xs) = reverse x: f' 0 xs
    f' 0 (x:xs) = x: f' 1 xs

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