Если вы создаете функцию сравнения, которая отслеживает свои аргументы, например, в командной строке 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» foobar »вручную на бумаге.
qsort [] = []
qsort (x:xs) = qsort less ++ [x] ++ qsort greater
where (less, greater) = partition (< x) xs
So
qsort ('f':"oobar")
= qsort ('b':"a") ++ "f" ++ qsort ('o':"or")
= ("a" ++ "b") ++ "f" ++ qsort ('o':"or")
И теперь мы сделали достаточно, чтобы найти, что «a» - это первый элемент в результате, без необходимости оценивать другой вызов «qsort». Я пропустил фактическое сравнение, потому что оно скрыто в вызове "partition". На самом деле «раздел» также ленив, поэтому фактически аргумент другой «qsort» не был оценен, насколько я это показал.