【问题标题】:Can mapEvery be implemented with foldrmapEvery可以用foldr实现吗
【发布时间】:2014-10-08 16:30:09
【问题描述】:

对于将函数映射到列表中每个第 n 个元素的函数:

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

是否可以像普通的map 一样使用foldr 来实现这一点?

编辑:在标题中,将“文件夹”更改为“文件夹”。自动更正...

【问题讨论】:

  • 我没想到会这样,至少在一个文件夹中不会。
  • 嗯?这真的很有趣。您将如何使用多个foldr 来实现它?
  • ...没关系,也不要认为那行得通。使用foldr,您只能计算列表末尾中有多少元素,而不是从开头开始有多少。
  • 是的,我意识到了。我在想,如果基本情况包括mod (length xs) n,那可能会起作用,但这只适用于有限列表。
  • @LouisWasserman 你可以用foldr 来实现foldl,所以你使用哪个折叠并不重要。

标签: haskell functional-programming fold


【解决方案1】:

这里有一个解决方案

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 as foldr”技巧在折叠时沿列表从左向右传递状态。本质上,如果我们将foldr 的类型读取为(a -> r -> r) -> r -> [a] -> r,那么我们将r 实例化为Int -> [a],其中传递的整数是我们在未调用函数的情况下传递的当前元素数。

【讨论】:

    【解决方案2】:

    是的,它可以:

    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,如果你真的想要的话,你可以得到更多的 fold-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 来实现相对复杂的功能是可能的。

    【讨论】:

      猜你喜欢
      • 2010-09-19
      • 2013-03-30
      • 1970-01-01
      • 2016-09-04
      • 2017-12-17
      • 1970-01-01
      • 2017-04-18
      • 1970-01-01
      • 2014-06-12
      相关资源
      最近更新 更多