Can mapEvery be implemented with foldr

For a function that displays a function for every nth item in the list:

mapEvery :: Int -> (a -> a) -> [a] -> [a]
mapEvery n f = zipWith ($) (drop 1 . cycle . take n $ f : repeat id)

      

Is it possible to implement this using foldr

as usual map

?

EDIT: Changed "folder" to "foldr" in the header. Auto corrections ...

+3


source to share


2 answers


Here's one solution

mapEvery :: Int -> (a -> a) -> [a] -> [a]
mapEvery n f as = foldr go (const []) as 1 where
  go a as m 
    | m == n    = f a : as 1
    | otherwise =   a : as (m+1)

      



This uses the " foldl

as foldr

" trick to pass state left to right through the list as you add. Essentially, if we read the type foldr

as (a -> r -> r) -> r -> [a] -> r

, then we instantiate r

as Int -> [a]

, where the integer passed in is the current number of elements that we passed without calling the function.

+6


source


Yes, he can:

mapEvery :: Int -> (a -> a) -> [a] -> [a]
mapEvery n f xs
    = foldr (\y ys -> g y : ys) []
    $ zip [1..] xs
    where
        g (i, y) = if i `mod` n == 0 then f y else y

      

And since it's possible to implement in terms , you could get even more folds if you like. This even works on infinite lists: zip

foldr

> take 20 $ mapEvery 5 (+1) $ repeat 1
[1,1,1,1,2,1,1,1,1,2,1,1,1,1,2,1,1,1,1,2]

      




This is how it looks with even more foldr

and more investment g

:

mapEvery :: Int -> (a -> a) -> [a] -> [a]
mapEvery _ _ [] = []
mapEvery n f xs
    = foldr (\(i, y) ys -> (if i `mod` n == 0 then f y else y) : ys) []
    $ foldr step (const []) [1..] xs
    where
        step _ _ [] = []
        step x zipsfn (y:ys) = (x, y) : zipsfn ys

      

Now, would I recommend writing it this way? Absolutely not. This is about as confusing as you can get by continuing to write "readable" code. But it demonstrates that it is possible to use very powerful foldr

to implement relatively complex functions.

+1


source







All Articles