我最近需要 stride 一个列表,以便只查看一些元素。它有点像过滤器功能,但并不是那么简单。但首先,这是一个跨越的步伐。
跨越列表 - 或任何可遍历的类型 - 与折叠它相同,但丢弃一些经常遇到的元素(关于步幅值)。我们选择一个元素,然后选择下一个值将是下一个 th 一个。例如,如果我们跨步一个步幅值设置为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
模块的一部分吗?我认为它可以提供很多帮助。
答案 0 :(得分:8)
一个更简单的实现,也适用于无限列表,效率更高
stride :: Int -> [a] -> [a]
stride s = go
where
go (x:xs) = x : go (drop s xs)
go _ = []
如果您希望将其用于除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
模块的一部分?
是的,可以在libraries@haskell.org邮件列表上提出建议。
但是,我不认为它会被接受。实用性太小,无法将其添加到base
包中。它会更好地添加到另一个包中。也许将其包括在split
中是最好的。
答案 1 :(得分:3)
此功能并不能完全符合您的要求,但您可以将stride
功能基于此功能:
chunksOf :: Int -> [a] -> [[a]]
chunksOf n = takeWhile (not . null) . map (take n) . iterate (drop n)
现在你的步幅可能看起来像这样:
stride :: Int -> [a] -> [a]
stride n = map head . chunksOf (n + 1)
使用head
就行了,因为takeWhile (not . null)
确保子列表永远不会为空。
答案 2 :(得分:0)
或者使用lens包,您可以:
>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"
#lens