Временные затраты оператора `seq` в Haskell
-
14-12-2019 - |
Вопрос
Этот FAQ Говорит, что
Оператор seq
seq :: a -> b -> b
Икс
seq
Y оценит X, достаточно, чтобы проверить, что он не внизу, а затем отбросьте результат и оцените Y.Это может показаться не полезным, но это означает, что X гарантированно оценивается до рассмотрения Y.
Это очень мило со стороны Haskell, но значит ли это, что в
x `seq` f x
стоимость оценки x
будет выплачена дважды («сбросить результат»)?
Решение
А seq
функция отбросит значение x
, но поскольку значение было вычислено, все ссылки на x
«обновлены», чтобы больше не указывать на неоцененную версию x
, а вместо этого указывать на оцененную версию.Итак, хотя seq
оценивает и отбрасывает x
, значение было оценено для других пользователей x
а также, что приводит к отсутствию повторных оценок.
Другие советы
Нет, это не «вычислить и забыть», это «вычислить», что требует кэширования.
Например, рассмотрим этот код:
let x = 1 + 1
in x + 1
Поскольку Haskell ленив, это оценивается как ((1 + 1) + 1)
.Thunk, содержащий сумму thunk и единицы, причем внутренний thunk равен единице плюс один.
Давайте воспользуемся javascript, неленивым языком, чтобы показать, как это выглядит:
function(){
var x = function(){ return 1 + 1 };
return x() + 1;
}
Объединив такие мысли, можно вызвать переполнение стека, если это делается неоднократно, так seq
в помощь.
let x = 1 + 1
in x `seq` (x + 1)
Я лгу, когда говорю вам, что это оценивается как (2 + 1)
, но это почти правда - просто вычисление 2 принудительно происходит до того, как произойдет остальное (но 2 все равно вычисляется лениво).
Возвращаясь к javascript:
function(){
var x = function(){ return 1 + 1 };
return (function(x){
return x + 1;
})( x() );
}
Я считаю x
будет оценен только один раз (и результат сохранится для использования в будущем, что типично для ленивых операций).Именно такое поведение делает seq
полезный.
Вы всегда можете уточнить у unsafePerformIO
или trace
…
import System.IO.Unsafe (unsafePerformIO)
main = print (x `seq` f (x + x))
where
f = (+4)
x = unsafePerformIO $ print "Batman!" >> return 3
Конечно seq
само по себе делает нет «оценивать» что-либо.Он просто записывает зависимость порядка принудительного выполнения.Само принуждение инициируется сопоставлением с образцом.Когда seq x (f x)
вынужден, x
сначала будет принудительно выполнено (запоминание полученного значения), а затем f x
будет вынужден.Ленивая оценка Haskell означает, что он запоминает результаты принудительного выполнения выражений, поэтому повторная «оценка» (здесь пугающие кавычки) выполняться не будет.
Я заключил слово «оценка» в пугающие кавычки, потому что оно подразумевает полный оценка.По словам Викибук по Хаскелю,
«Значения Haskell очень многоуровневы;«Оценка» значения Haskell может означать оценку любого из этих уровней».
Позвольте мне повторить: seq
сам по себе ничего не оценивает. seq x x
не оценивает x
ни при каких обстоятельствах. seq x (f x)
ничего не оценивает, когда f = id
, вопреки чему Отчет кажется, говорил.