库中的列表是否有一个操作可以组成 n 个元素组?例如:n=3
groupInto 3 [1,2,3,4,5,6,7,8,9] = [[1,2,3],[4,5,6],[7,8,9]]
如果没有,我该怎么做?
在Hoogle上快速搜索表明没有这样的功能。另一方面,回复说split
包里有一个,叫chunksOf
.
但是,您可以自己完成
group :: Int -> [a] -> [[a]]
group _ [] = []
group n l
| n > 0 = (take n l) : (group n (drop n l))
| otherwise = error "Negative or zero n"
当然,有些括号可以去掉,我留在这里是为了了解代码的作用:
基本情况很简单:只要列表为空,只需返回空列表。
递归案例首先测试是否n
为正。如果n
是0
或更低,我们将进入无限循环,我们不希望这样。take
然后我们使用and将列表分成两部分drop
:take
返回第一个n
元素,同时drop
返回其他元素。然后,我们将第一个n
元素添加到通过将我们的函数应用于原始列表中的其他元素而获得的列表中。
此功能以及其他类似功能可以在流行的拆分包中找到。
> import Data.List.Split
> chunksOf 3 [1,2,3,4,5,6,7,8,9]
[[1,2,3],[4,5,6],[7,8,9]]
正如 Mihai 指出的,你可以自己写一个。但我会使用该splitAt
函数,因为它不需要像take
-drop
组合那样对输入列表进行两次传递:
chunks :: Int -> [a] -> [[a]]
chunks _ [] = []
chunks n xs =
let (ys, zs) = splitAt n xs
in ys : chunks n zs
这是一种常见的模式 - 通过重复迭代从种子值(在本例中是您的输入列表)生成列表。该模式在unfoldr
函数中被捕获。我们可以将它与稍微修改的版本一起使用splitAt
(感谢Will Ness提供更简洁的版本):
chunks n = takeWhile (not . null) . unfoldr (Just . splitAt n)
也就是说,使用unfoldr
我们生成n
元素块,同时我们通过n
元素缩短输入列表,并且我们生成这些块直到我们得到空列表——此时初始输入被完全消耗。
当然,正如其他人指出的那样,您应该使用split
模块中已经存在的功能。但是习惯于标准 Haskell 库中的列表处理函数总是好的。
这通常被称为“块”,是最常提到的列表操作之一,它不在base
. 该包split
提供了这样的操作,复制和粘贴黑线鳕文档:
> chunksOf 3 ['a'..'z']
["abc","def","ghi","jkl","mno","pqr","stu","vwx","yz"]
此外,与我的意愿相反,hoogle 只搜索一小组库(那些由 GHC 或 HP 提供的库),但您可以使用+PKG_NAME
- hoogle withInt -> [a] -> [[a]] +split
获取您想要的内容显式地将包添加到搜索中。有些人出于这个原因使用 Hayoo。