2017-12-15 19 views
0

私はWriterTとState(それはadvent of code day 15です)を使ってHaskellの練習問題を解決しようとしている概念を理解しようとしています。何らかの理由で、私はメモリの負荷を使用して終了し、私のノートブック(ちょうど4Gラム)が停止に終わることを理解していない。WriterT Stateになぜメモリが必要なのですか?

私の最初のアイデアは、厳密さを使い、周りに振りかけることでしたが、問題は解決しません。

誰かが私がどこに間違っていたのか説明できますか?ここで

は、コードをクリーンアップしています:

{-# 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 

私はスタックでそれをコンパイルします。徒党ファイルのエントリは次のとおりです。

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 

更新

私はもう少し時間があったし、発電機は厳密にする提案を行いました。しかし、まだ何かがあまりにも多くのメモリを使用しています。

これは私が関連しているかもしれないと思うprofファイルの部分です。

  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 
+1

学習するために明示的に 'WriterT'と' State'を使用しているのであれば意味がありますが、その特定のパズルは 'unfoldr'と' zip'で解決できます。 FWIW、ここに私の解決策があります:https://github.com/ploeh/advent-of-code-2017/blob/master/Day15/Solution.hs –

+1

私がそれ以上のことを知らなかったら、作者のモナドに関するものではなく、 '生成する'ものである。しかし、本当に、私はあなたが実際にスペースを取っている場所を見つけるために実際にプロファイルする必要があると思います。 – MathematicalOrchid

+0

@MathematicalOrchidあなたは正しいことが重く表示されます。それを軽くするための提案はありますか? – bdecaf

答えて

1

原因はおそらくWriterTです。

「厳密」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 

WriterT内からアキュムレータを「strictify」することは不可能です。

長い計算では、サンクは蓄積され、多くのメモリを消費します。

代わりに、StateTレイヤーにカウンタを格納する方法があります。厳密なmodify'関数はここで役立ちます。追加専用アキュムレータためStateTを使用


は、しかし少し不十分です。別のオプションは、Accumを慎重に配置して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あなたは、アキュムレータにアクセスすることができますWriterのようなものです。それは自由に変更することはできません。追加するだけです。しかし、それを保持することは、厳密さを導入するのに十分です。

関連する問題