List 需要根据元素升序的中断将列表划分为列表(Haskell)

List 需要根据元素升序的中断将列表划分为列表(Haskell),list,haskell,partition,List,Haskell,Partition,假设我有这样的清单: [4,5,6,7,1,2,3,4,5,6,1,2] [[4,5,6,7],[1,2,3,4,5,6],[1,2]] 我需要一个Haskell函数,它将把这个列表转换成一个列表列表,列表列表由原始列表的段组成,这些段按升序形成一个系列。所以结果应该是这样的: [4,5,6,7,1,2,3,4,5,6,1,2] [[4,5,6,7],[1,2,3,4,5,6],[1,2]] 有什么建议吗?您可以在下一步使用右折叠来分解列表: ascend :: Ord a =>

假设我有这样的清单:

[4,5,6,7,1,2,3,4,5,6,1,2]
[[4,5,6,7],[1,2,3,4,5,6],[1,2]]
我需要一个Haskell函数,它将把这个列表转换成一个列表列表,列表列表由原始列表的段组成,这些段按升序形成一个系列。所以结果应该是这样的:

[4,5,6,7,1,2,3,4,5,6,1,2]
[[4,5,6,7],[1,2,3,4,5,6],[1,2]]

有什么建议吗?

您可以在下一步使用右折叠来分解列表:

ascend :: Ord a => [a] -> [[a]]
ascend xs = foldr f [] xs
  where
    f a []  = [[a]]
    f a xs'@(y:ys) | a < head y = (a:y):ys
                   | otherwise = [a]:xs'
foldr foo [] xs
  where
    foo x yss = (x:zs) : ws
      where
        (zs, ws) = case yss of
                     (ys@(y:_)) : rest
                            | x < y     -> (ys,rest)
                            | otherwise -> ([],yss)
                     _ -> ([],[])
foldr foo[]xs 哪里 foo x yss=(x:zs):ws 哪里 (zs,ws)=情况yss (y:):休息 |x(y,其余) |否则->([],yss) _ -> ([],[])
(第二个参数中的组合函数是惰性的,这有点复杂,因此它也适用于无限列表。)

您可以通过手动递归来实现这一点,但我相信Haskell是一种更先进的语言。让我们看看是否可以开发一个使用现有递归策略的解决方案。首先是一些预备赛

