Mri*_*ury 14 performance haskell
如果我正确理解这里的讨论,seq不应该两次评估一个值,因为x `seq` x应该评估x一次.
那为什么我有这种行为?
?> :set +s
?> let fib x = if x <= 1 then x else fib (x - 1) + fib (x - 2)
(0.01 secs, 102,600 bytes)
?> fib 30
832040
(2.49 secs, 638,088,448 bytes)
?> let x = fib 30 in x
832040
(2.47 secs, 638,088,792 bytes)
?> let x = fib 30 in x `seq` x
832040
(4.95 secs, 1,276,067,128 bytes)
这显然是双重评估?我误会了什么吗?
编辑:正如下面的@danidiaz所问,我也评估过
?> (\x -> x `seq` x) (fib 30)
832040
(2.51 secs, 638,087,888 bytes)
?> let x = (fib 30) :: Int in x `seq` x
832040
(2.52 secs, 732,476,640 bytes)
现在更令人惊讶的是.
编辑2:我看到这个问题已被标记为先前问题的副本,该问题询问单态限制.当我遇到这个问题时,我不知道这是由于限制.因此,如果有人发现他/她在我的位置,我想这个问题的答案会有所帮助.
dan*_*iaz 15
对于这个答案的第一部分,:set -XNoMonomorphismRestriction在ghci中.这将在后面解释.
天真地,人们会期望在Haskell中let x = 5 in (x + 1,x + 2)总是相当于(\x -> (x + 1, x + 2)) 5.但他们有不同的类型!
let x = 5 in (x + 1,x + 2) :: (Num a, Num b) => (a, b)
(\x -> (x + 1,x + 2)) 5 :: Num b => (b, b)
Run Code Online (Sandbox Code Playgroud)
原因是Haskell的一个特性叫做let-bound多态.与lambda绑定标识符不同,绑定在a中的标识符let可以在主体中以不同方式实例化let.例如:
ghci> let f = id in (f True, f 'a')
(True,'a')
ghci> (\f -> (f True, f 'a')) id
*** ERROR ***
Run Code Online (Sandbox Code Playgroud)
现在,你没有为你的fib功能提供类型签名,而推断的那个是类似的
fib :: (Ord a, Num a) => a -> a
Run Code Online (Sandbox Code Playgroud)
将针对不同情况的工作Num一样Int,Float等等.
但正因为如此,当你写作时x `seq` x,ghci无法确定两者x实际上是同一类型的!如果他们可能不同,那么他们就无法分享.
这就是(\x -> x `seq` x) (fib 30)分享原因的原因.因为x是lambda绑定的,所以编译器确保两个出现的值实际上是相同的.同样let x = (fib 30) :: Int in x `seq` x因为我们使用显式类型删除了多态性.
还有另外一条出路.打开-XMonomorphismRestriction扩展会增加默认类型的数量,导致let表达式比人们预期的更单一.在这种情况下,这应该足以恢复共享.
| 归档时间: |
|
| 查看次数: |
177 次 |
| 最近记录: |