我的排序算法不起作用

时间:2017-06-07 16:00:33

标签: algorithm sorting haskell

作为家庭作业,我们应该实现一些功能,然后提出可能实现这些功能的排序算法。首先,这些是两个必需的功能:

remove :: (Ord a) => a -> [a] -> [a] 
remove a (x:[]) = x:[]
remove a (x:xs) =
  if a == x
    then xs
    else x:(remove a xs)

smallest :: (Ord a) => [a] -> a
smallest (x:y:[]) =
  if x < y
    then x
    else y
smallest (x:y:xs) =
  if x < y
    then smallest (x:xs)
    else smallest (y:xs)

我的排序算法应该采用最小的元素并将其放在列表的开头:

sort :: (Integral a) => [a] -> [a]
sort [] = []
sort x = smallest x : sort (rest x)
      where
        rest = remove (smallest x) x

我得到的错误是

  • 无法匹配预期类型'[a] - &gt; [a]'与实际类型'[a]'
  • 函数'rest'应用于一个参数, 但它的类型'[a]'没有 在'sort'的第一个参数中,即'(rest x)' 在'(:)'的第二个参数中,即'sort(rest x)'

我肯定错过了一些明显的东西,但我无法弄清楚它是什么。帮帮我吧。

2 个答案:

答案 0 :(得分:2)

编译错误

如果我们查看来自ghci的整个错误消息,即:

    * Couldn't match expected type `[a] -> [a]' with actual type `[a]'
    * The function `rest' is applied to one argument,
      but its type `[a]' has none
      In the first argument of `sort', namely `(rest x)'
      In the second argument of `(:)', namely `sort (rest x)'
    * Relevant bindings include
        rest :: [a]
          (bound at ...)
        x :: [a]
          (bound at ...)
        sort :: [a] -> [a]
          (bound at ...)
Failed, modules loaded: none.

我们看到rest :: [a]。 但是您试图在rest处应用某些内容:

sort x = smallest x : sort (rest x)

但您已在此处应用x

rest = remove (smallest x) x

所以,只需将前者改为:

sort x = smallest x : sort rest

它会编译。也就是说,我没有检查算法本身的逻辑。

修复逻辑:

我用LambdaCase扩展名重写了代码,看起来更整洁。 我还将您的排序功能重命名为sort',以便它不会与Data.List (sort)冲突。

sort'中有2个问题。首先,您没有处理单例列表的情况,这导致非穷举模式匹配。我为此添加了[x] -> [x]条款。其次,你应该使用let绑定来评估smallest xs只一次,我也修复过。第三,你遇到了Thomas M. DuBuisson描述的问题,我已经解决了这个问题。以下是代码,quickCheck和所有。

remove :: Eq a => a -> [a] -> [a] 
remove a = \case
  []     -> []
  (x:xs) -> if a == x then xs else x : remove a xs

smallest :: Ord a => [a] -> a
smallest = \case
  (x:y:[]) -> if x < y then x else y
  (x:y:xs) -> if x < y then smallest (x:xs) else smallest (y:xs)

sort' :: Ord a => [a] -> [a]
sort' = \case
  [ ] -> [ ]
  [x] -> [x]
  xs  -> let s = smallest xs in s : sort' (remove s xs)

prop_sort :: Ord a => [a] -> Bool
prop_sort xs = sort xs == sort' xs

运行QuickCheck:

> quickCheck prop_sort
+++ OK, passed 100 tests.

根据Willem Van Onsem提出的替代变更,smallest, sort看起来像:

smallest :: Ord a => [a] -> a
smallest = \case
  [x]      -> x
  (x:y:xs) -> if x < y then smallest (x:xs) else smallest (y:xs)

sort' :: Ord a => [a] -> [a]
sort' = \case
  [] -> []
  xs -> let s = smallest xs in s : sort' (remove s xs)

smallest也可以写成折页:

import Data.List (foldl1)

smallest :: Ord a => [a] -> a
smallest = foldl1 $ \x y -> if x < y then x else y

甚至更短,同时使用min :: Ord a => a -> a -> a

import Data.List (foldl1)

smallest :: Ord a => [a] -> a
smallest = foldl1 min
因此,我们的解决方案可以写成:

sort' :: Ord a => [a] -> [a]
sort' = \case [] -> [] ; xs -> let s = foldl1 min xs in s : sort' (remove s xs)

答案 1 :(得分:2)

首先,您的remove函数将在空列表中出错,并且永远不会从一个元素的列表中删除元素。你可能想写:

remove :: Eq a => a -> [a] -> [a] 
remove a [] = []
remove a (x:xs) | a == x = xs
                | otherwise = x : remove a xs

接下来你的smallest表现得很奇怪,因为它不会从带有一个元素的列表中选择最小的(在这种情况下,最小元素就是这种情况)。我们可以改写它:

smallest :: Ord a => [a] -> a
smallest [x] = x
smallest (x:y:xs) | x < y = smallest (x:xs)
                  | otherwise = smallest (y:xs)

现在我们已经解决了这些问题,我们可以专注于真正的排序功能。我真的不明白你为什么要在这里使用辅助函数来删除元素,你可以使用变量small来存储列表中的最小元素,然后简单地使用递归,如:

sort :: Ord a => [a] -> [a]
sort [] = []
sort x  = small : sort (remove small x)
    where small = smallest x

现在使用这种排序算法,我们得到:

*Main> sort [1,4,2,5]
[1,2,4,5]
*Main> sort [1,4,2,5,1,3,0,2]
[0,1,1,2,2,3,4,5]