【问题标题】:My sorting algorithm doesn't work我的排序算法不起作用
【发布时间】:2017-06-07 16:00:33
【问题描述】:

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

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] -> [a]”与实际类型“[a]”匹配
  • 函数“rest”应用于一个参数, 但它的类型“[a]”没有 在‘sort’的第一个参数中,即‘(rest x)’ 在‘(:)’的第二个参数中,即‘sort (rest x)’

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

【问题讨论】:

  • 在 where 块中定义
  • 注意,修复后,算法似乎对列表进行排序和去重。 sort [1,1,1,1] == [1]。这是因为remove 会删除所有匹配的元素,而不是只删除一个。
  • 但是你调用rest x,但是rest的定义没有任何参数。

标签: algorithm sorting haskell


【解决方案1】:

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

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

接下来,smallest 的行为也很奇怪,因为它不会从具有 one 元素的列表中选择最小的元素(在这种情况下,最小的元素就是这种情况)。我们可以像这样重写它:

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]

【讨论】:

    【解决方案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] -&gt; [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 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 =&gt; a -&gt; a -&gt; 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”的数组
    • 输入什么?
    • sort [100,99..1],它只取最后一个元素并重复它
    • JohnDoe,那是因为您不会同时删除最后的最小元素:remove a (x:[]) = x:[]
    • @JohnDoe 通过修复逻辑更新了我的答案。请记住,此算法很慢且复杂O(n^2)。更好的解决方案是快速排序或合并排序。 smthngsmwhr.wordpress.com/2012/11/09/…
    猜你喜欢
    • 1970-01-01
    • 2014-12-30
    • 2014-04-18
    • 1970-01-01
    • 2019-11-12
    • 2020-01-04
    • 1970-01-01
    相关资源
    最近更新 更多