作为家庭作业,我们应该实现一些功能,然后提出可能实现这些功能的排序算法。首先,这些是两个必需的功能:
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
我得到的错误是
我肯定错过了一些明显的东西,但我无法弄清楚它是什么。帮帮我吧。
答案 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]