Временная стоимость оператора Haskell `seq`
Этот FAQ говорит, что
Оператор seq
seq :: a -> b -> b
x seq
y будет оценивать x, достаточно, чтобы проверить, что это не дно, тогда отбросить результат и оценить y. Это может показаться нецелесообразным, но это означает, что x гарантированно оценивается до рассмотрения y.
Это ужасно приятно Haskell, но означает ли это, что в
x `seq` f x
стоимость оценки x
будет выплачена дважды ( "отбросить результат" )?
Ответы
Ответ 1
Функция seq
будет отбрасывать значение x
, но поскольку значение было оценено, все ссылки на x
"обновлены", чтобы больше не указывать на неоценимую версию x
, но для вместо этого укажите на оцениваемую версию. Таким образом, даже если seq
оценивает и отбрасывает x
, значение также оценивалось для других пользователей x
, что не приводило к повторным оценкам.
Ответ 2
Нет, он не вычисляет и не забывает, он вычисляет - что заставляет кешировать.
Например, рассмотрим этот код:
let x = 1 + 1
in x + 1
Так как Haskell ленив, это оценивается как ((1 + 1) + 1)
. Бункер, содержащий сумму куска и одного, внутренний кусок - один плюс один.
Позвольте использовать 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() );
}
Ответ 3
Я считаю, что x
будет оцениваться только один раз (и результат сохраняется для будущего использования, что характерно для ленивых операций). Такое поведение делает seq
полезным.
Ответ 4
Конечно seq
сам по себе не "оценивает" все. Он просто записывает зависимость форсирующего порядка. Само форсирование запускается путем сопоставления шаблонов. Когда seq x (f x)
принудительно, x
будет принудительно первым (memoizing результирующее значение), а затем f x
будет принудительно. Haskell ленивая оценка означает, что она запоминает результаты форсирования выражений, поэтому повторная "оценка" (пугающие цитаты здесь) не будет выполнена.
Я помещаю "оценку" в страшные цитаты, потому что это подразумевает оценку полная. В словах Haskell wikibook,
" Значения Haskell очень многоуровневые, "оценка" значения Haskell может означать оценку вплоть до любого этих слоев ".
Повторю: seq
сам по себе ничего не оценивает. seq x x
не оценивает x
при любых обстоятельствах. seq x (f x)
ничего не оценивает, когда f = id
, в отличие от отчет, который, кажется, говорил.
Ответ 5
Вы всегда можете проверить с помощью unsafePerformIO
или trace
...
import System.IO.Unsafe (unsafePerformIO)
main = print (x `seq` f (x + x))
where
f = (+4)
x = unsafePerformIO $ print "Batman!" >> return 3