试图掌握我试图使用WriterT和State(它的advent of code day 15)来解决Haskell练习的概念。由于某些原因,我不明白我最终使用了大量的内存而我的笔记本电脑(只有4G Ram)就停止了。
我的第一个想法是使用严格并洒上刘海 - 但问题仍然存在。
有人能解释我哪里出错了吗?
此处已清理代码:
{-# LANGUAGE BangPatterns #-}
module Main where
import Control.Monad.State.Strict
import Control.Monad.Writer.Strict
main = do
let generators = (Generator 65 16807, Generator 8921 48271)
res1 = compute generators (4*10^7)
putStrLn "Answer 1"
print res1
data Generator = Generator { _value :: Int
, _factor :: Int
}
deriving Show
newtype Value = Value Int
deriving (Show, Eq)
newtype Counter = Counter Int
deriving (Show, Eq)
instance Monoid Counter where
mempty = Counter 0
mappend (Counter !a) (Counter !b) = Counter (a+b)
generate :: Generator -> (Value, Generator)
generate (Generator v f) = (Value newval, Generator newval f)
where newval = (v * f) `mod` 2147483647
agree (Value a) (Value b) = (a `mod` mf) == (b `mod` mf)
where mf = 2^16
oneComp :: State (Generator, Generator) Bool
oneComp = do
(!ga, !gb) <- get
let (va, gan) = generate ga
(vb, gbn) = generate gb
!ag = agree va vb
put (gan, gbn)
pure ag
counterStep :: WriterT Counter (State (Generator, Generator)) ()
counterStep = do
!ag <- lift oneComp
when ag $ tell (Counter 1)
afterN :: Int -> WriterT Counter (State (Generator, Generator)) ()
afterN n = replicateM_ n counterStep
compute s0 n = evalState (execWriterT (afterN n)) s0
我用堆栈编译它。 cabal文件中的条目是:
executable day15
hs-source-dirs: app
main-is: day15.hs
ghc-options: -threaded -rtsopts -with-rtsopts=-N
build-depends: base
, advent
, hspec
, mtl
default-language: Haskell2010
更新
我有更多的时间,并遵循建议使发电机严格。然而,仍然有一些东西使用太多的记忆。
这里是我认为可能相关的教程文件的一部分。
Fri Dec 15 16:28 2017 Time and Allocation Profiling Report (Final)
day15 +RTS -N -p -RTS
total time = 71.66 secs (71662 ticks @ 1000 us, 1 processor)
total alloc = 17,600,423,088 bytes (excludes profiling overheads)
COST CENTRE MODULE SRC %time %alloc
afterN Main app/day15.hs:79:1-36 41.1 20.0
mappend Main app/day15.hs:51:3-51 31.0 3.6
oneComp Main app/day15.hs:(64,1)-(71,9) 9.2 49.1
generate.(...) Main app/day15.hs:55:9-42 8.5 14.5
答案 0 :(得分:1)
原因可能是WriterT
图层。
即使是&#34; strict&#34; WriterT
在累加器中完全是懒惰的 - 在另一种意义上,它与累加器无关。
例如,此程序运行时没有错误:
import Data.Monoid
import Control.Monad.Trans.Writer
import Control.Exception
main :: IO ()
main = do
let (x,_) = runWriter $ do
tell $ Sum (1::Float)
tell (throw $ AssertionFailed "oops")
tell (error "oops")
tell undefined
tell (let z = z in z)
return True
print x
此外,不可能&#34;严格&#34; <{1}}内的累加器,因为没有办法达到它。
对于长时间的计算,thunk会累积并占用大量内存。
一种解决方案是将计数器存储在WriterT
层中。严格的modify'
功能在这里很有用。
使用StateT
作为仅附加累加器虽然有点令人不满意。另一个选择是使用Accum
明智地定位StateT
。该程序抛出错误:
BangPatterns
import Control.Monad.Trans.Accum
main :: IO ()
main = do
let (x,_) = flip runAccum mempty $ do
add $ Sum (1::Float)
add $ error "oops"
!_ <- look
return True
print x
就像Accum
一样,可以让您访问累加器。它不会让你随意改变它,只会添加它。但掌握它足以引入严格性。