【问题标题】:How to select every n-th element from a list [duplicate]如何从列表中选择每个第 n 个元素 [重复]
【发布时间】:2011-09-29 15:34:59
【问题描述】:

可能重复:
How to get every Nth element of an infinite list in Haskell?

简单的任务 - 我们有一个列表,并且希望只保留该列表中的每个第 n 个元素。 在 haskell 中最惯用的方法是什么?

在我的脑海中是这样的:

dr n [] = []
dr n (x : xs) = x : (dr n $ drop n xs)

但我有一种强烈的感觉,我把问题复杂化了。

【问题讨论】:

  • 这对我来说看起来很地道。
  • 不要以为你会得到更便宜的 - 我觉得很好。唯一的一点是,这不适合您的描述,因为 dr k [1..] 不会产生您的问题序列。

标签: haskell


【解决方案1】:

我的变种是:

each :: Int -> [a] -> [a]
each n = map head . takeWhile (not . null) . iterate (drop n)

速度快,适合懒惰。

【讨论】:

  • 是的,非常好。我不想讨论懒惰(老实说,我没有考虑添加 null 保护),但这是我第一个坏掉的例子的一个很好的选择。
【解决方案2】:

您的解决方案很好,但这里有其他三个使用 Haskell 基础库中的函数的解决方案。

dr1 m = concatMap (take 1) . iterate (drop m)

粗略地说,这永远不会终止(因为iterate 永远不会终止)。所以也许更好的解决方案是使用unfoldr:

{-# LANGUAGE TupleSections #-}
import Data.Maybe
dr2 m = unfoldr ((\x-> fmap (,drop m x) (listToMaybe x)))

如果您不了解 GHC 扩展和函子等概念,您传递给展开的函数可能会变得有点难看,这也是该解决方案,无需花哨的步法(未经测试):

dr2 m = unfoldr ((\x -> case listToMaybe x of
                         Nothing -> Nothing
                         Just i  -> Just (i,drop m x)))

如果您不喜欢展开,请考虑使用 zip 和过滤器:

dr3 m = map snd . filter ((== 1) . fst) . zip (cycle [1..m])

评论

了解所有这些解决方案都略有不同。学习为什么会让你成为一个更好的 Haskell 程序员。 dr1 使用迭代,因此永远不会终止(也许这对于无限列表是可以的,但可能不是一个好的整体解决方案):

> dr1 99 [1..400]
[1,100,199,298,397^CInterrupted.

dr2 解决方案将通过跳过展开中的值来显示每个 mth 值。展开将用于下一次展开的值和当前展开的结果都传递到单个元组中。

> dr2 99 [1..400]
[1,100,199,298,397]

dr3 解决方案稍长,但对于初学者来说可能更容易理解。首先,您使用[1..n, 1..n, 1..n ...] 循环标记列表中的每个元素。其次,您只选择带有1 标记的数字,有效地跳过了n-1 的元素。第三,您删除标签。

> dr3 99 [1..400]
[1,100,199,298,397]

【讨论】:

  • 托马斯,非常感谢。了解可以执行相对简单任务的所有方法非常重要,而且您刚刚给了我一些新想法。
【解决方案3】:

给牦牛剃毛的方法有很多!还有一个:

import Data.List.Split -- from the "split" package on Hackage
dr n = map head . chunk n

【讨论】:

  • chunk 的实现非常相似,其实是-- | split at regular intervals chunk :: Int -> [a] -> [[a]] chunk _ [] = [[]] chunk n xs = y1 : chunk n y2 where (y1, y2) = splitAt n xs
  • @shabunc 你打赌!代码重用是游戏的名称。
【解决方案4】:

试试这个:

getEach :: Int -> [a] -> [a]
getEach _ [] = []
getEach n list
 | n < 1     = []
 | otherwise = foldr (\i acc -> list !! (i - 1):acc) [] [n, (2 * n)..(length list)]

然后在 GHC 中:

*Main> getEach 2 [1..10]
[10,8,6,4,2]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-07-23
    • 2018-09-02
    • 1970-01-01
    • 2022-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多