如何在Haskell中划分列表?

如何在Haskell中划分列表?,haskell,Haskell,我想获取一个列表(或字符串)并将其拆分为N个元素的子列表。在哈斯克尔我该怎么做 例如: mysteryFunction 2 "abcdefgh" ["ab", "cd", "ef", "gh"] 这里有一个选择: partition :: Int -> [a] -> [[a]] partition _ [] = [] partition n xs = (take n xs) : (partition n (drop n xs)) 下面是该函数的尾部递归版本: partition

我想获取一个列表(或字符串)并将其拆分为N个元素的子列表。在哈斯克尔我该怎么做

例如:

mysteryFunction 2 "abcdefgh"
["ab", "cd", "ef", "gh"]
这里有一个选择:

partition :: Int -> [a] -> [[a]]
partition _ [] = []
partition n xs = (take n xs) : (partition n (drop n xs))
下面是该函数的尾部递归版本:

partition :: Int -> [a] -> [[a]]
partition n xs = partition' n xs []
  where
    partition' _ [] acc = reverse acc
    partition' n xs acc = partition' n (drop n xs) ((take n xs) : acc)
您可以使用:

mysteryFunction :: Int -> [a] -> [[a]]
mysteryFunction n list = unfoldr takeList list
  where takeList [] = Nothing
        takeList l  = Just $ splitAt n l
或者:

mysteryFunction :: Int -> [a] -> [[a]]
mysteryFunction n list = unfoldr (\l -> if null l then Nothing else Just $ splitAt n l) list
注意,例如,这会将所有剩余的元素放在最后一个列表中

mysteryFunction 2 "abcdefg" = ["ab", "cd", "ef", "g"]
然后使用from
Data.List.Split

import Data.List 
import Data.Function

mysteryFunction n = map (map snd) . groupBy ((==) `on` fst) . zip ([0..] >>= replicate n)
。。。只是开玩笑

mysteryFunction x "" = []
mysteryFunction x s = take x s : mysteryFunction x (drop x s)
可能不是您想要的优雅解决方案。

已经有了

Prelude Data.List> :t either
either :: (a -> c) -> (b -> c) -> Either a b -> c

所以真的应该有

list :: t -> ([a] -> t) -> [a] -> t
list n _ [] = n
list _ c xs = c xs
还有。有了它

import Data.List (unfoldr)

g n = unfoldr $ list Nothing (Just . splitAt n)
没有它

g n = takeWhile (not.null) . unfoldr (Just . splitAt n)
一个花哨的回答

在上面的答案中,您必须使用splitAt,它也是递归的。让我们看看如何从头开始构建递归解决方案

函子L(X)=1+A*X可以将X映射为1或将其拆分为A和X的一对,并将列表(A)作为其最小不动点:列表(A)可以映射为1+A*List(A)并使用同构返回;换句话说,我们有一种方法分解非空列表,只有一种方法表示空列表

函子F(X)=列表(A)+A*X类似,但列表的尾部不再是空列表-“1”-因此函子能够提取值A或将X转换为As的列表。然后列表(A)是它的固定点(但不再是最小固定点),函子可以将任何给定的列表表示为列表,或者表示为元素和列表的一对。实际上,任何coalgebra都可以“随意”停止分解列表

(这与添加以下平凡实例相同):

考虑hylomorphism的定义:

hylo :: (f b -> b) -> (c -> f c) -> c -> b
hylo psi phi = psi . fmap (hylo psi phi) . phi
splitList :: Int -> [a] -> [[a]]
splitList n xs = apo (hylo psi phi) (n, xs) where
  phi (n, []) = Z []
  phi (0, xs) = Z xs
  phi (n, (x:xs)) = S x (n-1, xs)

  psi (Z []) = Cons [] $ Left []
  psi (Z ys) = Cons [] $ Right (n, ys)
  psi (S h (Cons t b)) = Cons (h:t) b
给定一个种子值,它使用phi生成fc,fmap递归地对其应用hylo-psi-phi,然后psi从fmapped结构fb中提取b

