我刚开始学习Haskell,正在尝试使用Project Euler。
我有一个数字列表,我想创建一个二维列表,分成另一个列表中定义的长度的子列表。
所以,我喜欢一个函数,它接受一个Ints列表和一个长度列表,并对它们进行操作:
f [1..20] [5,8,6] = [[1,2,3,4,5],[6,7,8,9,10,11,12,13],[14,15,16,17,18,19]]
如果函数可以获取无限的数字和长度列表并且只是保持子列表,那将会很棒。
我不知道如何解决这个问题。看看Stack Overflow,我看到(对我的新手来说相当复杂)将列表打包到相同长度的子列表中的解决方案,但不像我感兴趣的那样。
答案 0 :(得分:0)
让我们根据您的规范使用递归来实现以下功能:
splitAts :: [a] -> [Int] -> [[a]]
请注意,我已经翻转了参数的顺序,因为这样,部分应用的函数splitAts n
可以被认为是有意义的操作,给定任何列表,将它分成子列表给定长度。
简单的情况是指定没有(更多)子列表长度:
splitAts [] xs = []
递归的情况是当第一个子列表长度为n
时:在这种情况下,我们返回的第一个子列表将具有此长度,其余的将仅仅是根据列表的其余部分的拆分其余的长度:
splitAts (n:ns) xs = take n xs : splitAts ns (drop n xs)
已经存在一个函数splitAt
,可以在一次扫描中执行take n
/ drop n
,因此上述内容可以重写为效果更好的
splitAts (n:ns) xs = here : splitAts ns there
where
(here, there) = splitAt n xs
示例运行:
*Main> splitAts [5, 8, 6] [1..20]
[[1,2,3,4,5],[6,7,8,9,10,11,12,13],[14,15,16,17,18,19]]
这很懒惰:不仅仅是这样:
*Main> splitAts [5, 8, 6] [1..]
[[1,2,3,4,5],[6,7,8,9,10,11,12,13],[14,15,16,17,18,19]]
但是这个也是:
*Main> take 10 (splitAts [3,5..] [1..])
[[1,2,3],[4,5,6,7,8],[9,10,11,12,13,14,15],[16,17,18,19,20,21,22,23,24],
[25,26,27,28,29,30,31,32,33,34,35],[36,37,38,39,40,41,42,43,44,45,46,47,48]]