【发布时间】:2018-10-29 14:30:16
【问题描述】:
如果我正确理解了here 的讨论,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: 我看到这个问题已被标记为之前询问单态限制的问题的副本。当我遇到这个问题时,我不知道这是由于限制。因此,如果有人发现他/她自己处于我的位置,我想这个问题的答案会有所帮助。
【问题讨论】:
-
如果你尝试
(\x -> x `seq` x) (fib 30)和let x = (fib 30) :: Int in x `seq` x会发生什么? -
已添加到帖子中,现在更加惊讶!
-
问题是
fib具有多态类型(Ord a, Num a) => a -> a。因此,在表达式x `seq` x中,第一个x可以实例化为与第二个x不同的单态类型。例如,第一个x可以是Int,而第二个x可以是Double。没有什么可以迫使两者成为同一类型。这就是为什么x被评估两次的原因。 -
解决方法很简单。给
fib一个明确的类型签名,如let fib :: Int -> Int; fib x = if x <= 1 then x else fib (x - 1) + fib (x - 2)。 -
避免这种性能问题正是引入单态限制的原因。没有 MR,
x是多态的,x `seq` x实际上意味着类似于x () `seq` x (),好像x是一个函数,而seq将调用该函数两次(可以理解)。
标签: performance haskell