Reputation: 61
I am trying to solve this question: Define a recursive function msort :: Ord a => [a] -> [a] that imple- ments merge sort, which can be specified by the following two rules: Lists of length 1 are already sorted; Other lists can be sorted by sorting the two halves and merging the resulting lists.
But I am having a hard time understanding how exactly the code of merge sort work.
here is my code ::
merge :: Ord a => [a] -> [a] -> [a]
merge [] [] = []
merge (x:xs) (y:ys) | y > x = (x:xs) ++ (y:ys)
|otherwise = (y:ys) ++ (x:xs)
msort :: Ord a => [a] -> [a]
--base case , if length less than or equal 1 , then list is already sorted
msort [] = [] -- how to implement base case?
msort (x:xs) = merge (take (length(x:xs) `div` 2 ) (x:xs)) (drop (length(x:xs) `div` 2 ) (x:xs))
Upvotes: 0
Views: 3040
Reputation: 48591
Because I can't resist a challenge, here's an efficient, incremental, stable top-down mergesort. The same design with a couple minor tweaks should produce an efficient (but non-incremental) mergesort for a strict language; laziness is only used in the merge
function, so it should be possible to do some juggling to avoid it if desired.
{-# language BangPatterns #-}
import Control.Monad
-- Divide a list into the first n `quot` 2 elements and
-- the rest. The front of the list is reversed.
half :: [a] -> ([a], [a])
half = join (go []) where
go front ts [] = (front, ts)
go front ts [_] = (front, ts)
go front (t:ts) (_:_:hs) = go (t:front) ts hs
-- Some care is required to make the sort stable.
merge :: Ord a => Bool -> [a] -> [a] -> [a]
merge _ [] ys = ys
merge _ xs [] = xs
merge up xxs@(x : xs) yys@(y : ys)
| (if up then (<=) else (<)) x y = x : merge up xs yys
| otherwise = y : merge up xxs ys
msort :: Ord a => [a] -> [a]
msort = go True where
go _ [] = []
go _ xs@[_] = xs
go !up xs = merge up frontSorted rearSorted where
frontSorted = go (not up) front
rearSorted = go up rear
(front, rear) = half xs
A version that uses laziness more heavily is considerably easier to write and understand, but may be susceptible to a subtle space leak if care is not taken to control certain compiler optimizations:
-- Split a list in half, with both halves in order
half :: [a] -> ([a], [a])
half = join go
where
go t [] = ([], t)
go t [_] = ([], t)
go (t : ts) (_:_:hs) = (t : front, rear)
where (front, rear) = go ts hs -- Interesting laziness
merge :: Ord a => [a] -> [a] -> [a]
merge [] ys = ys
merge xs [] = xs
merge xxs@(x:xs) yys@(y:ys)
| x <= y = x : merge xs yys
| otherwise = y : merge xxs ys
msort :: Ord a => [a] -> [a]
msort [] = []
msort [x] = [x]
msort xs = merge (msort front) (msort rear)
where (front, rear) = half xs
For completeness, I should mention that bottom-up mergesort seems rather more natural in Haskell.
Upvotes: 1
Reputation: 500
Your merge
function has two issues.
i) Patterns are not exhaustive: If precisely one of the lists is empty, there's no match. You should have
merge :: Ord a => [a] -> [a] -> [a]
merge [] ys = ys
merge xs [] = xs
merge (x:xs) (y:ys) = something
ii) Your something
is not actually merge sorting, it is looking at the heads and concatenates the two lists based on the comparison of the heads. For instance, if given [1,4,5]
and [2,3,4]
, 1
would be bound to x
, 2
to y
, and then since y > x
holds, then it would return [1,4,5,2,3,4]
, clearly unsorted, though the two given lists were sorted.
Imagine the merging as follows. Given two lists, you see only the heads, and pick the smaller one. In place of the one you removed, the next element rolls in, and you keep doing the same thing. This is a recursive process; after each pick, you do the same thing. In code:
merge (x:xs) (y:ys) | x < y = x : merge xs (y:ys)
| otherwise = y : merge (x:xs) ys
As for the actual sorting :: Ord a => [a] -> [a]
, you must also work with halves that are already sorted, which also calls for recursion:
msort :: Ord a => [a] -> [a]
msort [] = []
msort [x] = [x]
msort xs = merge firstHalfSorted secondHalfSorted
where firstHalfSorted = msort . fst $ halves
secondHalfSorted = msort . snd $ halves
halves = splitAt halfPoint xs
halfPoint = length xs `div` 2
Upvotes: 4