Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell - how to iterate list elements in reverse order in an elegant way?

I'm trying to write a function that given a list of numbers, returns a list where every 2nd number is doubled in value, starting from the last element. So if the list elements are 1..n, n-th is going to be left as-is, (n-1)-th is going to be doubled in value, (n-2)-th is going to be left as-is, etc.

So here's how I solved it:

MyFunc :: [Integer] -> [Integer]
MyFunc xs = reverse (MyFuncHelper (reverse xs))

MyFuncHelper :: [Integer] -> [Integer]
MyFuncHelper []       = []
MyFuncHelper (x:[])   = [x]
MyFuncHelper (x:y:zs) = [x,y*2] ++ MyFuncHelper zs

And it works:

MyFunc [1,1,1,1] = [2,1,2,1]
MyFunc [1,1,1] = [1,2,1]

However, I can't help but think there has to be a simpler solution than reversing the list, processing it and then reversing it again. Could I simply iterate the list backwards? If yes, how?

like image 331
Zaroth Avatar asked Dec 08 '13 21:12

Zaroth


People also ask

How do you iterate backwards in a list?

Iterate over the list using for loop and reversed() reversed() function returns an iterator to accesses the given list in the reverse order. Let's iterate over that reversed sequence using for loop i.e. It will print the wordList in reversed order.

How do you get the first N elements of a list in Haskell?

There is a function in Haskell that takes first n elements of user-supplied list, named take . The syntax is: function-name arg1 arg2 . So, take takes first 1000 elements from an infinite list of numbers from 0 to infinity.

How do you get the last element in a list in Haskell?

Line 3: To get the last element in the list, we use the print() method. Here, we pass the last() function with the list as an argument.


3 Answers

The under reversed f xs idiom from the lens library will apply f to xs in reverse order:

under reversed (take 5) [1..100] => [96,97,98,99,100]
like image 128
John Wiegley Avatar answered Oct 06 '22 00:10

John Wiegley


When you need to process the list from the end, usually foldr works pretty well. Here is a solution for you without reversing the whole list twice:

doubleOdd :: Num a => [a] -> [a]
doubleOdd = fst . foldr multiplyCond ([], False)
    where multiplyCond x (rest, flag) = ((if flag then (x * 2) else x) : rest, not flag)

The multiplyCond function takes a tuple with a flag and the accumulator list. The flag constantly toggles on and off to track whether we should multiply the element or not. The accumulator list simply gathers the resulting numbers. This solution may be not so concise, but avoids extra work and doesn't use anything but prelude functions.

like image 29
Malcolm Avatar answered Oct 05 '22 23:10

Malcolm


myFunc = reverse
       . map (\(b,x) -> if b then x*2 else x)
       . zip (cycle [False,True])
       . reverse

But this isn't much better. Your implementation is sufficiently elegant.

like image 29
Vektorweg Avatar answered Oct 05 '22 23:10

Vektorweg