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 は、一度に 1 文字ずつ文字列を遅延評価しています。左側の列は各文字が検出されるたびに出力され、右側の列は「trace」によって出力されるように、必要な比較が記録されます。
特に最適化をオンにしてこれをコンパイルすると、異なる結果が得られる可能性があることに注意してください。オプティマイザーは厳密性アナライザーを実行し、文字列全体が出力されることにおそらく気付くので、積極的に評価する方が効率的です。
それから試してください
> head $ sortBy myCompare "foobar"
Cmp 'f' 'o'
Cmp 'o' 'b'
Cmp 'f' 'b'
Cmp 'a' 'r'
Cmp 'b' 'a'
'a'
これがどのように機能するかを理解したい場合は、ソート機能のソースコードを調べて、紙の上で「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")
これで、「qsort」への他の呼び出しを評価することなく、「a」が結果の最初の項目であることを見つけるのに十分なことができました。「パーティション」の呼び出し内に隠されているため、実際の比較は省略しました。実は「partition」も怠け者なので、実はもう一方の「qsort」への引数は、私が示した限りでは評価されていません。