繁体   English   中英

检查列表是否按升序或降序排序或不按haskell排序?

[英]Check a list if sorted in ascending or descending or not sorted in haskell?

我是Haskell的新手。 我刚刚学习了Haskell两个星期。 我不太了解if else语句和haskell的列表理解如何工作。 所以我想做一个可以弄清楚排序类型的函数,例如列表按升序或降序排序,或者根本不排序。 我知道如何检查列表是否按升序和降序排列,但我不知道如何检查列表是否没有按排序。

data SortType = Ascending | Descending | NotSorted deriving (Show)

sorted :: (Ord a) => [a] -> TypeOfSort 
sorted [] = Ascending
sorted [x] = Ascending
sorted (x:y:xs) | x < y = sorted (y:xs) 
            | otherwise = Descending
sorted_  = Ascending

如果有人可以告诉我该怎么做,那将是一个很大的帮助。 谢谢。 P / s:这不是家庭作业/工作资料,而是我想学习的东西。

您的功能中有问题的部分是| otherwise = Descending | otherwise = Descending 根据您的函数定义,如果列表中有两个连续的示例,使得x >= y ,则该函数为降序 这不是True :如果对于所有两个连续元素x > y (或者如果不要求它严格降序,则x >= y则函数正在降序。

此外,这里的另一个问题是具有一个元素(或没有元素)的列表可以被视为AscendingDescending 所以我认为我们要做的第一件事是定义一些语义。 我们可以决定使输出成为TypeOfSort项的列表,或者我们可以决定扩展TypeOfSort的选项TypeOfSort

在这个答案中,我将选择最后一个选项。 我们可以将TypeOfSort扩展为:

data TypeOfSort = Ascending | Descending | Both | NotSorted
    deriving (Show)

现在我们可以处理函数本身。 这里的基本情况当然是空列表[]和带有一个元素[_]的列表:

sorted [] = Both
sorted [_] = Both

现在我们需要定义归纳案例。 列表何时排序? 如果所有元素都(严格)大于前一个元素。 如果所有元素都(严格地)比前一个元素小,则列表将以降序排序。 现在让我们假设严谨 以后很容易更改功能定义。

因此,如果我们有一个包含两个或多个元素的列表,则以第二个元素开头的列表为AscendingBothx < y ,换句话说,该列表为Ascending

sorted (x:y:xs) | Both <- sort_yxs, x < y = Ascending
                | Ascending <- sort_yxs, x < y = Ascending
    where sort_yxs = sorted (y:xs)

降序也是如此:如果列表的其余部分按降序排列,并且第一个元素大于第二个元素,则列表按降序排列:

                | Both <- sort_yxs, x > y = Descending
                | Ascending <- sort_yxs, > y = Descending
    where sort_yxs = sorted (y:xs)

在其余所有情况下,这意味着列表的某些部分Ascending而某些部分则Descending ,因此列表为NotSorted

                | otherwise = NotSorted

或将所有这些放在一起:

sorted [] = Both
sorted [_] = Both
sorted (x:y:xs) | Both <- sort_yxs, x < y = Ascending
                | Ascending <- sort_yxs, x < y = Ascending
                | Both <- sort_yxs, x > y = Descending
                | Ascending <- sort_yxs, x > y = Descending
                | otherwise = NotSorted
    where sort_yxs = sorted (y:xs)

重构:使TypeOfSortMonoid

上面的定义包含很多边缘情况,这使得编写简单的程序变得困难。 通过引入一些实用程序功能,我们可以使其变得更容易。 例如,可以通过定义一个接受两个TypeOfSort并返回交集的函数来完成此操作。 这样的功能看起来像:

intersect Both x = x
intersect x Both = x
intersect Ascending Ascending = Ascending
intersect Descending Descending = Descending
intersect _ _ = NotSorted

实际上, Both形成了一个以Both为标识元素的monoid

instance Monoid where
    mappend Both x = x
    mappend x Both = x
    mappend Ascending Ascending = Ascending
    mappend Descending Descending = Descending
    mappend _ _ = NotSorted

    mempty = Both

现在我们可以将定义重写为:

sorted [] = Both
sorted [_] = Both
sorted (x:y:ys) | x > y = mappend rec Ascending
                | x < y = mappend rec Descending
                | otherwise = NotSorted
    where rec = sorted (y:ys)

我的解决方案不使用排序功能,也没有递归:

data SortType = Ascending | Descending | NotSorted | Flat | Unknown deriving (Show)

sorted :: (Ord a) => [a] -> SortType 
sorted [] = Unknown
sorted [a] = Flat
sorted xs  
    | and [x == y | (x, y) <- zipPairs xs] = Flat
    | and [x <= y | (x, y) <- zipPairs xs] = Ascending
    | and [x >= y | (x, y) <- zipPairs xs] = Descending
    | otherwise                            = NotSorted

zipPairs :: [a] -> [(a, a)]
zipPairs xs = zip xs (tail xs)

使用lambda的人可能更快

all (\(x, y) -> x <= y) (zipPairs xs)

在Python中,我可能会做这样的事情

from itertools import izip, islice

n = len(lst)

all(x <= y for x, y in izip(islice(lst, 0, n - 1), islice(lst, 1, n)))

我想您也可以按照以下步骤进行:

data Sorting = Why | Karma | Flat | Descent | Ascent deriving (Show)

howSorted :: Ord a => [a] -> Sorting
howSorted xs | xs == []                    = Why
             | all (== head xs) $ tail xs  = Flat
             | and $ map (uncurry (<=)) ts = Ascent
             | and $ map (uncurry (>=)) ts = Descent
             | otherwise                   = Karma
             where ts = zip xs $ tail xs

我们已经从Data.List中实现了sort的实现,可以用来实现此目的。

import Data.List (sort)

sorted xs 
    | sort xs == xs = Ascending
    | reverse (sort xs) == xs = Descending
    | otherwise = NotSorted

如果排序后的列表等于列表,则必须使用升序排序。

如果反向排序的列表等于该列表,则必须使用降序排序。

否则,不进行排序。

正如@ Benjamin-Hodgson指出的那样,可能需要考虑边缘条件。 使用此实现,一个空列表计为已排序,一个项目的列表也是如此,重复的同一项目的列表也是如此。

用法:

λ> sorted [1..5]
Ascending
λ> sorted [5,4..1]
Descending
λ> sorted [1,3,1]
NotSorted
λ> sorted []
Ascending
λ> sorted [1]
Ascending
λ> sorted [1,1,1]
Ascending

或者,我们可以将sortBy用于相反的情况,以避免必须完全反转列表。 这只是按默认的比较功能排序,且参数已翻转,因此小于小于变为大于大于。

import Data.List (sort, sortBy)

sorted xs 
    | sort xs == xs = Ascending
    | sortBy (flip compare) xs == xs = Descending
    | otherwise = NotSorted

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM