如何手动拆分[1,2,4,5,6,7]
成[[1],[2],[3],[4],[5],[6],[7]]
?手动意味着不使用中断。
那么,如何根据谓词将列表拆分为子列表?像这样
f even [[1],[2],[3],[4],[5],[6],[7]] == [[1],[2,3],[4,5],[6,7]]
PS:这不是家庭作业,我已经尝试了几个小时自己弄清楚。
如何手动拆分[1,2,4,5,6,7]
成[[1],[2],[3],[4],[5],[6],[7]]
?手动意味着不使用中断。
那么,如何根据谓词将列表拆分为子列表?像这样
f even [[1],[2],[3],[4],[5],[6],[7]] == [[1],[2,3],[4,5],[6,7]]
PS:这不是家庭作业,我已经尝试了几个小时自己弄清楚。
要回答您的第一个问题,这是一个元素转换而不是拆分。执行此操作的适当功能是
map :: (a -> b) -> [a] -> [b]
现在,您需要一个函数(a -> b)
where b
is [a]
,因为您想将元素转换为包含相同类型的单例列表。这里是:
mkList :: a -> [a]
mkList a = [a]
所以
map mkList [1,2,3,4,5,6,7] == [[1],[2],...]
至于你的第二个问题:如果你不被允许(作业?)使用break
,那么你是否被允许使用takeWhile
以及dropWhile
结果的两半break
。
无论如何,对于没有它们的解决方案(“手动”),只需使用带有累加器的简单递归:
f p [] = []
f p (x:xs) = go [x] xs
where go acc [] = [acc]
go acc (y:ys) | p y = acc : go [y] ys
| otherwise = go (acc++[y]) ys
这将递归遍历您的整个列表尾部,始终记住当前子列表是什么,并且当您到达p
适用的元素时,输出当前子列表并开始新的子列表。
请注意,先接收[x]
而不是[]
提供第一个元素已经满足p x
并且我们不希望输出空的第一个子列表的情况。
此外,这对原始列表 ( [1..7]
) 而不是[[1],[2]...]
. 但是你也可以在转换后的那个上使用它:
> map concat $ f (odd . head) [[1],[2],[3],[4],[5],[6],[7]]
[[1,2],[3,4],[5,6],[7]]
第一的:
map (: [])
第二:
f p xs =
let rs = foldr (\[x] ~(a:r) -> if (p x) then ([]:(x:a):r) else ((x:a):r))
[[]] xs
in case rs of ([]:r) -> r ; _ -> rs
foldr
的操作很容易可视化:
foldr g z [a,b,c, ...,x] = g a (g b (g c (.... (g x z) ....)))
所以在编写组合函数时,它需要两个参数,第一个是列表的“当前元素”,第二个是“处理其余元素的结果”。这里,
g [x] ~(a:r) | p x = ([]:(x:a):r)
| otherwise = ((x:a):r)
因此,将其从右侧可视化,它只是添加到最新的子列表中,并在必要时打开一个新的子列表。但是由于列表实际上是从左侧访问的,所以我们使用惰性模式保持它的惰性, ~(a:r)
. 现在它甚至适用于无限列表:
Prelude> take 9 $ f odd $ map (:[]) [1..]
[[1,2],[3,4],[5,6],[7,8],[9,10],[11,12],[13,14],[15,16],[17,18]]
第一个参数的模式反映了预期输入列表的特殊结构。
首先,您可以使用列表推导:
>>> [[x] | x <- [1,2,3,4,5,6]]
[[1], [2], [3], [4], [5], [6]]
对于第二个问题,可以使用包提供的Data.List.Split模块split
:
import Data.List.Split
f :: (a -> Bool) -> [[a]] -> [[a]]
f predicate = split (keepDelimsL $ whenElt predicate) . concat
这第一个concat是列表,因为来自列表的函数split
而不是列表列表。生成的单个列表是使用 split 包中的函数再次拆分。