假设我想要列出列表中的所有元素,但不包括第一个负数,并返回数字和列表的其余部分。这样做的简单方法是
addPos l = s `seq` (s,back) where (front, back) = span (>= 0) l s = sum front
seq
应该确保没有人通过在总和之前强制退回来意外地建立一个巨大的thunk。
答案 0 :(得分:5)
当我们谈论编译器优化中间列表时,我们通常会谈论在GHC的RULES
编译指示中实现的“融合”;你可以了解它是如何工作的,哪些列表函数是“好消费者”和“生产者”here。
不幸的是,span
看起来不像是“好的制作人”。您可以通过询问GHC的核心输出并获取使用ghc -O2 -ddump-simpl -dsuppress-module-prefixes -dsuppress-uniques -ddump-core-stats -ddump-inlinings -ddump-rule-firings test.hs
这是一个清理过的输出:
Rule fired: Class op >=
Rule fired: SPEC Data.List.sum
Inlining done: geInt{v r3n} [gid]
Inlining done: sum_sum1{v rkV} [gid]
Inlining done: span{v r1Q} [gid]
Inlining done: sum_sum'1{v rl6} [gid]
==================== Tidy Core ====================
Result size of Tidy Core = {terms: 24, types: 27, coercions: 0}
addPos1 :: Int -> Bool
addPos1 = \ (ds :: Int) -> case ds of _ { I# x -> >=# x 0 }
addPos [InlPrag=INLINE[0]] :: [Int] -> (Int, [Int])
addPos =
\ (w :: [Int]) ->
case $wspan @ Int addPos1 w of _ { (# ww1, ww2 #) ->
case $wsum' ww1 0 of ww3 { __DEFAULT -> (I# ww3, ww2) }
}
您可以看到我们称之为某种重写/专门span
,然后是sum
。
您可能会看到vector
库是否可以融合它们,或者看看性能如何比较有趣。