- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试使用 CPS 来简化我的 Python 解释器中的控制流实现。具体来说,当实现 return
/break
/continue
,我必须手动存储状态和展开,这很乏味。我读过以这种方式实现异常处理非常棘手。我想要的是每个 eval
函数能够将控制流引导到下一条指令或完全不同的指令。
一些比我更有经验的人建议研究 CPS 作为正确处理此问题的一种方式。我真的很喜欢它如何简化解释器中的控制流,但我不确定我需要做多少才能实现这一点。
Cont
monad 适合这里,因为宿主语言是 Haskell。
data Statement
= Assignment Expression Expression
| Expression Expression
| Break
| While Expression [Statement]
data Expression
| Attribute Expression String
| Constant Value
data Value
= String String
| Int Integer
| None
eval
:
eval (Assignment (Variable var) expr) = do
value <- evalExpr expr
updateSymbol var value
eval (Expression e) = do
_ <- evalExpr e
return ()
evalExpr
:
evalExpr (Attribute target name) = do
receiver <- evalExpr target
attribute <- getAttr name receiver
case attribute of
Just v -> return v
Nothing -> fail $ "No attribute " ++ name
evalExpr (Constant c) = return c
eval (Break) = do
env <- get
when (loopLevel env <= 0) (fail "Can only break in a loop!")
put env { flow = Breaking }
eval (While condition block) = do
setup
loop
cleanup
where
setup = do
env <- get
let level = loopLevel env
put env { loopLevel = level + 1 }
loop = do
env <- get
result <- evalExpr condition
when (isTruthy result && flow env == Next) $ do
evalBlock block
-- Pretty ugly! Eat continue.
updatedEnv <- get
when (flow updatedEnv == Continuing) $ put updatedEnv { flow = Next }
loop
cleanup = do
env <- get
let level = loopLevel env
put env { loopLevel = level - 1 }
case flow env of
Breaking -> put env { flow = Next }
Continuing -> put env { flow = Next }
_ -> return ()
最佳答案
这终于给了我一个尝试使用ContT
的好借口!
这是执行此操作的一种可能方法:存储(在 Reader
中包裹在 ContT
中)继续退出当前(最内层)循环:
newtype M r a = M{ unM :: ContT r (ReaderT (M r ()) (StateT (Map Id Value) IO)) a }
deriving ( Functor, Applicative, Monad
, MonadReader (M r ()), MonadCont, MonadState (Map Id Value)
, MonadIO
)
runM :: M a a -> IO a
runM m = evalStateT (runReaderT (runContT (unM m) return) (error "not in a loop")) M.empty
withBreakHere :: M r () -> M r ()
withBreakHere act = callCC $ \break -> local (const $ break ()) act
break :: M r ()
break = join ask
IO
以便在我的玩具解释器中轻松打印,并添加
State (Map Id Value)
用于变量)。
Break
和
While
作为:
eval Break = break
eval (While condition block) = withBreakHere $ fix $ \loop -> do
result <- evalExpr condition
unless (isTruthy result)
break
evalBlock block
loop
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Interp where
import Prelude hiding (break)
import Control.Applicative
import Control.Monad.Cont
import Control.Monad.State
import Control.Monad.Reader
import Data.Function
import Data.Map (Map)
import qualified Data.Map as M
import Data.Maybe
type Id = String
data Statement
= Print Expression
| Assign Id Expression
| Break
| While Expression [Statement]
| If Expression [Statement]
deriving Show
data Expression
= Var Id
| Constant Value
| Add Expression Expression
| Not Expression
deriving Show
data Value
= String String
| Int Integer
| None
deriving Show
data Env = Env{ loopLevel :: Int
, flow :: Flow
}
data Flow
= Breaking
| Continuing
| Next
deriving Eq
newtype M r a = M{ unM :: ContT r (ReaderT (M r ()) (StateT (Map Id Value) IO)) a }
deriving ( Functor, Applicative, Monad
, MonadReader (M r ()), MonadCont, MonadState (Map Id Value)
, MonadIO
)
runM :: M a a -> IO a
runM m = evalStateT (runReaderT (runContT (unM m) return) (error "not in a loop")) M.empty
withBreakHere :: M r () -> M r ()
withBreakHere act = callCC $ \break -> local (const $ break ()) act
break :: M r ()
break = join ask
evalExpr :: Expression -> M r Value
evalExpr (Constant val) = return val
evalExpr (Var v) = gets $ fromMaybe err . M.lookup v
where
err = error $ unwords ["Variable not in scope:", show v]
evalExpr (Add e1 e2) = do
Int val1 <- evalExpr e1
Int val2 <- evalExpr e2
return $ Int $ val1 + val2
evalExpr (Not e) = do
val <- evalExpr e
return $ if isTruthy val then None else Int 1
isTruthy (String s) = not $ null s
isTruthy (Int n) = n /= 0
isTruthy None = False
evalBlock = mapM_ eval
eval :: Statement -> M r ()
eval (Assign v e) = do
val <- evalExpr e
modify $ M.insert v val
eval (Print e) = do
val <- evalExpr e
liftIO $ print val
eval (If cond block) = do
val <- evalExpr cond
when (isTruthy val) $
evalBlock block
eval Break = break
eval (While condition block) = withBreakHere $ fix $ \loop -> do
result <- evalExpr condition
unless (isTruthy result)
break
evalBlock block
loop
prog = [ Assign "i" $ Constant $ Int 10
, While (Var "i") [ Print (Var "i")
, Assign "i" (Add (Var "i") (Constant $ Int (-1)))
, Assign "j" $ Constant $ Int 10
, While (Var "j") [ Print (Var "j")
, Assign "j" (Add (Var "j") (Constant $ Int (-1)))
, If (Not (Add (Var "j") (Constant $ Int (-4)))) [ Break ]
]
]
, Print $ Constant $ String "Done"
]
i = 10
while i:
print i
i = i - 1
j = 10
while j:
print j
j = j - 1
if j == 4:
break
10 10 9 8 7 6 5
9 10 9 8 7 6 5
8 10 9 8 7 6 5
...
1 10 9 8 7 6 5
关于haskell - 试图将 CPS 应用于口译员,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25365900/
使用支持延续的语言,例如Scheme、Ruby 和 Haskell,假设有一个函数 cc' 不带参数并返回当前延续,以便通过调用 cc' 获得延续的调用者然后可以随心所欲地在任何地方调用延续。 cc'
我正在开发一个用于分割曲面的库。为了表示网格拓扑,我使用了一种 split 顶点板条数据结构(参见左侧的图表)。 在构建网格的过程中,也可以将其视为图形,它创建的节点应该指向另一个尚不存在的节点(请参
这是一个使用 CPS 样式将列表中的元素相乘的函数 mlist xx k = aux xx k where aux [] nk = nk 1 aux (0:xs) nk = k 0
我曾经在业余时间从事 IronLua 的工作。目前已完成词法分析和解析。由于在 .NET 中实现 Lua 协程而不诉诸肮脏的线程技巧并不容易,我有点沮丧地停止了它的工作。这与我编译 Lua 函数的方式
我对代数数据类型几乎没有经验,因为我使用的语言没有原生支持。通常可以使用延续传递样式来获得远程相似的体验,但 CPS 编码类型的处理不太舒服。 考虑到这一点,为什么像 Parsec 这样的库会使用 C
CPS如何像 lambda 演算或 Ocaml 这样的 curry 语言甚至有意义吗?从技术上讲,所有函数都有一个参数。假设我们有一个 CPS 版本的加法,用一种这样的语言: cps-add k n
我们被要求编写一个过程,当给定一个列表时,它将替换给定元素的第一次出现,并且只替换第一个,但要注意的是要以 CPS 风格编写。我们无法将其转换为 CPS 风格的书面程序,即给定成功-连续和失败-连续.
我正在尝试使用 CPS 来简化我的 Python 解释器中的控制流实现。具体来说,当实现 return/break/continue ,我必须手动存储状态和展开,这很乏味。我读过以这种方式实现异常处理
使用Scala 2.8的CPS编译器插件,有两个神奇的控件reset和shift。 Reset 界定延续,shift 捕获延续。 有一个example将 CPS 与 NIO 结合使用,使用嵌套重置作为
我正在研究 Jenkins Pipeline Global Lib功能。这看起来很方便,但由于其全局性,任何有害的变化都会影响所有工作。因此,我希望能够在推送到不同分支上的 master 之前对其进行
我正在努力掌握连续传球风格 (CPS),因此我正在修改 Gary Short 很久以前给我看的一个例子。我没有他的示例源代码,所以我试图从内存中修改他的示例。考虑以下代码: let checkedDi
https://en.wikipedia.org/wiki/Continuation-passing_style说 A function written in continuation-passing
在我的应用程序中,我正在生成遵循 CPS 样式的 JavaScript 代码。我“不”使用任何“延续”。没有异步行为,没有暂停和恢复,也没有回调。 只是代码在 continuation passing
我已经在尾递归和延续传递风格中实现了 map。两个版本非常相似: var inc = x => ++x; var xs = [1,2,3,4,5]; var mapR = f => xs => {
为什么首先打印“DIV/0”,然后打印“2”? let printZero = printfn "DIV/0" let printSuccess x = printfn "%d" x let div
这是我的功能。 function duplicate_step_through_highlighted (element_jq, target_jq, char_cb) { c
为了查看实现循环的延续传递样式是否太慢而无法在 javascript 中使用,我创建了一个 JS-Perf 来使用以下代码对此进行测试: const ITERATIONS = 10000; fu
我正在尝试更新我选择的记录 但是我收到错误#1093,这是我的代码 UPDATE `tabCategories_Products` SET`categories` = 'CAT00001' WHERE
假设我有以下功能: function f(x) { return x + 1; } function g(x) { return x * 2; } function h() { return 5; }
我一直在查看以前的问题,但是,它们似乎已经过时或不适用于我的情况。在我的应用程序中,我想每秒通过另一个变量 (cps) 增加一个变量 (money)。当我尝试执行永久循环或使用计时器时,我要么出错,要
我是一名优秀的程序员,十分优秀!