Scheme 哪种最简单的评估模型可以解释call/cc?
TL;DR:call/cc(半)正式地说是做什么的 长版本:我对continuations和call/cc略知一二,但对形式上的理解不强。我想要一个 在本文中,我们给出了替换模型和一个元循环解释器。在施里拉姆·克里希那穆蒂的作品中,我们看到了一种环境和商店通行的风格。在我学习uni的编译器课程中,我通过操作堆栈来计算Java表达式 call/cc最简单的评估模型是什么?您如何在其中表达call/cc?TL;DRScheme 哪种最简单的评估模型可以解释call/cc?,scheme,semantics,evaluation,interpretation,callcc,Scheme,Semantics,Evaluation,Interpretation,Callcc,TL;DR:call/cc(半)正式地说是做什么的 长版本:我对continuations和call/cc略知一二,但对形式上的理解不强。我想要一个 在本文中,我们给出了替换模型和一个元循环解释器。在施里拉姆·克里希那穆蒂的作品中,我们看到了一种环境和商店通行的风格。在我学习uni的编译器课程中,我通过操作堆栈来计算Java表达式 call/cc最简单的评估模型是什么?您如何在其中表达call/cc?TL;DRcall/cc允许您点击内部代码,这样您就可以使用continuations,而无需以
call/cc
允许您点击内部代码,这样您就可以使用continuations,而无需以continuationpassing样式编写代码。最好的评估模型是替代模型,前提是您不使用设置并从CPS代码查看它
想象一下这个小程序
(define (sum-list lst)
(cond ((null? lst) 0)
((number? (car lst)) (+ (car lst) (sum-list (cdr lsr))))
(else (sum-list (cdr lsr)))))
(display (sum-list '(1 2 3 4))) ; displays 10
想象一下,如果点击else
项,您希望结果为1337
。如果不重写整件事,你会怎么做?你不能。但是,大多数方案实现都会将CPS转换为代码,而更改代码则非常简单:
(define (sum-list& lst k)
(null?& lst
(lambda (nlst?)
(if& nlst?
(lambda ()
(k 0))
(lambda ()
(car& lst
(lambda (car-lst)
(number?& car-lst
(lambda (ncl?)
(if& ncl?
(lambda ()
(cdr& lst
(lambda (clst)
(sum-list& clst
(lambda (sclst)
(+& car-lst sclst k))))))
(lambda ()
(cdr& lst
(lambda (clst)
(sum-list& clst k))))))))))))))
(sum-list& '(1 2 3 4)
(lambda (sum)
(display& sum halt)))
cond
是一个宏,因此它是if&
调用您看到的。我知道你在想什么。为什么不首先告诉程序员这样做呢?开玩笑
如果您将第二个If&
中的第二个continuation更改为(lambda()(k 1337))
,那么就完成了。尽管CPS很漂亮,但读写都很糟糕,但既然编译器是这样做的,难道就没有办法在第一种方式下编写代码,同时仍然可以控制代码流吗?通过call/cc
启用“两个世界中最好的一个”<代码>呼叫/cc
在CPS中是这样的:
(define (call/cc& f& continuation)
(define (exit& value actual-continuation)
(continuation value))
(f& exit& continuation))
(define (sum-list& lst k)
(call/cc&
(lambda (k& real-k)
(define (helper& lst k)
(null?& lst
(lambda (nlst?)
(if& nlst?
(lambda ()
(k 0))
(lambda ()
(car& lst
(lambda (car-lst)
(number?& car-lst
(lambda (ncl?)
(if& ncl?
(lambda ()
(cdr& lst
(lambda (clst)
(helper& clst
(lambda (sclst)
(+& car-lst sclst k))))))
(lambda ()
(k& 1337 real-k))))))))))))
(helper& lst real-k))
k))
(sum-list& '(1 2 3 4)
(lambda (sum)
(display& sum halt)))
这根本没什么用。它传递exit&
,调用时忽略程序的实际延续,并使用call/cc&
调用的原始延续调用值。使用列表”(1 2 3#f)
您将有3个嵌套的continuation等待添加结果,但所有这些都需要取消。如果在计算之前选择连续值,则会自动取消。
就这样。当您使用它编写代码时,您不需要执行完整的CPS,只需继续您想要的内容就可以像这样思考call/cc
:
(define (sum-list lst)
(call/cc
(lambda (k)
(define (helper lst)
(cond ((null? lst) 0)
((number? (car lst))
(+ (car lst) (helper (cdr lst))))
(else (k 1337))))
(helper lst))))
(define (sum-list lst)
(define (helper lst sum)
(cond ((null? lst) sum)
((number? (car lst)) (helper (cdr lst) (+ sum (car lst))))
(else 1337)))
(helper lst 0))
在CPS中将其转换为:
(define (call/cc& f& continuation)
(define (exit& value actual-continuation)
(continuation value))
(f& exit& continuation))
(define (sum-list& lst k)
(call/cc&
(lambda (k& real-k)
(define (helper& lst k)
(null?& lst
(lambda (nlst?)
(if& nlst?
(lambda ()
(k 0))
(lambda ()
(car& lst
(lambda (car-lst)
(number?& car-lst
(lambda (ncl?)
(if& ncl?
(lambda ()
(cdr& lst
(lambda (clst)
(helper& clst
(lambda (sclst)
(+& car-lst sclst k))))))
(lambda ()
(k& 1337 real-k))))))))))))
(helper& lst real-k))
k))
(sum-list& '(1 2 3 4)
(lambda (sum)
(display& sum halt)))
呼叫/cc
总是可以避免的。我们的示例可以重写为返回1337,如下所示:
(define (sum-list lst)
(call/cc
(lambda (k)
(define (helper lst)
(cond ((null? lst) 0)
((number? (car lst))
(+ (car lst) (helper (cdr lst))))
(else (k 1337))))
(helper lst))))
(define (sum-list lst)
(define (helper lst sum)
(cond ((null? lst) sum)
((number? (car lst)) (helper (cdr lst) (+ sum (car lst))))
(else 1337)))
(helper lst 0))
这是尾部递归,它不创建连续性,而是累积。为完整起见,以下是CPS版本:
(define (helper& lst sum k)
(null?& lst
(lambda (nlst)
(if& nlst
(lambda () (k sum))
(lambda ()
(car& lst
(lambda (cl)
(number?& cl
(lambda (ncl?)
(if& ncl?
(lambda ()
(cdr& lst
(lambda (cdrl)
(+& sum
cl
(lambda (scl)
(helper& cdrl
scl
k))))))
(lambda () (k 1337))))))))))))
(define (sum-list& lst k)
(helper& lst 0 k))
TL;DRcall/cc
允许您点击内部代码,这样您就可以使用continuations,而无需以continuationpassing样式编写代码。最好的评估模型是替代模型,前提是您不使用设置并从CPS代码查看它
想象一下这个小程序
(define (sum-list lst)
(cond ((null? lst) 0)
((number? (car lst)) (+ (car lst) (sum-list (cdr lsr))))
(else (sum-list (cdr lsr)))))
(display (sum-list '(1 2 3 4))) ; displays 10
想象一下,如果点击else
项,您希望结果为1337
。如果不重写整件事,你会怎么做?你不能。但是,大多数方案实现都会将CPS转换为代码,而更改代码则非常简单:
(define (sum-list& lst k)
(null?& lst
(lambda (nlst?)
(if& nlst?
(lambda ()
(k 0))
(lambda ()
(car& lst
(lambda (car-lst)
(number?& car-lst
(lambda (ncl?)
(if& ncl?
(lambda ()
(cdr& lst
(lambda (clst)
(sum-list& clst
(lambda (sclst)
(+& car-lst sclst k))))))
(lambda ()
(cdr& lst
(lambda (clst)
(sum-list& clst k))))))))))))))
(sum-list& '(1 2 3 4)
(lambda (sum)
(display& sum halt)))
cond
是一个宏,因此它是if&
调用您看到的。我知道你在想什么。为什么不首先告诉程序员这样做呢?开玩笑
如果您将第二个If&
中的第二个continuation更改为(lambda()(k 1337))
,那么就完成了。尽管CPS很漂亮,但读写都很糟糕,但既然编译器是这样做的,难道就没有办法在第一种方式下编写代码,同时仍然可以控制代码流吗?通过call/cc
启用“两个世界中最好的一个”<代码>呼叫/cc
在CPS中是这样的:
(define (call/cc& f& continuation)
(define (exit& value actual-continuation)
(continuation value))
(f& exit& continuation))
(define (sum-list& lst k)
(call/cc&
(lambda (k& real-k)
(define (helper& lst k)
(null?& lst
(lambda (nlst?)
(if& nlst?
(lambda ()
(k 0))
(lambda ()
(car& lst
(lambda (car-lst)
(number?& car-lst
(lambda (ncl?)
(if& ncl?
(lambda ()
(cdr& lst
(lambda (clst)
(helper& clst
(lambda (sclst)
(+& car-lst sclst k))))))
(lambda ()
(k& 1337 real-k))))))))))))
(helper& lst real-k))
k))
(sum-list& '(1 2 3 4)
(lambda (sum)
(display& sum halt)))
这根本没什么用。它传递exit&
,调用时忽略程序的实际延续,并使用call/cc&
调用的原始延续调用值。使用列表”(1 2 3#f)
您将有3个嵌套的continuation等待添加结果,但所有这些都需要取消。如果在计算之前选择连续值,则会自动取消。
就这样。当您使用它编写代码时,您不需要执行完整的CPS,只需继续您想要的内容就可以像这样思考call/cc
:
(define (sum-list lst)
(call/cc
(lambda (k)
(define (helper lst)
(cond ((null? lst) 0)
((number? (car lst))
(+ (car lst) (helper (cdr lst))))
(else (k 1337))))
(helper lst))))
(define (sum-list lst)
(define (helper lst sum)
(cond ((null? lst) sum)
((number? (car lst)) (helper (cdr lst) (+ sum (car lst))))
(else 1337)))
(helper lst 0))
在CPS中将其转换为:
(define (call/cc& f& continuation)
(define (exit& value actual-continuation)
(continuation value))
(f& exit& continuation))
(define (sum-list& lst k)
(call/cc&
(lambda (k& real-k)
(define (helper& lst k)
(null?& lst
(lambda (nlst?)
(if& nlst?
(lambda ()
(k 0))
(lambda ()
(car& lst
(lambda (car-lst)
(number?& car-lst
(lambda (ncl?)
(if& ncl?
(lambda ()
(cdr& lst
(lambda (clst)
(helper& clst
(lambda (sclst)
(+& car-lst sclst k))))))
(lambda ()
(k& 1337 real-k))))))))))))
(helper& lst real-k))
k))
(sum-list& '(1 2 3 4)
(lambda (sum)
(display& sum halt)))
呼叫/cc
总是可以避免的。我们的示例可以重写为返回1337,如下所示:
(define (sum-list lst)
(call/cc
(lambda (k)
(define (helper lst)
(cond ((null? lst) 0)
((number? (car lst))
(+ (car lst) (helper (cdr lst))))
(else (k 1337))))
(helper lst))))
(define (sum-list lst)
(define (helper lst sum)
(cond ((null? lst) sum)
((number? (car lst)) (helper (cdr lst) (+ sum (car lst))))
(else 1337)))
(helper lst 0))
这是尾部递归,它不创建连续性,而是累积。为完整起见,以下是CPS版本:
(define (helper& lst sum k)
(null?& lst
(lambda (nlst)
(if& nlst
(lambda () (k sum))
(lambda ()
(car& lst
(lambda (cl)
(number?& cl
(lambda (ncl?)
(if& ncl?
(lambda ()
(cdr& lst
(lambda (cdrl)
(+& sum
cl
(lambda (scl)
(helper& cdrl
scl
k))))))
(lambda () (k 1337))))))))))))
(define (sum-list& lst k)
(helper& lst 0 k))
我发现这篇精彩的演讲(德语)回答了我的问题:
要使用call/CC计算lambda演算,需要传递一个由环境(通常)和调用堆栈组成的计算上下文。调用call/cc
会创建一个特殊的函数,如continuation对象,它存储求值上下文。将特殊对象应用于表达式expr
的结果是在continuation对象中捕获的求值上下文中解释expr
的结果
TL;DR:您可以使用环境和调用堆栈传递解释器实现call/cc
如果您还希望围绕可变存储线程,那么continuation对象不应该捕获它。相反,在调用延续时,将存储作为参数传递给恢复的求值上下文中的解释。(存储可以是线性类型。)
这里是Haskell中的一个这样的实现。下面是一个您可能需要计算的示例表达式:解释0(应用程序(Lambda)1,(CallCC(Lambda)2,(应用程序(变量2)(Lambda)3,(变量