5

プリミティブ プログラムを評価するコードがいくつかあります。プログラムは、ステートメント (式、ブロック、return ステートメント) のリストです。評価の結果は、最後に評価された式です。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

問題は、このコードを継続モナドでどのように書き直すことができるかということです。および関数で明示的に渡されたNextStepコールバックを取り除きたいです。出来ますか?evalStmtevalBlock

4

1 に答える 1

7

翻訳はかなり機械的です。

継続モナドでは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持っている値を返すだけです。

于 2014-05-11T15:36:04.323 に答える