我已經實現歸併排序的兩個版本在Haskell就像如下:爲什麼在使用foldl'實現Haskell時MergeSort更快?
mergeSort1 :: (Ord a) => [a] -> [a]
mergeSort1 xs = foldl' (\acc x -> merge [x] acc) [] xs
和
mergeSort2 :: (Ord a) => [a] -> [a]
mergeSort2 [] = []
mergeSort2 (x:[]) = [x]
mergeSort2 xs = (mergeSort2 $ fst halves) `merge` (mergeSort2 $ snd halves)
where halves = splitList xs
其中 '合併' 和 'splitList' 是這樣實現的:
merge :: (Ord a) => [a] -> [a] -> [a]
merge [] [] = []
merge xs [] = xs
merge [] ys = ys
merge [email protected](x:xs) [email protected](y:ys)
| x < y = x:merge xs all_y
| otherwise = y:merge all_x ys
splitList :: [a] -> ([a], [a])
splitList zs = go zs [] [] where
go [] xs ys = (xs, ys)
go [x] xs ys = (x:xs, ys)
go (x:y:zs) xs ys = go zs (x:xs) (y:ys)
在ghci中使用last $ mergeSort2 [1000000,999999..0]
會導致在超過一分鐘的處理後顯示數字1000000,同時做last $ mergeSort1 [1000000,999999..0]
結果顯示只有5秒後才能顯示最後一個元素。
我可以理解爲什麼mergeSort1使用比mergeSort2少得多的內存,因爲foldl'的尾遞歸性等等。
我不明白爲什麼mergeSort1比mergeSort2快這麼大的區別?
難道是splitList是mergeSort2中的瓶頸,每次調用都會生成兩個新列表?
不要使用ghci測試這些東西;它完全不代表編譯的程序的性能。用優化編譯測試程序('-O');你可以使用['criterion'](http://hackage.haskell.org/package/criterion/)來對它進行基準測試,或者如果你想快速和骯髒,只需編寫兩個單獨的測試程序並使用Unix時間用於計時的實用程序。 – 2012-08-10 19:27:24
@sacundim是的,我已經測試了它們都是以可執行格式進行測試,用-O2進行編譯,但差別仍然很大。 mergeSort1在4秒內完成,而mergeSort2在58秒內完成。 – tonlika 2012-08-10 19:35:14