我想生成长度为n的所有可能的二进制序列作为列表。我想出了两种方法。我更喜欢第二种,因为它似乎更容易阅读,但我不知道为什么它比第一种慢得多。
bin_seqs_1 :: Int -> [[Int]]
bin_seqs_1 n = iterate add_bit [[]] !! n
where add_bit seqs = [b : seq | seq <- seqs, b <- [-1,1]]
bin_seqs_2 :: Int -> [[Int]]
bin_seqs_2 n = sequence $ replicate n [-1,1]
main :: IO ()
main = putStrLn $ show $ bin_seqs_2 23
当我使用优化进行编译并使用bin_seqs_1运行(将输出重定向到/ dev / null)时,需要13秒。使用bin_seqs_2,需要29秒。
为什么第二种方法这么慢?
编辑: 所以它与sequence的实现有关。如果我重新定义序列以首先评估列表的尾部,那么头部,性能可与bin_seqs_1相比
bin_seqs_3 n = sequence $ replicate n [-1,1]
where sequence ms = foldr k (return []) ms
k m m' = do { xs <- m'; x <- m; return (x:xs) }
但我仍然不明白为什么,在“k”函数中,评估xs然后x比评估x然后xs快得多(就像在Control.Monad中那样)。
答案 0 :(得分:1)
这是部分答案“为什么第二种方法这么慢?”
当对列表融合的性能有疑问时,请使用coredump,Luke!这很容易。
给定test.hs
module Test (_1_bin_seqs, _2_bin_seqs) where
_1_bin_seqs :: Int -> [[Int]]
_1_bin_seqs n = iterate add_bit [[]] !! n
where add_bit seqs = [b : seq | seq <- seqs, b <- [-1,1]]
_2_bin_seqs :: Int -> [[Int]]
_2_bin_seqs n = sequence $ replicate n [-1,1]
你必须运行
$ ghc test.hs -fforce-recomp -O2 -ddump-simpl -dsuppress-all
-fforce-recomp
- 始终重建-O2
- 首选优化级别-ddump-simpl
- 将核心代码输出到stdout -dsuppress-all
- 让人类可读的输出-ddump-rule-rewrites
- (可选)显示使用的代码重写规则评论模块导入可以控制coredump的功能。您会看到GHC将第一个版本转换为iterate
+循环,而第二个版本并不那么甜。