haskell中seq和$有什么区别?

时间:2017-06-23 14:34:32

标签: haskell lazy-evaluation evaluation forcing

我无法理解强制评估的for eachseq之间的差异,这也是同样的。

3 个答案:

答案 0 :(得分:3)

($) :: (a -> b) -> a -> b获取函数和值。它返回一个thunk。当强制该thunk时,它会产生将函数应用于值的结果。

> let x = succ $ (2 :: Int)

> :sprint x
x = _

> x
3

> :sprint x
x = 3

($)与普通函数应用程序完全等效,但运算符优先级较低,这对于避免使用某些括号非常有用。

print (take 10 (map (* 2) (filter even [1..])))

print $ take 10 $ map (* 2) $ filter even [1..]

seq :: a -> b -> b非常不同:它在结果和第一个参数之间安排依赖,这样当强制结果时,首先计算第一个参数:

> let y = succ (1 :: Int)

> :sprint y
y = _

> let z = y `seq` (3 :: Int)

> :sprint z
z = _

> z
3

> :sprint z
z = 3

> :sprint y
y = 2

此处,yz最初是未评估的thunk。但是评估z会产生评估y的副作用,因为我们使用y安排了对seq的依赖。您还可以使用trace中的Debug.Trace来观察评估顺序:

> import Debug.Trace

> (trace "a evaluated" ()) `seq` (trace "b evaluated" ())
a evaluated
b evaluated
()

> let p = (trace "a evaluated" (1 :: Int), trace "b evaluated" (2 :: Int))

> :sprint p
p = (_,_)

> snd p
b evaluated
2

> :sprint p
p = (_,2)

> fst p
a evaluated
1

> :sprint p
p = (1,2)

seq是一个低级操作,主要用于性能原因,因为它可以控制何时评估thunk。例如,在seq的定义中使用foldl'以确保在进行下一步之前评估折叠的每个步骤的结果。它的懒惰表兄foldl不会这样做,因此它经常累积一系列深层嵌套的thunk,这会在评估时导致堆栈溢出。

答案 1 :(得分:2)

默认情况下,Haskell会被懒惰地评估。所以"正常"使用seq签名(a -> b -> b)的函数会忽略它的第一个参数然后返回它的第二个参数 - 它不能对任何一个参数做任何其他操作,因为它不会知道它们是什么!

但是seq有点特别,并且略有不同。相反,严格评估其第一个参数,然后返回其第二个参数。这对于各种目的是有用的,例如强制副作用的顺序,或者用于防止在计算期间累积大的thunk。您可以在此处找到更多信息:https://wiki.haskell.org/Seq

正如@palik所说,$的类型签名完全不同,它做了不同的事情:它将一个函数应用于一个参数。 $和普通函数应用程序之间的区别在于它具有非常低的运算符优先级,这使您可以避免编写大量括号。

答案 2 :(得分:1)

seq($)具有不同的类型签名:

λ> :t seq
seq :: a -> b -> b
λ> :t ($)
($) :: (a -> b) -> a -> b
λ> (+1) `seq` 2
2
λ> (+1) $ 2
3