Если вы создаете функцию сравнения, которая прослеживает свои аргументы, как это в командной строке 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'
Если вы хотите, чтобы понять, как это работает, посмотрите исходный код функции сортировки и оценки «сортировки„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». Я опустил фактическое сравнение, потому что он скрыт внутри вызова «раздела». Фактически «раздел» также ленив, поэтому на самом деле аргумент другому «qsort» не был оценен, насколько я показал его.
Эта последняя часть неверна; компиляторы не могут сделать вывод! – porges
Porges: В то время как компилятор может быть жестко связан с анализом намерений в конкретных случаях, вы не ** ** должны делать вывод * намерение *. Вам необходимо механически использовать теорему, доказавшую, что оптимизированная версия кода математически равна исходной версии. Функциональные языки упрощают эту теорему, отвергая побочные эффекты. –
Возможно, но я не знаю каких-либо компиляторов Haskell, кроме как автоматических теоретических прокси-серверов как часть их прохода оптимизации. Причина, по которой этот состав функций работает, объясняется исключительно дефолтным характером Haskell. – porges