ghci使用什么优化技术来加速递归映射?

时间:2015-03-07 10:24:42

标签: haskell optimization expression

说我有以下功能:

minc = map (+1)
natural = 1:minc natural

它似乎像这样展开:

1:minc(1:minc(1:minc(1:minc(1:minc(1:minc(1:minc(1:minc(1:minc(1:minc...
1:2:minc(minc(1:minc(1:minc(1:minc(1:minc(1:minc(1:minc(1:minc(1:minc...
1:2:minc(2:minc(2:minc(2:minc(2:minc(2:minc(2:minc(2:minc(2:minc(minc...
1:2:3:minc(3:minc(3:minc(3:minc(3:minc(3:minc(3:minc(3:minc(minc(minc...
...                                                                

虽然进行了懒惰评估,但要在列表中构建每个新号码n,必须展开n次表达式,这会使我们O(N^2)复杂化。但是通过执行时间,我可以看到真正的复杂性仍然是线性的!

Haskell在这种情况下使用哪种优化以及它如何展开这个表达式?

2 个答案:

答案 0 :(得分:5)

每个递归步骤之间共享自然列表。图表的评估方式如下。

1:map (+1) _
 ^         |
 `---------'

1: (2 : map (+1) _)
      ^          |
      `----------'

1: (2 : (3 : map (+1) _)
           ^          |
           `----------'

此共享意味着代码使用O(n)时间而不是预期的O(N ^ 2)。

答案 1 :(得分:2)

  

要在列表中构建每个新数字n,必须展开n次表达式,这样我们才能 O N 2 )复杂性。

不完全。以这种方式展开第一个 N 数字的复杂性实际上是 O N 2 显然我错了 [1] 。但是,如果您只请 N 号码,那么它的实际评估结果如下:

(!!n) $ 1:minc(1:minc(1:minc(1:minc(1:minc(1:minc(1:minc(1:minc(1:minc...
(!!n-1) $ minc(1:minc(1:minc(1:minc(1:minc(1:minc(1:minc(1:minc(1:minc...
(!!n-1) $ (1+1):minc(minc(1:minc(1:minc(1:minc(1:minc(1:minc(1:minc(1:minc...
            -- note that `(1+1)` isn't actually calculated!
(!!n-2) $ minc(minc(1:minc(1:minc(1:minc(1:minc(1:minc(1:minc(1:minc...
(!!n-2) $ ((1+1)+1):minc(minc(minc(1:minc(1:minc(1:minc(1:minc(1:minc(1:minc...
            -- again, neither of the additions is actually calculated.
(!!n-3) $ minc(minc(minc(1:minc(1:minc(1:minc(1:minc(1:minc(1:minc...
(!!n-3) $ ((...)+1):minc(minc(minc(minc(1:minc(1:minc(1:minc(1:minc(1:minc...
...
(!!n-n) $ ((...+1)+1) : minc(minc(...minc(minc(1:minc(...
           ╰─ n ─╯
(!!0) $ (n+1) : _
n+1

N 每增加一个固定数量的两个步骤,加上 N 一旦达到索引,它们仍会增加 - 这仍然是总而言之, O N )。

这里至关重要的是,map基本上只对整个列表应用一次。它完全是懒惰的,即产生_:_ thunk它只需要知道列表至少 length 1,但实际的元素并不重要所有

这样,我们minc(minc(...(minc(1 : ...所写的内容仅在一步中被(... + 1) : minc(...取代。


[1] 事实证明,即使我们总和第一个 N 数字,它也在< EM> 0 的(名词的)。我不知道怎么做。