Haskell:写一个漂亮的快排

写快速排序几乎是很多人被Haskell圈粉的第一瞬间,然而可能很多人一开始的实现并不是最优的。此处记录了我的心路历程。

最初的实现

quick_sort :: Ord a => [a] -> [a]
quick_sort [] = []
quick_sort [x] = [x]
quick_sort xs = quick_sort [xs !! i | i <- [0..len-1], xs !! i <= mid_num && i /= half_pos] 
                ++ [mid_num] 
                ++ quick_sort [xs !! i | i <- [0..len-1], xs !! i > mid_num && i /= half_pos] 
                where mid_num = xs !! half_pos
                      half_pos = floor $ fromIntegral (len / 2)
                      len = length xs

使用filter

上面的代码存在两点可以改进的东西:
1.使用filter函数取得比中值小的所有数,语法比列表解析式更加简洁
2.如果数据是随机分布的,可以不必取中位元素,取开头元素即可

quick_sort :: Ord a => [a] -> [a]
quick_sort [] = []
quick_sort [x] = [x]
quick_sort xs = quick_sort (filter (< mid_num) xs)
                ++ quick_sort (filter (== mid_num) xs)
                ++ quick_sort (filter (> mid_num) xs)
                where mid_num = head xs

张淞老师的书里面写得更简单:

quick_sort :: Ord a => [a] -> [a]
quick_sort [] = []
quick_sort [x] = [x]
quick_sort (x:xs) = quick_sort (filter (<= x) xs)
                    ++ [x]
                    ++ quick_sort (filter (> x) xs)

只需要filter一次

进一步地,上面的算法每次分割对列表扫描2次(两次filter),但实际上一次就够了(两部分列表各为补集)。实现如下:

sep :: Ord a => a -> [a] -> ([a], [a])
sep _ [] = ([], [])
sep sep_num (x:xs) | x <= sep_num = ([x] ++ fst sep_xs, snd sep_xs)
                   | otherwise = (fst sep_xs, [x] ++ snd sep_xs)
                   where sep_xs = sep sep_num xs


quick_sort :: Ord a => [a] -> [a]
quick_sort [] = []
quick_sort [x] = [x]
quick_sort (x:xs) = quick_sort (fst sep_tuple)
                    ++ [x]
                    ++ quick_sort (snd sep_tuple)
                    where sep_tuple = sep x xs

优雅地解决问题

几个月后又回顾这段代码:

  • 善用模式匹配!(l, r)的写法比fst, snd的写法好很多。
  • 善用标准库!Data.List.partition提供了sep的功能,没必要reinvent
  • 对代码里面的冗余要敏锐!quick_sort [x] = [x]完全没有必要。
  • 使用默认构造器提高效率![x]++xs不如x:xs效率高。
import Data.List

quickSort :: Ord a => [a] -> [a]
quickSort [] = []
quickSort (x:xs) = quickSort l ++ (x : quickSort r) where 
    (l, r) = partition (<x) xs

写好之后还要quickcheck一下成果:

import Test.QuickCheck

sorted :: Ord a => [a] -> Bool
sorted [] = True
sorted (x:[]) = True
sorted (x:y:xs) = x <= y && sorted (y:xs)

prop_sort :: Ord a => [a] -> Bool
prop_sort xs = sorted (quickSort xs)

成功通过

*Main> :l sort.hs
[1 of 1] Compiling Main             ( sort.hs, interpreted )
Ok, modules loaded: Main.
*Main> quickCheck prop_sort
+++ OK, passed 100 tests.

完整的代码

import Data.List (partition)
import Test.QuickCheck

-- qsort

quickSort :: Ord a => [a] -> [a]
quickSort [] = []
quickSort (x:xs) = quickSort l ++ [x] ++ quickSort r where 
    (l, r) = partition (<x) xs

sorted :: Ord a => [a] -> Bool
sorted [] = True
sorted (x:[]) = True
sorted (x:y:xs) = x <= y && sorted (y:xs)

-- check

prop_sort :: Ord a => [a] -> Bool
prop_sort xs = sorted (quickSort xs)

-- quickCheck prop_sort
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值