【问题标题】:Rewriting code with continuations用延续重写代码
【发布时间】:2014-06-28 22:26:39
【问题描述】:

我有一些评估原始程序的代码。程序是一个语句列表(表达式、块、返回语句)。评估结果是最后评估的表达式。评估者还应正确对待return 语句(即在第一次出现return 后停止评估)。

为了实现这个逻辑,我传递了特殊的回调函数 (NextStep),它在当前语句之后进行下一步评估。处理return语句时不调用下一步:

data Statement = 
      Expr Int
    | Block [Statement]
    | Return Int
    deriving (Show, Eq)

data Value = 
      Undefined
    | Value Int
    deriving (Show, Eq)

type NextStep = Value -> Value

evalStmt :: Statement -> NextStep -> Value
evalStmt (Expr val) next = 
    let res = Value val
    in next res
evalStmt (Block stmts) next = evalBlock stmts next
evalStmt (Return val) next = Value val

evalBlock :: [Statement] -> NextStep -> Value
evalBlock [] next = next Undefined
evalBlock [st] next = evalStmt st next
evalBlock (st:rest) next = evalStmt st $ \ _ -> evalBlock rest next

evalProgram stmts = evalBlock stmts id

prog1 = [Expr 1, Block [Return 3, Expr 2], Expr 4] 
evalProg1 = evalProgram prog1 -- result will be Value 3

问题是如何用 continuation monad 重写这段代码?我想摆脱在evalStmtevalBlock 函数中显式传递的NextStep 回调。有可能吗?

【问题讨论】:

    标签: haskell continuations continuation-passing


    【解决方案1】:

    翻译相当机械。

    请记住,在延续单子中,return 将值提供给延续。

    evalStmt :: Statement -> Cont Value Value
    evalStmt (Expr val) = 
        let res = Value val
        in return res
    evalStmt (Block stmts) = evalBlock stmts
    evalStmt (Return val) = cont $ \_ -> Value val
    
    evalBlock :: [Statement] -> Cont Value Value
    evalBlock [] = return Undefined
    evalBlock [st] = evalStmt st
    evalBlock (st:rest) = evalStmt st >> evalBlock rest
    
    evalProgram :: [Statement] -> Value
    evalProgram stmts = runCont (evalBlock stmts) id
    

    为了模拟提前返回,我们只是忽略给Return val 的延续,只返回我们拥有的值。

    【讨论】:

    • Cont 的 monad 实例被定义为以这种方式链接,因此 应该 简单地等同于 evalBlock (st:rest) = evalStmt st >> evalBlock rest
    猜你喜欢
    • 1970-01-01
    • 2021-07-19
    • 2018-06-03
    • 2010-10-19
    • 2010-10-22
    • 2014-08-20
    • 2018-07-11
    • 2017-08-24
    • 1970-01-01
    相关资源
    最近更新 更多