You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Haskell中如何对列表相邻元素按指定数量分组?

在Haskell中实现滑动窗口分组

嘿,这个滑动窗口分组的需求在Haskell里有好几种实用的实现思路,我给你整理几个常用的:

方法一:手动递归实现(适合理解原理)

这个方法最直观,一步步拆解需求:先判断当前列表长度是否够分组的大小,够的话就取前n个元素作为一组,再递归处理剩下的列表(去掉第一个元素)。

sliding :: Int -> [a] -> [[a]]
sliding n xs
  | length xs < n = []
  | otherwise = take n xs : sliding n (tail xs)

不过要注意,length函数需要遍历整个列表,所以如果处理超大型列表的话,这个方法的效率会稍低一些。

方法二:用unfoldr高效实现

Data.List里的unfoldr函数非常适合生成这种迭代式的列表,它可以从初始状态一步步生成结果。我们可以用它来避免不必要的遍历:

import Data.List (unfoldr)

sliding' :: Int -> [a] -> [[a]]
sliding' n = unfoldr (\xs -> if length xs >= n then Just (take n xs, tail xs) else Nothing)

如果想进一步优化,还可以自己写一个短路的“判断是否有至少n个元素”的函数,避免调用length遍历整个列表:

import Data.List (unfoldr)

hasAtLeast :: Int -> [a] -> Bool
hasAtLeast 0 _ = True
hasAtLeast _ [] = False
hasAtLeast n (_:xs) = hasAtLeast (n-1) xs

sliding'' :: Int -> [a] -> [[a]]
sliding'' n = unfoldr (\xs -> if hasAtLeast n xs then Just (take n xs, tail xs) else Nothing)

这个版本在处理大列表时会更高效,因为hasAtLeast只要遍历到足够的元素就会返回结果,不用走完全程。

方法三:用tails简化实现

Data.List的tails函数可以生成列表的所有后缀,利用它我们可以写出非常简洁的代码:

import Data.List (tails)

sliding''' :: Int -> [a] -> [[a]]
sliding''' n xs = filter (\window -> length window == n) $ map (take n) (tails xs)

举个例子,tails [1,2,3,4,5,6,7]会生成[[1,2,3,4,5,6,7],[2,3,4,5,6,7],...,[7],[]],我们给每个后缀取前3个元素,再过滤掉长度不足3的结果,就得到了想要的滑动分组。这个写法简洁易懂,日常使用完全足够。

测试一下的话,调用sliding 3 [1,2,3,4,5,6,7](或者其他任意一个实现),都会得到你想要的结果:[[1,2,3],[2,3,4],[3,4,5],[4,5,6],[5,6,7]]。

内容的提问来源于stack exchange,提问作者Qwertie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 10:39:38