{-# LANGUAGE NoMonomorphismRestriction #-}
-- because who wants to write type signatures, amirite?
import Data.List.Split -- from package split on Hackage
第一步是观察我们想要根据同时查看列表中两个元素的标准分割列表。因此,我们需要一个新的列表,其中包含表示“上一个”和“下一个”值的元素。这里有一个非常标准的技巧:

previousAndNext xs = zip xs (drop 1 xs)
然而,就我们的目的而言,这并不是很有效:这个函数总是输出一个比输入短的列表,并且我们总是想要一个与输入长度相同的列表(尤其是当输入是长度为1的列表时,我们想要一些输出)。因此,我们将使用“空终止符”稍微修改标准技巧

现在,我们将在这个列表中查找上一个元素大于下一个元素(或者下一个元素不存在)的位置。让我们编写一个执行该检查的谓词

bigger (x, y) = maybe False (x >) y
现在,让我们编写实际执行拆分的函数。我们的“分隔符”将是满足
biger
的值;我们永远不想把它们扔掉,所以让我们保留它们吧

ascendingTuples = split . keepDelimsR $ whenElt bigger
最后一步是将构造元组的位、拆分元组的位和最后一点咀嚼,以丢弃我们不关心的元组位:

ascending = map (map fst) . ascendingTuples . pan
让我们在ghci中尝试一下:

*Main> ascending [4,5,6,7,1,2,3,4,5,6,1,2]
[[4,5,6,7],[1,2,3,4,5,6],[1,2]]
*Main> ascending [7,6..1]
[[7],[6],[5],[4],[3],[2],[1]]
*Main> ascending []
[[]]
*Main> ascending [1]
[[1]]

另外,在当前版本的
split
中,
keepDelimsR
比需要的要严格一些,因此
升序
目前无法处理无限列表。不过,我已经提交了一个补丁,使它变得更懒惰。

这个问题自然适合基于的解决方案。拥有(如该职位所定义)

我们可以写作

partition_asc xs  =  para g [] xs  where
  g x (y:_) ~(a:b) | x<y  =  (x:a):b 
  g x  _      r           =  [x]:r 
这两个版本中的惰性模式都使它能够处理无限的输入列表 以富有成效的方式(如’s中所示)

更新2020-05-08:毕竟不是那么琐碎。都
头部。头。分区\u asc$[4]++未定义
和分区\u asc2同样失败,出现
***异常:Prelude.未定义
。组合函数
g
过早地强制下一个元素
y
。在查看下一个元素(例如第二个版本)之前,需要更仔细地编写,以便立即产生效果

partition_asc2' xs  =  foldr g [] . init . tails $ xs  where
  g (x: ~(y:_)) r@ ~(a:b)  =  (x:g):gs
                        where
                        (g,gs) | x < y    =  (a,b)
                               | otherwise  =  ([],r)
partition_asc2'xs=foldr g[]。初始化。尾$xs在哪里
g(x:~(y:))r@~(a:b)=(x:g):gs
哪里
(g,gs)| x

(再次,如Daniel的回答中所示)。

完成此任务的另一种方法(实际上,它奠定了非常有效的排序算法的基础)是使用,在本例中,它应用于从右侧折叠<代码>foldr

照目前的情况,这个答案只会把上升的部分拼凑起来,但是,最好同时把下降的部分拼凑起来。。。最好是以相反的顺序全部输入O(n),这将使我们只需要对获得的块进行二进制合并,就可以得到完全排序的输出。然而,这是另一个问题的另一个答案

chunks :: Ord a => [a] -> [[a]]
chunks xs = foldr go return xs $ []
            where
            go :: Ord a => a -> ([a] -> [[a]]) -> ([a] -> [[a]])
            go c f = \ps -> let (r:rs) = f [c]
                            in case ps of
                               []  -> r:rs
                               [p] -> if c > p then (p:r):rs else [p]:(r:rs)

*Main> chunks [4,5,6,7,1,2,3,4,5,6,1,2]
[[4,5,6,7],[1,2,3,4,5,6],[1,2]]
*Main> chunks [4,5,6,7,1,2,3,4,5,4,3,2,6,1,2]
[[4,5,6,7],[1,2,3,4,5],[4],[3],[2,6],[1,2]]

在上面的代码中,
c
代表当前,而
p
代表上一个和下一个,记住我们从右边折叠,所以上一个实际上是下一个要处理的项目。

你可以使用foldr来完成。引用的副本实际上根本不是副本,尽管非常相似。它特别要求子序列是
(\x y->x+1==y)
,而不是
(为了便于参考,这里有上面提到的链接。我认为最好是颠倒模式,在第一个列表前面进行匹配,而不是使用
head
。请参阅丹尼尔·菲舍尔的
案例
表达以获得灵感。当然,他的回答还有其他优点。
partition_asc2' xs  =  foldr g [] . init . tails $ xs  where
  g (x: ~(y:_)) r@ ~(a:b)  =  (x:g):gs
                        where
                        (g,gs) | x < y    =  (a,b)
                               | otherwise  =  ([],r)
chunks :: Ord a => [a] -> [[a]]
chunks xs = foldr go return xs $ []
            where
            go :: Ord a => a -> ([a] -> [[a]]) -> ([a] -> [[a]])
            go c f = \ps -> let (r:rs) = f [c]
                            in case ps of
                               []  -> r:rs
                               [p] -> if c > p then (p:r):rs else [p]:(r:rs)

*Main> chunks [4,5,6,7,1,2,3,4,5,6,1,2]
[[4,5,6,7],[1,2,3,4,5,6],[1,2]]
*Main> chunks [4,5,6,7,1,2,3,4,5,4,3,2,6,1,2]
[[4,5,6,7],[1,2,3,4,5],[4],[3],[2,6],[1,2]]