在此示例中,我试图理解 call / cc 的执行情况:
(let ((x (call/cc (lambda (k) k))))
(x (lambda (ignore) "hi")))
给出值"hi"
。
doc中的执行描述为:
取值,将其绑定到x,然后将x的值应用于的值 (lambda(忽略)“嗨”)。
我可以理解,在 let 中捕获的延续将是“取值,将其绑定到x”,因为这是 let 的作用,而不是“应用x”部分的值。
因此,我期望输出只是将绑定 x
与(lambda (ignore) "hi")
绑定,而不应用。
例如,定义可以按预期工作:
(define x (call/cc (lambda (k) k)))
(x (lambda (ignore) "hi"))
将定义为 x 作为该值,但不应用。
在这种情况下,有人可以解释 let 和 define 之间的区别吗?
对此的可能解释也将有助于了解正在发生的事情:
(let ((x (call/cc (lambda (k) k)))) (+ (x 1) 1))
我希望它将 x 绑定到1并仅求值为2,但是它说它希望使用 procedure 。
编辑:我相信还有待完成的工作实际上是 bind x和 evaluate let >表达也。在这种情况下,这是有道理的。另外,这意味着(let ((x (call/cc (lambda (k) k)))) (+ (x 1) 1))
永远无法正常工作。
编辑:的确是这样工作的。您可以观看this视频,该视频说明了此示例。我认为可以将其非正式地概括为“ let”比“ define”更重要,因为它也必须执行表达式。
答案 0 :(得分:3)
TL; DR:define
和let
之间的真正区别在于顶层程序表达式,因为define
只能在标识符上执行一次,并且顶层语句之间通常会有连续提示。
我将在此答案中使用CPS。我使用的通用过程的定义是:
;; returns argument
(define halt values)
;; call/cc in CPS version
(define (call/cc-k f k)
(f (lambda (v k-ignore) (k v)) k))
let
和define
在过程中的作用类似:
(let ()
(define x (call/cc (lambda (k) k)))
(x (lambda (ignore) "hi")))
; ==
(let ()
(letrec ((x (call/cc (lambda (k) k)))
(x (lambda (ignore) "hi")))
let
中的相同内容变为:
(let ()
(let ((x 'undefined))
(set! x (call/cc (lambda (k) k)))
(x (lambda (ignore) "hi"))))
CPS中的相同(尽管我省略了绑定的更新,因为阴影在此代码中具有相同的作用:
((lambda (x k)
(call/cc-k
(lambda (k2 real-k) (real-k k2))
(lambda (x)
(x (lambda (ignore k2) (k2 "hi")) k))))
'undefined halt)
;; ===
((lambda (x k)
((lambda (f5 k5) (f5 (lambda (v k-ignore) (k5 v)) k5))
(lambda (k3 real-k) (real-k k3))
(lambda (x)
(x (lambda (ignore k2) (k2 "hi")) k))))
'undefined halt)
;; ===
((lambda (x k)
(define hi-k (lambda (x) (x (lambda (ignore k2) (k2 "hi")) k)))
((lambda (f5 k5) (f5 (lambda (v k-ignore) (k5 v)) k5))
(lambda (k3 real-k) (real-k k3))
hi-k))
'undefined halt)
;; === this is starting to look like a combinator :-)
((lambda (x k)
((lambda (ignore k2) (k2 "hi"))
(lambda (ignore k2) (k2 "hi")) k))
'undefined halt)
;; ===
((lambda (x k)
(k "hi"))
'undefined halt)
;; ===
(halt "hi")
您的let
版本会这样做:
(let ((x (call/cc (lambda (k) k))))
(x (lambda (ignore) "hi")))
;;; === in terms of lambda instead of let
((lambda (x)
(x (lambda (ignore) "hi")))
(call/cc (lambda (k) k)))
;;; === in CPS
(call/cc-k
(lambda (k real-k) (real-k k))
(lambda (x)
(x (lambda (ignore k2) (k2 "hi")) halt)))
;;; ===
(define hi-k (lambda (x) (x (lambda (ignore k2) (k2 "hi")) halt)))
((lambda (k real-k) (real-k k)) (lambda (v k-ignore) (hi-k v)) hi-k))
;;; ===
((lambda (ignore k2) (k2 "hi"))
(lambda (ignore k2) (k2 "hi"))
halt)
;;; ===
(halt "hi")
顶层使用的 define
有很大不同,并且由于define
不能在同一个变量上执行两次,因此您的代码无效(无效),用于顶层评估。为了弥补这一点,我想我们将其重写为:
(define x #f)
(set! x (call/cc (lambda (k) k)))
(x (lambda (ignore) "hi"))
我将跳过define
并在续篇上写下CPS:
(call/cc-k
(lambda (k real-k) (real-k k))
(lambda (v)
(set!-k x
v
(lambda (undefined)
(x (lambda (ignore k2) (k2 "hi")) halt)))))
;;; ===
(define set-k (lambda (v)
(set!-k x
v
(lambda (undefined)
(x (lambda (ignore k2) (k2 "hi")) halt)))))
(call/cc-k
(lambda (k real-k) (real-k k))
set-k)
;; ===
(define set-k (lambda (v)
(set!-k x
v
(lambda (undefined)
(x (lambda (ignore k2) (k2 "hi")) halt)))))
((lambda (k real-k) (real-k k))
(lambda (v k-ignore) (set-k v))
set-k)
;; ===
(define set-k (lambda (v)
(set!-k x
v
(lambda (undefined)
(x (lambda (ignore k2) (k2 "hi")) halt)))))
(set-k (lambda (v k-ignore) (set-k v)))
;; ===
(define set-k (lambda (v)
(set!-k x
v
(lambda (undefined)
(x (lambda (ignore k2) (k2 "hi")) halt)))))
(set!-k x
(lambda (v k-ignore) (set-k v))
(lambda (undefined)
(x (lambda (ignore k2) (k2 "hi")) halt)))
;; ===
(set!-k x
(lambda (v k-ignore) (set-k v))
(lambda (undefined)
((lambda (v k-ignore) (set-k v)) (lambda (ignore k2) (k2 "hi")) halt)))
;; ===
(set!-k x
(lambda (v k-ignore) (set-k v))
(lambda (undefined)
(set!-k x
(lambda (ignore k2) (k2 "hi"))
(lambda (undefined)
(x (lambda (ignore k2) (k2 "hi")) halt)))))
;;; ===
(set!-k x
(lambda (v k-ignore) (set-k v))
(lambda (undefined)
(set!-k x
(lambda (ignore k2) (k2 "hi"))
(lambda (undefined)
((lambda (ignore k2) (k2 "hi")) (lambda (ignore k2) (k2 "hi")) halt)))))
;;; ===
(set!-k x
(lambda (v k-ignore) (set-k v))
(lambda (undefined)
(set!-k x
(lambda (ignore k2) (k2 "hi"))
(lambda (undefined)
(halt "hi")))))
;;; ===
(halt "hi")
但是这很奇怪,因为如果您尝试这样做,您可能什么也得不到。这样做的原因是顶层表达式由继续提示分隔。因此,call/cc
捕获的每个顶级语句的延续都是halt
而不是程序的其余部分。让我们尝试一下:
(call/cc-k
(lambda (k real-k) (real-k k))
(lambda (v)
(set!-k x
v
halt)))
(x (lambda (ignore k2) (k2 "hi")) halt)
;; ===
(define set-k
(lambda (v)
(set!-k x
v
halt)))
((lambda (k real-k) (real-k k)) (lambda (v k-ignore) (set-k v)) set-k)
(x (lambda (ignore k2) (k2 "hi")) halt)
;; ===
(set-k (lambda (v k-ignore) (set-k v)))
(x (lambda (ignore k2) (k2 "hi")) halt)
;; ===
((lambda (v)
(set!-k x
v
halt))
(lambda (v k-ignore) (set-k v)))
(x (lambda (ignore k2) (k2 "hi")) halt)
;; ===
(set!-k x (lambda (v k-ignore) (set-k v)) halt)
(x (lambda (ignore k2) (k2 "hi")) halt)
;; ===
(set!-k x (lambda (v k-ignore) (set-k v)) halt)
((lambda (v k-ignore) (set-k v))
(lambda (ignore k2) (k2 "hi"))
halt)
;; ===
(set!-k x (lambda (v k-ignore) (set-k v)) halt)
(set-k (lambda (ignore k2) (k2 "hi")))
;; ===
(set!-k x (lambda (v k-ignore) (set-k v)) halt)
((lambda (v)
(set!-k x
v
halt))
(lambda (ignore k2) (k2 "hi")))
;; ===
(set!-k x (lambda (v k-ignore) (set-k v)) halt)
(set!-k x (lambda (ignore k2) (k2 "hi")) halt)
由于延续提示未执行完整的延续,并且代码真正执行的唯一操作是两次设置x
。
您的最后一个示例不起作用,因为x
在连续和数字之间切换。
(let ((x (call/cc (lambda (k) k))))
(+ (x 1) 1))
;; in CPS
(call/cc-k
(lambda (k real-k) (realk k))
(lambda (x)
(x 1 (lambda (v1)
(+-k v1 1 halt)))))
;; ===
(define k (lambda (x)
(x 1 (lambda (v1)
(+-k v1 1 halt)))))
((lambda (k real-k) (realk k))
(lambda (v k-ignore) (k v))
k)
;; ===
(define k (lambda (x)
(x 1 (lambda (v1)
(+-k v1 1 halt)))))
(k (lambda (v k-ignore) (k v)))
;; ===
((lambda (x)
(x 1 (lambda (v1)
(+-k v1 1 halt))))
(lambda (v k-ignore) (k v)))
;; ===
((lambda (v k-ignore) (k v))
1
(lambda (v1)
(+-k v1 1 halt)))
;; ===
(k 1)
;; ===
((lambda (x)
(x 1 (lambda (v1)
(+-k v1 1 halt))))
1)
;; ===
(1 1 (lambda (v1) (+-k v1 1 halt)))
ERROR: 1 is not a procedure!
YMMV以及所有这些可能都是噪音,但是第二个发现call/cc
只是在偷看CPS版本的代码,而无需编写CPS来了解它的工作原理是很容易的。祝您黑客愉快!
答案 1 :(得分:2)
(let ((x (call/cc (lambda (k) k)))) ; binds x to the value of (call/cc ...)
(x (lambda (ignore) "hi"))) ; applies x to the value of (lambda ...)
翻译为
(let ((x #f))
(set! x (call/cc (lambda (k) k)))
(x (lambda (ignore) "hi")))
变成
(let ((x #f))
(set! x (lambda (ignore) "hi"))
(x (lambda (ignore) "hi")))
和带有define
的代码翻译相同(出于解释此代码段的目的;带有内部非顶级 {{1} } s。
define
和let
都对表达式进行求值,以找出变量绑定的值,这正是您所要的,就好像它们在这方面有所不同。他们不是。
您的第三个代码段翻译为
define
变成
(let ((x #f))
(set! x (call/cc (lambda (k) k)))
(+ (x 1) 1))
变成
(let ((x #f))
(set! x 1)
(+ (x 1) 1))
答案 2 :(得分:1)
(let ((x (call/cc (lambda (k) k))))
(x (lambda (ignore) "hi")))
call/cc
接受一个参数,一个接受一个参数的函数,然后调用该函数。传递给它的参数(续集)本身是一个参数的函数,当被调用时,使call/cc
返回其参数。如果从未调用该函数,则call/cc
返回其参数返回的任何值。所以...
(let ((x (call/cc (lambda (k) k))))
这时,x拥有一个延续,当用一个值调用x时,它使call/cc
返回该值。即使call/cc
已经退出,调用该继续会使它再次跳到该点。
(x (lambda (ignore) "hi")))
现在,该连续被调用,带有一个参数(返回一个“ hi”)的lambda(称为A)。所以它跳回到了
(let ((x (call/cc (lambda (k) k))))
并且x现在绑定到该lambda A,因为调用延续的行为就像call/cc
刚返回一样。
(x (lambda (ignore) "hi")))
,现在调用x(即A),忽略其参数,并返回“ hi”。使用define的示例以相同的方式工作:x
绑定到一个值,然后在调用该值时,x
绑定到一个新值,并使用该新值进行调用。