gpt4 book ai didi

exception - 如何使用动态变量和中止控制在方案中定义异常?

转载 作者:行者123 更新时间:2023-12-04 15:06:20 24 4
gpt4 key购买 nike

我在使用动态范围变量和 handle 实现异常( raiseabort )时遇到问题.
这个问题来自阅读论文A syntactic theory of dynamic binding ,第 6 节图 7。
我试图在 try 块内抛出异常并在 try 块内的 try 块内抛出异常似乎可以正常工作。
不能正常工作的是从处理程序内部抛出异常。在这种情况下,它应该中止到下一个 try 块。
你可以在这里看到我在 Racket 方案中的工作,以及 2 个测试程序。 test-1 正在工作,而 test-2 失败。

#lang racket

;; A Syntactic Theory of Dynamic Binding - Luc Moreau
;; https://link.springer.com/content/pdf/10.1007%2FBFb0030637.pdf

(require racket/control)

(define x_ed (make-parameter 'x_ed))

; NOTE: (abort ..) is the same as (shift k ..) where you don't use k
; NOTE: in (handle f M) we call f the handler and M the try block

; v1
;(define-syntax handle
; (syntax-rules ()
; ((handle f M)
; (parameterize ((x_ed (lambda (v) (abort (f v))))) M))))
; v2
;(define-syntax handle
; (syntax-rules ()
; ((handle f M)
; (reset (parameterize ((x_ed (lambda (v) (abort (f v))))) M)))))
; v3
;(define-syntax handle
; (syntax-rules ()
; ((handle f M)
; (parameterize ((x_ed (lambda (v) (abort (f v))))) (reset M)))))
; v4
(define-syntax handle
(syntax-rules ()
((handle f M)
(let ((old-x_ed (x_ed)))
(parameterize ((x_ed (lambda (v)
(abort (parameterize ((x_ed old-x_ed))
(f v))))))
(reset M))))))
(define-syntax raise
(syntax-rules ()
((raise v) ((x_ed) v))))

(define (print x) (write x) (newline))

(define (test-1)
(print "level-1 open")
(handle (lambda (v)
(print "level-1 caught"))
(begin
(print "level-2 open")
(handle (lambda (v)
(print "level-2 caught"))
(begin
(print "level-3 open")
(raise #t)
(print "level-3 close")))
(print "level-2 close")))
(print "level-1 close"))

(define (test-2)
(print "level-1 open")
(handle (lambda (v)
(print "level-1 caught"))
(begin
(print "level-2 open")
(handle (lambda (v)
(print "level-2 caught")
(raise #t))
(begin
(print "level-3 open")
(raise #t)
(print "level-3 close")))
(print "level-2 close")))
(print "level-1 close"))

;v1
;> (test-1)
;"level-1 open"
;"level-2 open"
;"level-3 open"
;"level-2 caught"

;v2 and v3
;> (test-1)
;"level-1 open"
;"level-2 open"
;"level-3 open"
;"level-2 caught"
;"level-2 close"
;"level-1 close"

;v2 and v3
;> (test-2)
;...
;"level-2 caught"
;"level-2 caught"
; infinite loop

;v4
;> (test-2)
;"level-1 open"
;"level-2 open"
;"level-3 open"
;"level-2 caught"
;"level-1 caught"
;"level-2 close" <--- we don't want this to happen
;"level-1 close"

由于答案,我能够想出这个工作版本:
(define-syntax handle
(syntax-rules ()
((handle f M)
(prompt0
(parameterize ((x_ed (lambda (v)
(control0 k (f v)))))
M)))))

最佳答案

( 编辑 : 我错了,我不能通过使用特殊的控制运算符来实现更节省空间的实现。可以使处理程序运行在相对于 handle 形式的尾部位置,但我也不知道有什么方法可以评估尾部位置的 body 。)

首先,您是否专门尝试在 handle 的主体中实现异常处理?表格相对于 handle 处于尾部位置形式本身?如果没有,就简单的转义延续而言,有更直接的方法来实现异常处理。

但是,如果您真的想实现“空间安全”或“正确尾递归”异常处理,请继续阅读。

实现的挑战handle以安全的空间方式是为了避免将额外的堆栈帧插入到“无异常引发”路径中,您需要能够展开堆栈并在该上下文中使用表达式恢复评估。或者等效地,通过在该上下文中调用过程来恢复评估。那和call/cc不一样提供;它只允许您展开堆栈,然后立即将值返回到该上下文中。

您可以使用 call/cc 模拟额外的功率。以插入额外的堆栈帧为代价(因此主体不在尾部位置):

;; call/cc :       ((Any      -> None) -> Any) -> Any

;; call/cc/apply : (((-> Any) -> None) -> Any) -> Any
(define (call/cc/apply proc)
((call/cc (lambda (k) (let ([v (proc k)]) (lambda () v))))))

额外的栈帧来自 call/cc 的结果的应用表达。

你能消除对额外堆栈帧的需要吗?是的!但不是 shiftreset .

您遇到的问题是 (abort e) (其中 abort 对应于 Felleisen 和 Hieb 的 A 运算符)是 不是 (shift _ e) .如果你看 docs for shift and reset ,您将看到以下归约规则:
(reset val) => val
(reset E[(shift k expr)]) => (reset ((lambda (k) expr)
(lambda (v) (reset E[v]))))
; where E has no reset

即, shift删除 其定界 reset ,还有那个倔强 reset是什么阻止您在不运行 (print "level-2 close") 的情况下直接从 2 级处理程序跳到您的 1 级处理程序.您需要选择一个分隔符和一个允许您删除分隔符的控制运算符。

你不能用 resetshift .
你不能用 promptcontrol .
你可以用 prompt0 来做和 control0 .
你可以用 % 来做和 fcontrol (和正确的处理程序)。
当然,您可以使用 call-with-continuation-prompt 来做到这一点。和 abort-current-continuation (和正确的处理程序)。

关于exception - 如何使用动态变量和中止控制在方案中定义异常?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52938872/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com