如果您创建一个跟踪其参数的比较函数,例如在 GHCi 的命令行中:
> :module + Data.List Debug.Trace
> let myCompare x y = trace ("\tCmp " ++ show x ++ " " ++ show y) $ compare x y
然后您可以自己查看行为:
> sortBy myCompare "foobar"
" Cmp 'f' 'o'
Cmp 'o' 'b'
Cmp 'f' 'b'
Cmp 'a' 'r'
Cmp 'b' 'a'
a Cmp 'b' 'r'
b Cmp 'f' 'o'
Cmp 'f' 'r'
f Cmp 'o' 'o'
Cmp 'o' 'r'
o Cmp 'o' 'r'
or"
Haskell 正在懒惰地评估字符串,一次一个字符。找到每个字符时打印左列,右列记录所需的比较,如“trace”打印。
请注意,如果您编译它,尤其是在优化的情况下,您可能会得到不同的结果。优化器运行一个严格分析器,它可能会注意到整个字符串都被打印出来了,所以急切地评估它会更有效。
然后尝试
> head $ sortBy myCompare "foobar"
Cmp 'f' 'o'
Cmp 'o' 'b'
Cmp 'f' 'b'
Cmp 'a' 'r'
Cmp 'b' 'a'
'a'
如果您想了解这是如何工作的,请查看 sort 函数的源代码并在纸上手动评估“sort "foobar"”。
qsort [] = []
qsort (x:xs) = qsort less ++ [x] ++ qsort greater
where (less, greater) = partition (< x) xs
所以
qsort ('f':"oobar")
= qsort ('b':"a") ++ "f" ++ qsort ('o':"or")
= ("a" ++ "b") ++ "f" ++ qsort ('o':"or")
现在我们已经做了足够的工作来发现“a”是结果中的第一项,而无需评估对“qsort”的其他调用。我省略了实际比较,因为它隐藏在对“分区”的调用中。实际上“分区”也是惰性的,所以事实上,就我所展示的而言,另一个“qsort”的参数还没有被评估过。