垃圾收集列表,同时对其执行IO操作

Chr*_*isR 6 garbage-collection haskell lazy-evaluation

我想在Haskell中编写一个共轭梯度求解器,并希望使用惰性列表来解除停止规则和迭代中的信息输出.我的代码基本上是这样的:

data CGState = CGState { cgx :: Image
                       , cgp :: Image
                       , cgr :: Image
                       , cgr2 :: Double
                       }

cg :: Operator -> Image -> [CGState]
cg = [...]

runCG :: (CGState -> Bool) -> Operator -> Image -> IO Image
runCG stoprule op rhs = do
  let steps = takeWhile (not . stoprule) $ cg op rhs
  fmap last $ forM (zip [(1::Int)..] steps) $ \(n, cgs) -> do
      putStrLn $ show n ++ "  " ++ show (sqrt $ cgr2 cgs)
      return $ cgx cgs
Run Code Online (Sandbox Code Playgroud)

我们的想法是迭代列表,输出一些信息,但只保留最后一次迭代.但是,在运行此代码时,它似乎不会垃圾收集前面的迭代.我的猜测是这与IO连接:如果我重写代码就好了

runCG :: (CGState -> Bool) -> Operator -> Image -> IO Image
runCG stoprule op rhs = do
  let steps = takeWhile (not . stoprule) $ cg op rhs
  return $ cgx $ last steps
Run Code Online (Sandbox Code Playgroud)

问题不会发生,即除最终迭代之外的所有内容都会直接收集垃圾.

如何能够输出有关迭代的一些信息,我怎么能达到同样的效果?

Ørj*_*sen 6

对,我觉得这个问题是fmapIO:由于IO操作是在严格的顺序总是执行中,fmap只有应用last 后,从整个结果列表中forM已建成.

相反,你可以使用Control.Monad.foldM,它在列表上单独折叠(未经测试):

runCG stoprule op rhs = do
  let steps = takeWhile (not . stoprule) $ cg op rhs
  foldM (\ _ (n, cgs) -> do
      putStrLn $ show n ++ "  " ++ show (sqrt $ cgr2 cgs)
      return $ cgx cgs)
    undefined -- initial value for fold is ignored as long as steps is nonempty
    (zip [(1::Int)..] steps)
Run Code Online (Sandbox Code Playgroud)