Haskell递归缓慢,有什么陷阱? [英] Haskell recursion slow, what's the pitfall?

查看:43
本文介绍了Haskell递归缓慢,有什么陷阱?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是Haskell的初学者

I'm very much a beginner in 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天)的解决方案.该代码可产生正确的结果,但速度慢得令人发指.

The above piece of code is part of a recursion which I omitted. The addRecipes is called again and again in a recursive call. This is a solution to an advent of code problem (AoC 2018 day 14). The code produces the correct result but is cripplingly slow.

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

I'm just trying to learn where the problem lies, what is horribly inefficient in the code above?

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

I've tried optimizing away the ++-operator and replace it with a : and combining things with the digits call. I know the ++ is slower than the : but is that really it? (Remember, i'm learning Haskell so I want to fix this code if possible and know why I can't write it like this)

食谱"中的列表会增加并变大

The list in Recipes grows and becomes large

推荐答案

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

I followed the advice posted in the comments; Do not use a the list data structure when you make a lot of random accesses. So I replaced the list with with Sequence 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个小时到几秒钟).谢谢评论者!

It worked and the run time is now reasonable (from 1 hour to seconds). Thanks commenters!

这篇关于Haskell递归缓慢,有什么陷阱?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