Dra*_*gno 0 continuations haskell monad-transformers
我在Haskell中编写了以下代码:
import Data.IORef
import Control.Monad
import Control.Monad.Trans.Cont
import Control.Monad.IO.Class
fac n = do
i<-newIORef 1
f<-newIORef 1
replicateM_ n $ do
ri<-readIORef i
modifyIORef f (\x->x*ri)
modifyIORef i (+1)
readIORef f
Run Code Online (Sandbox Code Playgroud)
这是非常好的代码,它将factorial实现为命令式函数.但是replicateM_无法完全模拟真实for循环的使用.所以我尝试使用continuation创建一些东西,但我在这里失败的是我的代码:
ff = (`runContT` id) $ do
callCC $ \exit1 -> do
liftIO $ do
i<-newIORef 1
f<-newIORef 1
callCC $ \exit2 -> do
liftIO $ do
ri<-readIORef i
modifyIORef (\x->x*ri)
modifyIORef i (+1)
rri<-readIORef i
when (rri<=n) $ exit2(())
liftIO $ do
rf<-readIORef f
return rf
Run Code Online (Sandbox Code Playgroud)
你能帮我纠正我的代码吗?谢谢
既然你是Haskell的初学者,而不仅仅是为了学习延续和IORefs是如何工作的,那么你做错了.
编写命令循环的Haskell-y方式是尾调用或折叠.
factorial n = foldl1' (*) [1..n]
factorial' n = go 1 n
where go accum 0 = accum
go accum n = go (n-1) (accum * n)
Run Code Online (Sandbox Code Playgroud)
此外,因为Haskell callCC本质上为您提供了早期回报,使用它来模拟循环是行不通的.
callCC (\c -> ???)
Run Code Online (Sandbox Code Playgroud)
想想我们???为了循环而必须投入的东西.不知何故,我们想要callCC再次运行,如果它返回一定的值,否则只是继续我们的快乐方式.
但我们投入的任何东西都???无法callCC再次运行!无论我们做什么,它都会返回一个值.所以相反,我们需要做一些事情callCC
let (continue, val) = callCC (someFunc val)
in if continue
then callCallCCAgain val
else val
Run Code Online (Sandbox Code Playgroud)
这样的事情对吗?但等等,callCallCCAgain是递归!这甚至是尾递归!事实上,这对callCC任何人都没有好处
loop val = let (continue, val') = doBody val
in if continue
then loop val'
else val'
Run Code Online (Sandbox Code Playgroud)
看起来熟悉?这与factorial'上面的结构相同.
你仍然可以使用IORefs和monad-loops包这样的东西,但它总是一场艰苦的战斗,因为Haskell并不是那样写的.
如果要在haskell中直接执行"循环",请使用尾递归.但实际上,尽量使用组合程序像fold和map,他们就像小专业化环路和GHC是在优化他们太棒了.并且绝对不要使用IORefs,试图对Haskell进行编程就好像C会损害你的性能,可读性,每个人都会感到难过.