如果按升序或降序排序或未在haskell中排序,请检查列表?

时间:2017-11-12 19:36:48

标签: list sorting 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:这不是作业/工作的东西,而是我想要学习的东西。

4 个答案:

答案 0 :(得分:5)

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

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

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

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

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

sorted [] = Both
sorted [_] = Both

现在我们需要定义归纳案例。什么时候列表是按升序排序的?如果所有元素都(严格地)大于前一个元素。模拟如果所有元素(严格地)小于前一元素,则列表按降序排序。现在让我们假设严格。以后很容易改变函数定义。

因此,如果我们有一个包含两个或更多元素的列表,如果以第二个元素开头的列表为AscendingAscending,则列表为Both,{{1或者换句话说:

x < y

同样适用于降序:如果列表的其余部分按降序排列,并且第一个元素大于第二个,则列表按降序排列:

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) 设为TypeOfSort

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

TypeOfSort

这实际上形成了一个 monoid intersect Both x = x intersect x Both = x intersect Ascending Ascending = Ascending intersect Descending Descending = Descending intersect _ _ = NotSorted 作为标识元素:

Both

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

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

    mempty = Both

答案 1 :(得分:4)

我的解决方案没有使用sort函数而没有递归:

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)

使用lambdas

的速度可能更快
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)))

答案 2 :(得分:2)

我猜你也可以这样做;

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

答案 3 :(得分:1)

我们已经从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