【问题标题】:In Haskell, what is the most common way to apply a function to every Nth element of a list?在 Haskell 中,将函数应用于列表的每个第 N 个元素的最常见方法是什么?
【发布时间】:2014-11-20 08:02:30
【问题描述】:

我遇到一些元素列表xs 并想对每个第 N 个元素做一些事情是很常见的。最简单的例子是 Sieve 或 Erastothenes,您想在其中“剔除”给定素数的每个倍数。我可以做到这一点的两种方法是显式递归传递一个计数器变量;或zipWith ($) (cycle (replicate (n-1) id ++ f))。那么哪种方式更好/更优雅/更常用,或者有没有像mapEveryN :: (a -> a) -> Int -> [a] -> [a]这样的库函数我还没有找到?

【问题讨论】:

  • 好吧,你可以使用zipWith ($) (drop 1 $ cycle $ f : replicate (n - 1) id),这对我来说似乎很优雅。没有显式递归,避免串联,而是使用:,它是一个 1-liner。
  • 你也可以通过piecesOf n = unfoldr (Just . splitAt n)concatMap 获得一个中间列表。可能不像其他方法那样简短高效,但有时会很方便。
  • @n.m. piecesOf 创建由 n 组成的组,然后是可能小于 n 的组,然后是无限数量的空组。
  • @pat 是的,它适用于无限列表......为什么有人会满足于更少? :)
  • 这可能不是实现eratosphenes 筛子的最佳方法,但你所拥有的作为“适用于每个n 次”的解决方案相当不错,尽管我会这样做@贝克莱尔风格

标签: list haskell repeat sieve-of-eratosthenes circular-list


【解决方案1】:

正如您和 bheklilr 所提到的,cycle 提供了一个很好的方法来实现这一点。不过,您可以稍微利用一下懒惰:

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

zipWithcycle 的使用似乎比结合这两个任务的手写递归函数更惯用(复杂行为由更简单的行为组成)。

注意tail 在这里为n = 0 定义了这个函数,所以drop 1 更可取。

据我所知,没有一个库函数。

【讨论】:

  • 对列表的每个第 0 个元素做一些事情可能是无稽之谈,所以在这种情况下最好让函数失败。如果它在特定上下文中确实有意义,您应该弄清楚 n=0 的适当行为是什么,而不是以“安全”的名义以某种任意方式使函数总计。幸运的是循环 [] 已经是一个错误,因此将 tail 更改为 drop 1 没有效果。
  • 很好地将replicate 切换为take。我觉得这样更干净,我会用这个。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-08
  • 2013-03-21
  • 2015-11-04
  • 1970-01-01
  • 2014-09-24
相关资源
最近更新 更多