Haskell递归缓慢,有什么陷阱?

时间:2018-12-16 12:22:59

标签: haskell optimization

我是Haskell的初学者

data Recipes = Recipes Int Int Int [Int] deriving(Show) 

addRecipes :: Recipes -> Recipes
addRecipes (Recipes t e1 e2 list) = 
    let (e1c, e2c) = (list !! e1, list !! e2)
        newVal = e1c + e2c
        newList = list ++ (digits $ newVal)
        e1n = calcNewPos e1 e1c newList
        e2n = calcNewPos e2 e2c newList
    in Recipes t e1n e2n newList

calcNewPos :: Int -> Int -> [Int] -> Int    
calcNewPos i i2 list = (i + i2 + 1) `mod` (length list)

-- Borrowed:
-- https://stackoverflow.com/questions/3963269/split-a-number-into-its-digits-with-haskell
digits :: Int -> [Int]
digits = map (read . (:[])) . show

以上代码是我省略的递归的一部分。 addRecipes在递归调用中被反复调用。这是解决代码问题(AoC 2018第14天)的解决方案。该代码可产生正确的结果,但速度非常慢。

我只是想了解问题出在哪里,在上面的代码中什么效率低下?

我尝试优化++运算符,并用:代替它,并将其与数字调用结合起来。我知道++:慢,但这是真的吗? (请记住,我正在学习Haskell,所以我想尽可能修复此代码,并知道为什么我不能这样写)

编辑:“食谱”中的列表会增加并变大

1 个答案:

答案 0 :(得分:1)

我遵循了评论中发布的建议;进行大量随机访问时,请勿使用列表数据结构。所以我用序列http://hackage.haskell.org/package/containers-0.6.0.1/docs/Data-Sequence.html

替换了列表
import qualified Data.Sequence as Seq

data Recipes = Recipes Int Int Int (Seq.Seq Int) deriving(Show) 

addRecipes :: Recipes -> Recipes
addRecipes (Recipes t e1 e2 seq) = 
let (e1c, e2c) = (Seq.index seq e1, Seq.index seq e2)
    newVal = e1c + e2c
    newSeq = (Seq.><) seq (Seq.fromList (digits newVal))
    e1n = calcNewPos e1 e1c newSeq
    e2n = calcNewPos e2 e2c newSeq
in Recipes t e1n e2n newSeq

calcNewPos :: Int -> Int -> (Seq.Seq Int) -> Int    
calcNewPos i i2 seq = (i + i2 + 1) `mod` (Seq.length seq)

它可以正常工作,并且运行时间现在合理(从1小时到几秒钟)。谢谢评论者!