Reputation: 3
As homework, we were supposed to implement a few functions and then come up with a sorting algorithm potentially implementing those functions. first of all, these are the two needed functions:
remove :: (Ord a) => a -> [a] -> [a]
remove a (x:[]) = x:[]
remove a (x:xs) =
if a == x
then xs
else x:(remove a xs)
and:
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)
My sorting Algorithm is just supposed to take the smallest element and put it at the beginning of the list:
sort :: (Integral a) => [a] -> [a]
sort [] = []
sort x = smallest x : sort (rest x)
where
rest = remove (smallest x) x
The Error I get is
I'm surely missing something obvious, but I can't figure out what it is. help me pls.
Upvotes: 0
Views: 148
Reputation: 2658
If we look at the entire error message from ghci, which is:
* 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.
We see that rest :: [a]
.
But you are attempting to apply something to rest
here:
sort x = smallest x : sort (rest x)
But you had already applied x
here:
rest = remove (smallest x) x
So, simply change the former to:
sort x = smallest x : sort rest
And it will compile. That said, I haven't checked the logic of the algorithm itself.
I rewrote the code with the LambdaCase
extension which looks neater.
I also renamed your sorting function to sort'
so that it doesn't clash with Data.List (sort)
.
You had 2 problems in sort'
. First, you didn't handle the case of a singleton list, which resulted in a non-exhaustive pattern match. I added the clause [x] -> [x]
for this. Second, you should use a let
binding to evaluate smallest xs
only once, I fixed that too. Third, you had the problem that Thomas M. DuBuisson described, which I fixed. And below is the code, with quickCheck
and all.
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
Running QuickCheck:
> quickCheck prop_sort
+++ OK, passed 100 tests.
With the alternate changes proposed by Willem Van Onsem, smallest, sort
instead looks as:
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
can also be written as a fold:
import Data.List (foldl1)
smallest :: Ord a => [a] -> a
smallest = foldl1 $ \x y -> if x < y then x else y
and even shorter with by also using min :: Ord a => a -> a -> a
:
import Data.List (foldl1)
smallest :: Ord a => [a] -> a
smallest = foldl1 min
wherefore our solution can be written, in total as:
sort' :: Ord a => [a] -> [a]
sort' = \case [] -> [] ; xs -> let s = foldl1 min xs in s : sort' (remove s xs)
Upvotes: 2
Reputation: 477607
First of all, your remove
function will error on the empty list, and never remove an element from a list with one element. You probably want to write:
remove :: Eq a => a -> [a] -> [a]
remove a [] = []
remove a (x:xs) | a == x = xs
| otherwise = x : remove a xs
Next your smallest
behaves weird as well since it will not select the smallest from a list with one element (in that case the smallest element is that case). We can rewrite it like:
smallest :: Ord a => [a] -> a
smallest [x] = x
smallest (x:y:xs) | x < y = smallest (x:xs)
| otherwise = smallest (y:xs)
Now that we have fixed these, we can focus on the real sorting function. I do not really see why you want to use a helper function here to remove the element, you can use a variable small
to store the smallest element in the list, and then simply use recursion, like:
sort :: Ord a => [a] -> [a]
sort [] = []
sort x = small : sort (remove small x)
where small = smallest x
Now with this sorting algorithm, we obtain:
*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]
Upvotes: 2