![](/img/trans.png)
[英]Can the function argument to `call/cc` equivalently either invoke the continuation or return without invoking the continuation?
[英]Can any case of using call/cc be rewritten equivalently without using it?
任何使用call/cc
可以在不使用的情況下等效地重寫嗎?
例如
在(g (call/cc f))
,是的目的f
評估一些的值expression
,所以g
可應用於值?
是(g (call/cc f))
總是能夠在沒有call/cc
情況下等效地重寫,例如(g expression)
?
在((call/cc f) arg)
, f
的目的是評估某些函數g
的定義,以便函數g
可以應用於arg
的值?
是((call/cc f) arg)
總是能夠在不call/cc
情況下等效地重寫(g arg)
例如(g arg)
?
如果答案是肯定的,為什么我們需要使用call/cc
? 我試圖理解使用call/cc
的目的,將其與不使用它進行對比。
這里直接回答的關鍵是“圖靈對等”的概念。 也就是說,基本上所有常用的編程語言(C,Java,Scheme,Haskell,Lambda Calculus等等)都是等價的,對於其中一種語言中的任何一種語言,每種語言中都有相應的程序。具有相同含義的其他語言。
除此之外,這些等價物中的一些可能是“好的”而有些可能非常可怕。 這表明我們重新構思了這樣一個問題:哪些功能可以用“漂亮”的方式重寫為沒有該功能的語言,哪些不能?
對此的正式處理來自Matthias Felleisen,他在1991年的論文“關於編程語言的表達能力”( https://www.sciencedirect.com/science/article/pii/016764239190036W )中引入了宏觀可表達性的概念。 ,指出某些功能可以用本地方式重寫,有些功能需要全局重寫。
你原來問題的答案顯然是肯定的。 Scheme是Turing-complete,有或沒有call/cc
,所以即使沒有call/cc
,你仍然可以計算任何可計算的東西。
為什么“比使用lambda編寫等效表達式更方便”?
Matthias Felleisen撰寫的關於編程語言表達能力的經典論文給出了這個問題的一個答案。 好吧,要將一個帶有call/cc
的程序重寫為沒有它的程序,你可能需要轉換整個程序(全局轉換)。 這是為了對比其他一些只需要局部轉換(即可以寫成宏)的構造來移除它們。
關鍵是: 如果您的程序是以延續傳遞方式編寫的,則不需要call/cc
。 如果沒有,祝你好運。
我全心全意地推薦:
丹尼爾P.弗里德曼。 “Continuations的應用:邀請教程”。 1988年編程語言原則(POPL88)。 1988年1月
https://cs.indiana.edu/~dfried/appcont.pdf
如果您喜歡閱讀該論文,請查看: https : //github.com/scheme-live/bibliography/blob/master/page6.md
當然,使用call/cc
編寫的任何內容都可以在沒有它的情況下編寫,因為Scheme中的所有內容最終都是使用lambda
編寫的。 您使用call/cc
因為它比使用lambda
編寫等效表達式更方便。
這個問題有兩種感覺:一種無趣的感覺和一種有趣的感覺:
無趣的一個。 是否有一些計算可以用call/cc
做,你不能用沒有它的語言做?
不,沒有: call/cc
沒有使語言更加強大:着名的情況是只有λ和功能應用的語言相當於通用的圖靈機,因此沒有(已知的)。 ..)更強大的計算系統。
但從編程語言設計的角度來看,這有點無趣:受內存和c的正常限制,幾乎所有編程語言都等同於UTM,但人們仍然喜歡使用不涉及打孔的語言紙膠帶,如果他們可以。
有趣的一個。 call/cc
會使編程語言的一些理想特性更容易表達?
答案是肯定的,確實如此。 我只想舉幾個例子。 假設您希望在您的語言中使用某種非本地退出功能,因此一些深度嵌套的程序可以簡單地說“我想要這個地獄”,而不必通過一些很好的層來爬回來職能。 使用call/cc
這是微不足道的 :延續過程是轉義過程。 如果你希望它更好,你可以用一些語法包裝它:
(define-syntax with-escape
(syntax-rules ()
[(_ (e) form ...)
(call/cc (λ (e) form ...))]))
(with-escape (e)
... code in here, and can call e to escape, and return some values ...)
你可以在沒有call/cc
情況下實現嗎? 嗯,是的,但不是沒有依賴於其他一些特殊構造(比如CL中的block
和return-from
),或者沒有以某種方式將語言轉換為內部。
你可以建立這樣的東西來實現各種非本地轉義。
或者,好吧,假設您想要GO TO(以下示例是Racket):
(define (test n)
(define m 0)
(define start (call/cc (λ (c) c)))
(printf "here ~A~%" m)
(set! m (+ m 1))
(when (< m n)
(start start)))
或者,使用一些語法:
(define-syntax-rule (label place)
(define place (call/cc identity)))
(define (go place)
(place place))
(define (horrid n)
(define m 0)
(label start)
(printf "here ~A~%" m)
(set! m (+ m 1))
(when (< m n)
(go start)))
所以,好吧,這可能不是編程語言的理想特征。 但是,好吧,Scheme沒有GO TO,但是,在這里,它確實如此。
所以,是的, call/cc
(特別是與宏結合使用時)可以表達編程語言的許多理想特性。 其他語言都有這些特殊目的,有限的黑客攻擊,Scheme具有這種通用的東西,所有這些特殊用途的黑客都可以用來構建。
問題是call/cc
並沒有停止使用好的特殊用途黑客:你還可以構建所有可怕的恐怖,這些恐怖曾經用於破壞編程語言。 call/cc
就像是可以接觸到一位上帝的神:如果你想要恐懼力量,這真的很方便,但是當你打電話時,你最好小心一下,因為它可能是超越時空的無法形容的恐怖。
很容易使用call/cc
作為紓困。 例如。
;; (1 2) => (2 4)
;; #f if one element is not a number
(define (double-numbers lst)
(call/cc
(lambda (exit)
(let helper ((lst lst))
(cond ((null? lst) '())
((not (number? (car lst))) (exit #f))
(else (cons (* 2 (car lst)) (helper (cdr lst)))))))))
所以要理解這一點。 如果我們正在做(double-numbers '(1 2 r))
,結果是#f
,但幫助器已經完成(cons 1 (cons 2 (exit #f)))
如果沒有call/cc
我們會看到延續將是所謂的double-numbers
因為它實際上從它返回。 這是一個沒有call/cc
的示例:
;; (1 2) => (2 4)
;; #f if one element is not a number
(define (double-numbers lst)
(define (helper& lst cont)
(cond ((null? lst) (cont '()))
((not (number? (car lst))) #f) ; bail out, not using cont
(else (helper& (cdr lst)
(lambda (result)
(cont (cons (* 2 (car lst)) result)))))))
(helper& lst values)) ; values works as an identity procedure
我想它變得越來越難。 例如。 我的發電機實施 。 生成器依賴於訪問continuation以將生成器代碼與其使用位置混合,但是如果沒有call/cc
,則需要在生成器,生成的生成器和使用它的代碼中執行CPS。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.