【发布时间】:2018-12-16 12:22:59
【问题描述】:
我是 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 天)。该代码产生了正确的结果,但速度非常慢。
我只是想知道问题出在哪里,上面的代码中什么地方效率极低?
我已经尝试优化 ++-operator 并将其替换为 : 并将其与数字调用相结合。我知道++ 比: 慢,但真的是这样吗? (记住,我正在学习 Haskell,所以我想尽可能地修复这段代码,并知道为什么我不能这样写)
编辑:Recipes 中的列表越来越大
【问题讨论】:
-
如果你需要大量的随机访问,比如
list !! n,或者附加list ++ something,那么基本列表不是一个合适的数据结构。如果不了解总体情况,很难建议使用什么。也许一个数组就足够了,或者需要Seq/Vector。也许你可以不用随机访问,然后列表就可以了。 -
嗯,好的,adventofcode.com/2018/day/14 如果有兴趣,这里有谜题......但这可能会有所帮助,我会调查
!!,也许我可以以某种方式将其删除 -
完成这个问题后,我意识到所有的工作都在序列的末尾完成,所以如果你把它保持在相反的顺序,你可能会避免只使用一个列表。 (并跟踪长度。)但很容易出错,使用
Seq会简单得多。 -
只是为了扩展@DavidFletcher的评论,
Seq指的是hackage.haskell.org/package/containers-0.6.0.1/docs/… -
它与 Seq 一起工作! 1 小时 -> 2 秒。谢谢,但是我得到:“getMBlocks:VirtualAlloc MEM_COMMIT 失败:分页文件太小,无法完成此操作。”在我的递归结束条件上:
Seq.take 6 (Seq.reverse seq) == part2Input = Seq.length seq - 5。我会自己回答,但是如果您对最后一个障碍有指示,我会接受! :)
标签: haskell optimization