1

我最近需要跨越一个列表,以便只看到一些元素。这是一个过滤功能,但它不是那么简单。但首先,这是一个跨步。

跨越一个列表——或任何可遍历的类型——与折叠它是一样的,但会丢弃一些经常遇到的元素(关于步幅值)。我们挑选一个元素,然后挑选的下一个值将是下一个步幅。例如,如果我们将一个跨步值设置为 0 的列表进行跨步,我们实际上得到的列表是不变的。如果我们在列表中跨步 1,我们会在两个元素上得到一个元素:

stride 0 [1..10] == [1..10]
stride 1 [1..10] == [1,3,5,7,9]
stride 2 [1..10] == [1,4,7,10]

我看了看Data.List,我没有找到stride一个列表。这就是为什么我编写了一个函数来跨越我的 - 和你的!- 东西:

import Data.DList

-- for Data.List
stride :: (Num a, Eq a) => a -> [b] -> [b]
stride s = toList . snd . foldl (\(sa,xa) x -> if sa == s then (0,xa `snoc` x) else (sa+1,xa)) (s,fromList [])

你可以像上面一样使用它。是否可以提议将其作为Data.List模块的一部分?我认为它可以帮助很大。

4

3 回答 3

8

一个更简单的实现也适用于无限列表并且更有效将是

stride :: Int -> [a] -> [a]
stride s = go
  where
    go (x:xs) = x : go (drop s xs)
    go _      = []

如果您想要它用于其他类型而不是just Int,请制作它

import Data.List

stride :: Integral i => i -> [a] -> [a]
stride s = go
  where
    go (x:xs) = x : go (genericDrop s xs)
    go _      = []

对非整数类型执行此操作毫无意义 IMO。

是否可以提议将其作为Data.List模块的一部分?

是的,这是可能的,请在 library@haskell.org 邮件列表上提出建议。

然而,我认为它不会被接受。用处太小,无法添加到base包中。它会更好地添加到另一个包中。也许包括它split是最好的。

于 2013-01-04T15:30:43.207 回答
3

这个函数并不完全符合你的要求,但你可以stride在它的基础上创建你的函数:

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

现在你的 stride 函数可能看起来像这样:

stride :: Int -> [a] -> [a]
stride n = map head . chunksOf (n + 1)

在这里使用head很好,因为takeWhile (not . null)确保子列表永远不会为空。

于 2013-01-04T15:29:33.597 回答
1

或者使用镜头包,您可以:

>stridingOf l n = (elementsOf l ((==0) . (flip mod (n + 1))))
>striding = stridingOf traverse
>stride n = toListOf striding

>stride 1 [1..10]
[1,3,5,7,9]

编写 stridingOf 而不是仅仅 striding 的一些额外工作允许在 text 和 byteStrings 上轻松使用,在没有lens包的情况下进行更困难的重写。

>import Data.Text
>import Data.Text.Lens

>testText = pack "This is a test"
>toListOf (stridingOf text 1) testText
"Ti sats"

#镜片

于 2013-01-06T00:24:18.313 回答