此函子的(co)代数对的hylomorphism是一个splitAt:

splitAt :: Int -> [a] -> ([a],[a])
splitAt n xs = hylo psi phi (n, xs) where
  phi (n, []) = Z []
  phi (0, xs) = Z xs
  phi (n, (x:xs)) = S x (n-1, xs)
这个余代数提取一个头部,只要有一个要提取的头部并且提取元素的计数器不为零。这是因为函子是如何定义的:只要phi产生sxy,hylo就会将y作为下一个种子输入phi;一旦生成zxs,函子就不再对其应用hylo-psi-phi,递归停止

与此同时,hylo将把结构重新映射到一对列表中:

  psi (Z ys) = ([], ys)
  psi (S h (t, b)) = (h:t, b)
现在我们知道splitAt是如何工作的了。我们可以使用无同态将其扩展到拆分列表:

hylo :: (f b -> b) -> (c -> f c) -> c -> b
hylo psi phi = psi . fmap (hylo psi phi) . phi
splitList :: Int -> [a] -> [[a]]
splitList n xs = apo (hylo psi phi) (n, xs) where
  phi (n, []) = Z []
  phi (0, xs) = Z xs
  phi (n, (x:xs)) = S x (n-1, xs)

  psi (Z []) = Cons [] $ Left []
  psi (Z ys) = Cons [] $ Right (n, ys)
  psi (S h (Cons t b)) = Cons (h:t) b

这一次,重映射适用于无向态化:只要它是正确的,无向态化将继续使用hylo-psi-phi生成列表的下一个元素;如果保留,它将一步生成列表的其余部分(在本例中,仅以[]结束列表)。

请注意,如果已安装Haskell平台,则已安装了
split
软件包。无论如何,这是一个非常有用的软件包!请注意,还有来自
Data.Text
(package
Text
)的
chunksOf
,它的字符串实现比字符列表更有效。因为
(:)
是惰性的,我想说第一个选项是尾部递归,第二个不是。@Toxaris-Hmm,很好。编辑:但第一个选项仍然需要堆栈上的非恒定内存量,而第二个选项则不需要,对吗?第二个选项的问题是,只要分区的调用者请求输出列表的第一个
(:)
单元格,
分区
必须遍历整个输入列表以构建中间列表,然后
反向
必须遍历整个中间列表以找到整个输出的第一个
(:)
单元格。我希望这些遍历需要堆栈空间。但是,第一个选项可以在检查输入列表是否为空后立即返回第一个
(:)
单元格。我认为没有必要在那里使用堆栈。非严格尾部递归不好,受保护的corecursion好。:)非严格数据结构上的尾部递归可能导致Thunk的累积,而不是中间数据的累积。这取决于优化级别和ghc执行严格性分析的能力。拜托,你离完全无点和不可读还不远@从右到左阅读:首先,我构建了一个具有正确重复次数的无限列表,例如,
[0,0,0,1,1,1,2,2…]
并将其与输入列表(无点样式,因此我不需要参数)压缩在一起,从而给出一个对列表。接下来,我使用
groupBy
根据我的“数字模式”获取所需的数据块,最后一步,我使用double-
map
删除现在不再需要的数字(因为我想修改列表中的列表)。然而,我在现实生活中决不会使用这样的“火车残骸”。这很有启发性,但我发现@PaulManta的方法更容易阅读(可能是因为(作为新手)我不倾向于使用
unfover
。这两种方法之间有什么性能考虑因素吗?
  psi (Z ys) = ([], ys)
  psi (S h (t, b)) = (h:t, b)
splitList :: Int -> [a] -> [[a]]
splitList n xs = apo (hylo psi phi) (n, xs) where
  phi (n, []) = Z []
  phi (0, xs) = Z xs
  phi (n, (x:xs)) = S x (n-1, xs)

  psi (Z []) = Cons [] $ Left []
  psi (Z ys) = Cons [] $ Right (n, ys)
  psi (S h (Cons t b)) = Cons (h:t) b