如何手动将[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:这不是家庭作业,我已经花了好几个小时自己解决这个问题。
答案 0 :(得分:3)
要回答您的第一个问题,这是一个基于元素的转换而不是拆分。执行此操作的适当功能是
map :: (a -> b) -> [a] -> [b]
现在,您需要一个函数(a -> b)
b
为[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]]
答案 1 :(得分:2)
首先:
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]]
第一个参数的模式反映了预期输入列表的特殊结构。
答案 2 :(得分:2)
首先,你可以使用列表理解:
>>> [[x] | x <- [1,2,3,4,5,6]]
[[1], [2], [3], [4], [5], [6]]
对于第二个问题,您可以使用split
包提供的Data.List.Split模块:
import Data.List.Split
f :: (a -> Bool) -> [[a]] -> [[a]]
f predicate = split (keepDelimsL $ whenElt predicate) . concat
这是列表的第一个concat,因为split
中的函数适用于列表而不是列表列表。生成的单个列表是使用拆分包中的函数再次拆分。