Как компилятор Haskell обрабатывает оператор 'where'?
-
01-10-2019 - |
Вопрос
В следующей функции мне интересно, достаточно ли умен компилятор, чтобы решить это x
будет ли он оставаться постоянным или будет вычислять начало списка для каждого элемента в списке?(Я использую GHC)
allSame :: Eq a => [a] -> Bool
allSame xs = all (==x) xs where x = head xs
Решение
Семантика 'where' в GHC заключается в том, что для 'x' будет выделено одно замыкание и оно будет общим для всех применений.Будет сгенерировано новое замыкание для функции (== 'x'), и оптимизатор выведет его, так что оно генерируется только один раз за обход.
Чтобы точно увидеть, какой код генерируется, проверьте Ядро (напримерчерез ghc-ядро).GHC оптимизирует код, чтобы:
M.allSame a eq xs =
all
(let
ds =
case xs of
[] -> error "bad head"
x : _-> x
in
\y -> x == y
) xs
Если вас беспокоит производительность, рассмотрите возможность использования векторов, так как отдельные обходы будут сливаться, устраняя рекурсию.
Другие советы
Я думаю, что Haskell просто оценивает то, что нужно: так ищет x
и находит это в where
-пункт. Тогда я думаю, что это вычисляет x
один раз и делает all
.
Если вы хотите проверить его, вы можете написать функцию myall
это делает рекурсию в all (==x)
, но по сути просто печатает сравнительный элемент. Поэтому вы увидите, если вы получите новый аргумент каждый раз или если он останется только тем же одновременно каждый раз.
Редактировать:
Вот небольшая функция, чтобы проверить это: myall
Просто собирает первые аргументы и ставит его в список.
myall x [] = [x]
myall x xs = x:(myall x (tail xs))
test xs = myall (x) xs where x = head xs
Если вы звоните test [1,2,3]
, вы увидите, что результат [1,1,1,1]
, т.е. во-первых x
оценивается в 1
, после этого myall
оценивается.