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 ...
source to share
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.
source to share
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.
source to share