是否有可能改进功能容器的渐近性?

时间:2015-10-14 02:23:10

标签: performance haskell

请注意以下程序:

data Box = Box Int
initial         = Box 1
stepper (Box x) = Box (x+1)
getter  (Box x) = x
run 0 state     = []
run n state     = getter state : run (n-1) (stepper state)
main            = print $ sum $ run 50000000 initial

此处,run显然是线性的,因为它是从0n的递归,而stepper是一个恒定时间函数。您可以通过更改常量来验证 - 运行时线性比例变化。现在,请注意这段代码:

initial' box      = box 1
stepper' box box_ = box (\ x -> (box_ (x+1)))
getter' box       = box (\ x -> x)
run' 0 state      = []
run' n state      = getter' state : run' (n-1) (stepper' state)
main              = print $ sum $ run' 8000 initial'

这与上面的程序完全相同,唯一改变的是函数用作容器,而不是数据类型。然而,它是二次的:stepper' state永远不会执行,创建一个越来越大的thunk,在每一步重新评估。无论常量如何不同,两个程序都需要相同的运行时间。我相信第二个程序可以用一个术语评估为正常形式的方法来修复,但是GHC没有提供,所以,是否可以修复第二个程序,使其不再是二次方程式?< /强>

1 个答案:

答案 0 :(得分:6)

在我的机器上,以下运行速度仅比快速代码慢三倍:

mkBox n box  = box n
getter' box  = box (\ x -> x)
initial'     = mkBox 1
stepper' box = mkBox $! getter' box+1
run' 0 state = []
run' n state = getter' state : run' (n-1) (stepper' state)
main         = print $ sum $ run' 50000000 initial'

有两个主要区别:第一,我镜像了stepper (Box x) = Box (x+1)定义,也可以写成stepper box = Box (getter box + 1)。为了镜像它,我定义了mkBox镜像Box。第二个关键区别是我明确地将参数设为mkBox严格;我相信你的快速版本GHC的严格性分析在幕后做到这一点。