0

如何手动拆分[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:这不是家庭作业,我已经尝试了几个小时自己弄清楚。

4

3 回答 3

3

要回答您的第一个问题,这是一个元素转换而不是拆分。执行此操作的适当功能是

map :: (a -> b) -> [a] -> [b]

现在,您需要一个函数(a -> b)where bis [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]]
于 2013-08-01T11:15:32.650 回答
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]]

第一个参数的模式反映了预期输入列表的特殊结构。

于 2013-08-01T11:18:21.787 回答
2

首先,您可以使用列表推导:

>>> [[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 包中的函数再次拆分。

于 2013-08-01T11:28:29.390 回答