我正在阅读方案编程语言并在后续部分看到这个例子:
(((call/cc (lambda (k) k)) (lambda (x) x)) "HEY!") => "HEY!"
我不明白为什么这个表达式评估为“嘿!”
根据 CSE 341 讲义:
(call/cc expr)
执行以下操作:
C
,并将当前延续应用到该参数值。expr
--- 即,它调用 (expr C)
.(expr C)
的结果,除非expr
调用C
,在这种情况下返回传递给C
的值。所以,在这个例子中
(((call/cc (lambda (k) k)) (lambda (x) x)) "HEY!") => "HEY!"
call/cc
的当前延续是
(lambda (v)
((v (lambda (x) x)) "HEY!")
构造函数
C
是
(lambda (y)
((lambda (v)
((v (lambda (x) x)) "HEY!")) y))
调用
((lambda (x) x) C)
返回评估
((lambda (x) x) C)
的结果,也就是C
本身
因此,
(call/cc (lambda (k) k))
的计算结果为 C
。整个表达式将是:
(((lambda (y)
((lambda (v)
((v (lambda (x) x)) "HEY!")) y)) (lambda (x) x)) "HEY!")
即
((C (lambda (x) x)) "HEY!")
,也就是("HEY!" "HEY!")
。
这将导致异常:Wrong type to apply: "HEY!"
,意味着应用非函数值“嘿!”。
我哪里错了?
我会尝试分解它。
从
开始(((call/cc (lambda (k) k)) (lambda (x) x)) "HEY!")
评估
((call/cc (lambda (k) k)) (lambda (x) x))
的结果需要是一个函数,并且用参数"HEY!"
调用它。
(call/cc (lambda (k) k))
调用一个匿名函数并传递给它一个延续,一个特殊的函数,当被调用时,会将其参数返回到
call/cc
返回的位置。匿名函数只是返回那个延续而不调用它。将该延续称为kont
,并将其替换为整个call/cc
表达式:
(kont (lambda (x) x))
现在调用该延续,带有一个匿名函数,该函数接受一个参数并将其作为参数返回。因此,该功能是评估延续的结果。所以基本上
((call/cc (lambda (k) k)) (lambda (x) x))
等同于(lambda (x) x)
。然后用参数"HEY!"
调用它,然后它返回。
我是这样读的,用CPS表示:
(define (identity& v k)
(k v))
(define (call/cc& f& k)
(f& (lambda (v ingnored-k) (k v)) k))
(define (repl-print v)
v)
(call/cc& identity&
(lambda (v&)
(v& identity&
(lambda (v2&)
(v2& "HEY!" repl-print)))))
所以
call/cc&
调用的当前延续是:
(lambda (v&)
(v& identity&
(lambda (v2&)
(v2& "HEY!" repl-print))))
第一轮,
v&
是一个continuation函数,当被调用时,不会将值传递给它自己的continuation(ignored-k),而是call/cc&
的continuation。因此,如果 v&
被调用,上面将被调用。
v&
被调用,结果是 identity&
。因此,原始延续被调用,这次 v&
是 identity&
v&
(identity&
) 被称为 identity&
作为参数和延续 (lambda (v2&) ...)
v2&
是 identity&
它被称为 "HEY!"
和延续 repl-print
repl-print
被调用 "HEY!"
。显示文本,程序终止。
注意 #1 和 #2 最终以不同的值调用相同的延续。如果它是一样的,你就会有一个循环。