实现对于列表中的一个函数映射到每个第n个元素的功能:可以mapEvery与foldr相似
mapEvery :: Int -> (a -> a) -> [a] -> [a]
mapEvery n f = zipWith ($) (drop 1 . cycle . take n $ f : repeat id)
是否有可能与foldr
像普通map
来实现这一点?
编辑:在标题中,将'文件夹'更改为'foldr'。自动更正...
实现对于列表中的一个函数映射到每个第n个元素的功能:可以mapEvery与foldr相似
mapEvery :: Int -> (a -> a) -> [a] -> [a]
mapEvery n f = zipWith ($) (drop 1 . cycle . take n $ f : repeat id)
是否有可能与foldr
像普通map
来实现这一点?
编辑:在标题中,将'文件夹'更改为'foldr'。自动更正...
这里有一个解决方案
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)
这将使用“foldl
为foldr
”招自左沿为你折列表右侧传状态。本质上,如果我们将foldr
的类型读作(a -> r -> r) -> r -> [a] -> r
,那么我们将r
实例化为Int -> [a]
,其中传递的整数是我们未调用函数而传递的当前元素数。
是的,它可以:
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
而且因为它是possible to implement zip
in terms of foldr
,你可以得到更多的折叠-Y,如果你真的想要的。这甚至适用于无限列表:
> 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]
这是它看起来像更foldr
和内联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
现在,我会建议写这样说?绝对不。尽管您仍然可以编写“可读”代码,但您仍然可以获得这种混淆。但它确实证明了可以使用非常强大的foldr
来实现相对复杂的功能。
我不会期望如此,至少不会有单一的折叠。 – 2014-10-08 16:38:00
嗯?这真的很有趣。你将如何用多个'foldr'来实现它? – 2014-10-08 16:42:50
...没关系,不要认为这也可以。使用'foldr',你只能计算你列表的_end_中有多少元素,而不是从一开始就有多少元素。 – 2014-10-08 16:45:02