我无法理解强制评估的for each
和seq
之间的差异,这也是同样的。
答案 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
此处,y
和z
最初是未评估的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