Haskell-单子转换器-限制解释器中的求值次数
我正在学习Monad Transformers,并决定为一种简单的语言(带有循环结构)编写一个解释器,类似于使用Monad Transformers的Brainfuck。我想在一定数量的陈述之后终止口译员Haskell-单子转换器-限制解释器中的求值次数,haskell,monad-transformers,state-monad,Haskell,Monad Transformers,State Monad,我正在学习Monad Transformers,并决定为一种简单的语言(带有循环结构)编写一个解释器,类似于使用Monad Transformers的Brainfuck。我想在一定数量的陈述之后终止口译员 module Transformers where import qualified Data.Map as Map import Data.Maybe import Control.Monad.State.L
module Transformers where
import qualified Data.Map as Map
import Data.Maybe
import Control.Monad.State.Lazy
import Control.Monad.Writer.Lazy
import Control.Monad.Except
data Term = Input
| Output
| Increment
| Decrement
| Loop [Term]
deriving (Show)
data World = World {
inp :: [Int],
out :: [Int],
mem :: Int
} deriving Show
op_limit = 5
loop
:: [Term]
-> StateT World (WriterT (Sum Int) (Except World)) ()
-> StateT World (WriterT (Sum Int) (Except World)) ()
loop terms sp = sp >> do
s <- get
if mem s == 0 then put s else loop terms (foldM (\_ t -> eval t) () terms)
limit :: StateT World (WriterT (Sum Int) (Except World)) ()
limit = do
(s, count) <- listen get
when (count >= op_limit) $ throwError s
tick :: StateT World (WriterT (Sum Int) (Except World)) ()
tick = tell 1
eval :: Term -> StateT World (WriterT (Sum Int) (Except World)) ()
eval Input =
limit >> tick >> modify (\s -> s { inp = tail (inp s), mem = head (inp s) })
eval Output = limit >> tick >> modify (\s -> s { out = mem s : out s })
eval Increment = limit >> tick >> modify (\s -> s { mem = mem s + 1 })
eval Decrement = limit >> tick >> modify (\s -> s { mem = mem s - 1 })
eval (Loop terms) = loop terms (void get)
type Instructions = [Term]
interp :: Instructions -> World -> Either World (World, Sum Int)
interp insts w =
let sp = foldM (\_ inst -> eval inst) () insts
in runExcept (runWriterT (execStateT sp w))
这种简单的语言是由单个存储单元组成的,能够容纳一个Int和5条指令输入、输出、递增、递减和循环。当内存中的值为零时,循环终止。输入从一个列表中读取,同样地,输出被写入另一个列表。增量和减量对应地对内存执行+1和-1操作
我使用World
type来跟踪输入、输出(流)和内存,Sum Int
来计算计算的指令数<代码>除世界之外在某些语句后终止计算
module Transformers where
import qualified Data.Map as Map
import Data.Maybe
import Control.Monad.State.Lazy
import Control.Monad.Writer.Lazy
import Control.Monad.Except
data Term = Input
| Output
| Increment
| Decrement
| Loop [Term]
deriving (Show)
data World = World {
inp :: [Int],
out :: [Int],
mem :: Int
} deriving Show
op_limit = 5
loop
:: [Term]
-> StateT World (WriterT (Sum Int) (Except World)) ()
-> StateT World (WriterT (Sum Int) (Except World)) ()
loop terms sp = sp >> do
s <- get
if mem s == 0 then put s else loop terms (foldM (\_ t -> eval t) () terms)
limit :: StateT World (WriterT (Sum Int) (Except World)) ()
limit = do
(s, count) <- listen get
when (count >= op_limit) $ throwError s
tick :: StateT World (WriterT (Sum Int) (Except World)) ()
tick = tell 1
eval :: Term -> StateT World (WriterT (Sum Int) (Except World)) ()
eval Input =
limit >> tick >> modify (\s -> s { inp = tail (inp s), mem = head (inp s) })
eval Output = limit >> tick >> modify (\s -> s { out = mem s : out s })
eval Increment = limit >> tick >> modify (\s -> s { mem = mem s + 1 })
eval Decrement = limit >> tick >> modify (\s -> s { mem = mem s - 1 })
eval (Loop terms) = loop terms (void get)
type Instructions = [Term]
interp :: Instructions -> World -> Either World (World, Sum Int)
interp insts w =
let sp = foldM (\_ inst -> eval inst) () insts
in runExcept (runWriterT (execStateT sp w))
monad
限制基于计数,应决定在当前状态下失败或不执行任何操作。但是我注意到Writer
monad中的count
计算无法访问自己的累加器。更重要的是:在计算运行时,累加器永远不会被强制,即使是WHNF也不会。这适用于Writer
的严格变量和惰性变量-严格变量在某种意义上与累加器无关。如果计算运行时间过长,累加器中不可避免的惰性可能会导致空间泄漏
您的limit
函数没有在“mainline”WriterT
累加器的值上分支。get
操作(您正在使用mtl)只是从StateT
层读取状态,在其他层中不执行任何效果:它将mempty
添加到其WriterT
累加器中,并且不会抛出错误
然后,listen
提取get
操作的Writer
累加器(仅是get
的累加器,而不是整个计算的累加器),并将其添加到“主线”累加器中。但是这个提取的值(元组中返回的值)将始终是mempty
,也就是说,Sum 0
如@chi所述,您可以将计数器置于StateT
状态,而不是WriterT
。您还可以使用,这与WriterT
非常类似,但允许您检查累加器(还允许您使用bang模式将累加器强制到WHNF)
AccumT
似乎没有相应的mtl typeclass,因此您需要喷洒一些升降机才能使用它 我只是简单地看了一下,但似乎您想阅读您的“语句数”,它目前是由编写器monad建模的。如果是这样,不要使用writer monad,因为你需要阅读。将计数器置于世界状态,然后删除writer层。