Стоимость времени оператора Haskell `seq` - PullRequest
12 голосов
/ 14 марта 2012

В этом FAQ говорится, что

Оператор seq:

seq :: a -> b -> b

x seq y будет вычислять x, достаточно, чтобы убедиться, что этоне снизу, затем откажитесь от результата и оцените y.Это может показаться бесполезным, но это означает, что x гарантированно будет оцениваться до того, как будет рассмотрено y.

Это ужасно хорошо для Haskell, но означает ли это, что в

x `seq` f x

стоимость оценки x будет оплачена дважды («отменить результат»)?

Ответы [ 5 ]

18 голосов
/ 14 марта 2012

Функция seq отбрасывает значение x, но, поскольку значение было оценено, все ссылки на x "обновляются", чтобы больше не указывать на неоцененную версию x, но на вместо этого укажите на оцененную версию. Таким образом, даже если seq оценивает и отбрасывает x, значение было оценено и для других пользователей x, что не привело к повторным оценкам.

12 голосов
/ 14 марта 2012

Нет, это не вычисление и забыть, это вычисление - что вызывает кеширование.

Например, рассмотрим этот код:

 let x = 1 + 1
 in x + 1

Поскольку Haskell ленив, это оценивается как ((1 + 1) + 1).Thunk, содержащий сумму thunk и one, внутренний 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() );
 }
4 голосов
/ 14 марта 2012

Полагаю, x будет оцениваться только один раз (и результат будет сохранен для будущего использования, что характерно для ленивых операций).Именно такое поведение делает seq полезным.

1 голос
/ 15 марта 2012

Вы всегда можете проверить с помощью unsafePerformIO или trace

import System.IO.Unsafe (unsafePerformIO)

main = print (x `seq` f (x + x))
  where
    f = (+4)
    x = unsafePerformIO $ print "Batman!" >> return 3
1 голос
/ 15 марта 2012

Конечно seq само по себе не"оценивает" что-либо .Он просто записывает зависимость принудительного порядка.Само принуждение инициируется сопоставлением с образцом.Когда seq x (f x) принудительно, сначала будет принудительно x (запоминание полученного значения), а затем принудительно будет f x.Ленивая оценка Haskell означает, что он запоминает результаты форсирования выражений, поэтому повторная «оценка» (страшные кавычки здесь) не будет выполняться.

Я помещаю «оценку» в страшные кавычки, поскольку она подразумевает full оценка.В словах Haskell wikibook ,

«Значения Haskell очень многослойны;« оценка »значения Haskell может означать оценку до любого из этих слоев.»

Позвольте мне повторить: seq само по себе ничего не оценивает. seq x x не оценивает x ни при каких обстоятельствах.seq x (f x) ничего не оценивает, когда f = id, в отличие от того, что отчет , кажется, говорил.

...