Apply a function to every second item in the list

I would like to apply a function to every second element in the list:

> mapToEverySecond (*2) [1..10] [1,4,3,8,5,12,7,16,9,20] 

I wrote the following function:

 mapToEverySecond :: (a -> a) -> [a] -> [a] mapToEverySecond fl = map (\(i,x) -> if odd i then fx else x) $ zip [0..] l 

This works, but I'm wondering if there is a more idiomatic way to do something like this.

+6
source share
6 answers

I did not write a lot of Haskell, but here is the first thing that came to mind:

 func :: (a -> a) -> [a] -> [a] func f [] = [] func f [x] = [x] func f (x:s:xs) = x:(fs):(func f xs) 

This is a bit ulgy, since you need to not only take care of the empty list, but also the list with one element. It also doesn't scale very well (what if you want every third, or

You can do as @Landei points out and write

 func :: (a -> a) -> [a] -> [a] func f (x:s:xs) = x:(fs):(func f xs) func f xs = xs 

To get rid of the ugly checks for [] and [x] , although IMHO, this makes it a little harder to read (at least for the first time).

+7
source

Here's how I do it:

 mapOnlyOddNumbered f [] = [] mapOnlyOddNumbered f (x:xs) = fx : mapOnlyEvenNumbered f xs mapOnlyEvenNumbered f [] = [] mapOnlyEvenNumbered f (x:xs) = x : mapOnlyOddNumbered f xs 

Regardless of whether this is โ€œidiomatic,โ€ it is a matter of opinion (and I would give it as a comment if it fit there), but it can be useful to see a number of different approaches. Your decision is just as fair as mine, or those in the comments, and easier to change to text mapOnlyEvery13nd or mapOnlyPrimeNumbered

+5
source
 mapToEverySecond = zipWith ($) (cycle [id, (*2)]) 

I am the smallest I can think of, also looks pretty clear in my opinion. He also has scales with every nth.

Edit: Oh, people have already suggested this in the comments. I do not want to steal it, but I really think that is the answer.

+3
source

Here's how I would do it:

 mapToEverySecond f xs = foldr go (`seq` []) xs False where go x cont !mapThisTime = (if mapThisTime then fx else x) : cont (not mapThisTime) 

But if I were writing library code, I would probably wrap this in a build form.

Edit

Yes, this can also be done using mapAccumL or traverse .

 import Control.Applicative import Control.Monad.Trans.State.Strict import Data.Traversable (Traversable (traverse), mapAccumL) mapToEverySecond :: Traversable t => (a -> a) -> ta -> ta -- Either mapToEverySecond f = snd . flip mapAccumL False (\mapThisTime x -> if mapThisTime then (False, fx) else (True, x)) -- or mapToEverySecond f xs = evalState (traverse step xs) False where step x = do mapThisTime <- get put (not mapThisTime) if mapThisTime then return (fx) else return x 

Or you can do it with scanl , which I will leave to you for clarification.

+2
source

This is more of a comment on @MartinHaTh's answer. I optimized its solution a bit for

 func :: (a -> a) -> [a] -> [a] func f = loop where loop [] = [] loop [x] = [x] loop (x:s:xs) = x : fs : loop xs 
0
source

Not very elegant, but this is my trick:

 mapToEverySecond f = reverse . fst . foldl' cmb ([], False) where cmb (xs, b) x = ((if b then f else id) x : xs, not b) 

Or an improvement in MartinHaTh's answer:

 mapToEverySecond f (x : x' : xs) = x : fx' : mapToEverySecond f xs mapToEverySecond _ xs = xs 
0
source

Source: https://habr.com/ru/post/981821/


All Articles