【发布时间】:2012-05-20 13:37:20
【问题描述】:
我正在用 Haskell 编写一个脑残的解释器,我想出了一个我认为非常有趣的程序描述:
data Program m = Instruction (m ()) (Program m)
| Control (m (Program m))
| Halt
但是,将脑残程序的文本表示解析为这种数据类型是很棘手的。尝试正确解析方括号时会出现问题,因为需要打结,以便循环内的最终Instruction 再次链接到循环的Control。
更多初步信息。详情请见this version on the github repo。
type TapeM = StateT Tape IO
type TapeP = Program TapeM
type TapeC = Cont TapeP
branch :: Monad m => m Bool -> Program m -> Program m -> Program m
branch cond trueBranch falseBranch =
Control ((\b -> if b then trueBranch else falseBranch) `liftM` cond)
loopControl :: TapeP -> TapeP -> TapeP
loopControl = branch (not <$> is0)
这是我尝试过的:
toProgram :: String -> TapeP
toProgram = (`runCont` id) . toProgramStep
liftI :: TapeM () -> String -> TapeC TapeP
liftI i cs = Instruction i <$> toProgramStep cs
toProgramStep :: String -> TapeC TapeP
toProgramStep ('>':cs) = liftI right cs
-- similarly for other instructions
toProgramStep ('[':cs) = push (toProgramStep cs)
toProgramStep (']':cs) = pop (toProgramStep cs)
push :: TapeC TapeP -> TapeC TapeP
push mcontinue = do
continue <- mcontinue
cont (\breakMake -> loopControl continue (breakMake continue))
pop :: TapeC TapeP -> TapeC TapeP
pop mbreak = do
break <- mbreak
cont (\continueMake -> loopControl (continueMake break) break)
我想我可以以某种方式使用延续将信息从 '[' 案例传递到 ']' 案例,反之亦然,但我对 Cont 没有足够的把握,除了收集疯狂的猜测之外,我还没有真正做任何事情一些看起来可能有效的东西,如上面push 和pop 所示。这编译并运行,但结果是垃圾。
Cont 可以在这种情况下适当地打结吗?如果不是,那我应该使用什么技术来实现toProgram?
注意 1:我之前遇到了一个微妙的逻辑错误:loopControl = branch is0 的布尔值颠倒了。
注意 2:我设法使用 MonadFix(如 jberryman 建议)和 State 来提出解决方案(请参阅 the current state of the github repository)。我仍然想知道如何使用Cont 来代替。
注 3:我的 Racketeer 导师为我整理了 a similar Racket program(查看所有修订)。可以使用Cont 将他的管道/管道输出技术翻译成 Haskell 吗?
tl;dr 我设法使用 MonadFix 做到了这一点,而其他人设法使用 Racket 的延续组合器做到了这一点。我很确定这可以通过 Haskell 中的Cont 来完成。你能告诉我怎么做吗?
【问题讨论】:
-
使用 Sentinel 捕捉未来过去几天的突变体。
-
说真的,你还需要使用
Cont吗?难道你不能只计算要跳转的指令数,也许做一个多遍解析,其中额外的遍将要跳转的指令数(连同方向)与'['和']'字符相关联?也就是说,[Char] -> [JumpInfo Char]然后在结果上使用你的解析器,其中data JumpInfo = JumpInfo Char (Maybe Integer)。 -
或者,您可以保持数据不变(或者大多数情况下,没有投入太多脑力),仍然使用 1-pass 解析器,并通过在运行时,您可以在其中反复移动磁带,直到到达目的地。虽然这会奏效,但这将是一个缓慢的跳跃。
-
@trinithis 当然,但我对了解
Cont的兴趣远大于我对实现这个特定解释器的兴趣。 -
如果在遇到
[之前遇到]会发生什么?
标签: haskell monads continuations tying-the-knot monadfix