优化Haskell中的“列表”索引

时间:2014-01-13 22:55:39

标签: algorithm haskell optimization

假设您有一个非常确定的算法来生成列表,例如inits中的Data.List。有没有办法让Haskell编译器能够最优地对该算法执行“索引”操作而不实际生成所有中间结果?

例如,inits [1..] !! 10000非常慢。编译器能否以某种方式推断出inits会在第10000个元素上产生什么而不进行任何递归等等?当然,同样的想法可以超越列表。

编辑:虽然inits [1..] !! 10000是常量,但我想知道某些算法上的任何“类索引”操作。例如,可以对\i -> inits [1..] !! i进行优化,以便不会执行[或最小]递归来达到任何i的结果吗?

1 个答案:

答案 0 :(得分:7)

是和否。如果你看一下Data.List.inits的定义:

inits                   :: [a] -> [[a]]
inits xs                =  [] : case xs of
                                  []      -> []
                                  x : xs' -> map (x :) (inits xs')

你会看到它是递归定义的。这意味着结果列表的每个元素都构建在列表的前一个元素上。因此,如果你想要任何第n个元素,你必须构建所有n-1个前面的元素。

现在你可以定义一个新功能

inits' xs = [] : [take n xs | (n, _) <- zip [1..] xs]

具有相同的行为。如果您尝试使用inits' [1..] !! 10000,它会很快完成,因为列表的连续元素不依赖于先前的元素。当然,如果你实际上是在尝试生成一个inits列表而不是一个单独的元素,那么这将会慢得多。

编译器必须知道很多信息才能从inits这样的函数中优化掉递归。也就是说,如果一个函数确实是“非常确定的”,那么以非递归的方式重写它应该是微不足道的。