Dynamic Scoping in Scheme

詞法作用域 vs 動態(tài)作用域

scheme是一門采用詞法作用域(lexical scoping)的lisp方言,這個設計是從alogol語言里借鑒過來的。現(xiàn)在,詞法作用域已經被許多l(xiāng)isp方言所吸收,實踐表明疤苹,這的確是一項正確的設計,避免了很多奇怪的錯誤敛腌,比較符合人類的思維習慣卧土。

但是凸椿,在某些場合下娄昆,動態(tài)作用域又是很有用的特性,比如emacs lisp里面就默認采用動態(tài)作用域囤官。

下面的程序演示了詞法作用域與動態(tài)作用域的不同

(define x 1)
(define y (lambda () x))
(let ([x 2]) (y))

如果是詞法作用域生棍,返回1颤霎,如果是動態(tài)作用域,返回2。

在scheme中模擬動態(tài)作用域

從一個簡單的例子出發(fā)友酱,演示如何在scheme里面實現(xiàn)動態(tài)作用域晴音,我們想要寫一個try catch宏來處理程序中的異常。其中一個函數(shù)叫做current-exception-handler缔杉,我們希望它是動態(tài)的锤躁,隨代碼運行位置而變化,永遠指向當前的異常處理器或详,下面是我們寫的第一個版本

(define current-exception-handler
? (lambda (msg) (error "No Top Level Try")))
(define-syntax try
? (syntax-rules (catch)
??? [(_ expr ... (catch msg expr* ...))
??????? (call/cc (lambda (k)
??????????????? (let ([msg (call/cc (lambda (k1)
????????????????????????????????????? (set! current-exception-handler k1)
????????????????????????????????????? (let ([result (begin expr ...)])
????????????????????????????????????? (k result)
????????????????????????????????????? )))])
????????????????? expr* ...)))]))
(define (throw msg) (current-exception-handler msg))

其中系羞,涉及throw的代碼必須被包含在try里面,否則會導致錯誤霸琴。先來測試一下

(try 1
???? (throw 'foo)
???? (catch msg (display "catch ") (display msg)))

但是如果我們在后面再加上一行代碼

(throw 'test)

這里就出現(xiàn)問題了椒振,按照我們上面的要求throw應該在try catch塊里面使用,而這里卻不會報錯梧乘,說明我們上面的代碼錯了杠人。

進入try catch塊時,我們把current-exception-handler設置為當前try catch塊的exception-handler宋下,但當運行出try catch塊的時候,exception-handler并沒有發(fā)生變化辑莫,我們希望恢復原來的exception-handler学歧,使得不論是正常退出或者是發(fā)生錯誤退出都能恢復原有的exception-handler,所以修改一下代碼各吨,就是:

(define current-exception-handler
? (lambda (msg) (error "No Top Level Try")))
(define-syntax try
? (syntax-rules (catch)
??? [(_ expr ... (catch msg expr* ...))
??????? (call/cc (lambda (k)
??????????????? (let ([msg (call/cc (let ([preserved current-exception-handler])
??????????????????????????????????????? (lambda (k1)
????????????????????????????????????? (set! current-exception-handler (lambda (msg)
????????????????????????????????????????????????????????????????????? (set! current-exception-handler preserved)?
??????????????????????????????????????????????????????????????????????? (k1 msg)))
????????????????????????????????????? (let ([result (begin expr ...)])
??????????????????????????????????????? (set! current-exception-handler preserved)
????????????????????????????????????? (k result)
????????????????????????????????????? ))))])
????????????????? expr* ...)))]))
(define (throw msg) (current-exception-handler msg))

這樣枝笨,只要代碼出了try catch塊,current-exception-handler就會恢復成原來的揭蜒,從而實現(xiàn)了動態(tài)作用域的效果横浑。

fluid-let語句

如果你記得fluid-let語句的話,你就會發(fā)現(xiàn)上面的代碼效果和fluid-let語句很類似屉更,沒錯徙融,fluid-let語句就是被設計用來實現(xiàn)dynamic scoping效果的,fluid-let語句的定義如下(摘自chez scheme user guide 8):

(define-syntax fluid-let
?? (lambda (x)
???? (syntax-case x ()
?????? [(_ () b1 b2 ...) #'(let () b1 b2 ...)]
?????? [(_ ((x e) ...) b1 b2 ...)
??????? (andmap identifier? #'(x ...))
??????? (with-syntax ([(y ...) (generate-temporaries #'(x ...))])
????????? #'(let ([y e] ...)
????????????? (let ([swap (lambda ()
??????????????????????????? (let ([t x]) (set! x y) (set! y t))
??????????????????????????? ...)])
??????????????? (dynamic-wind swap (lambda () b1 b2 ...) swap))))])))

具體的實現(xiàn)細節(jié)就不說了瑰谜,給一個例子:

(define x 1)
(define y (lambda () x))
(fluid-let ([x 2]) (y))

這和第一段代碼一模一樣欺冀,只不過用了fluid-let語句,就在scheme里面模擬出了dynamic scoping的效果萨脑。

這樣隐轩,try catch宏就可以寫的很簡單了:

(define-syntax try
? (syntax-rules (catch)
??? [(_ expr ... (catch msg expr* ...))
??????? (call/cc (lambda (k)
??????????????? (let ([msg (call/cc (lambda (k1)
????????????????????????????????????? (fluid-let ([current-exception-handler k1])
????????????????????????????????????? (let ([result (begin expr ...)])
????????????????????????????????????? (k result)
????????????????????????????????????? ))))])
????????????????? expr* ...)))]))

parameterize語句-更好的選擇

但是出于某些原因(效率,賦值渤早,標準庫里面沒有提供职车。。),我們并不想用fluid-let語句來解決問題悴灵,恰好rnrs標準庫里面就提供了一個類似的扛芽,parameterize語句,同樣可以完成任務。

(define x (make-parameter 1))
(define y (lambda () (x)))
(parameterize ([x 2]) (y))

如果我們在parameterize塊外面嘗試調用y函數(shù)称勋,就會得到1胸哥,這說明parameterize只對塊內生效,一旦出了parameterize塊(不論是通過何種方式)赡鲜,就會恢復成原來的樣子空厌。

scheme與其他lisp方言在作用域方面還有一個不同點,它的宏也是采用了詞法作用域银酬,而其他lisp方言的宏幾乎都是動態(tài)作用域嘲更。如果你想了解scheme的宏如何實現(xiàn)動態(tài)作用域的效果,請看這里:http://schemeworkshop.org/2011/slides/Barzilay2011.pdf

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末揩瞪,一起剝皮案震驚了整個濱河市赋朦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌李破,老刑警劉巖宠哄,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異嗤攻,居然都是意外死亡毛嫉,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進店門妇菱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來承粤,“玉大人,你說我怎么就攤上這事闯团⌒岭” “怎么了?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵房交,是天一觀的道長彻舰。 經常有香客問我,道長候味,這世上最難降的妖魔是什么淹遵? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮负溪,結果婚禮上透揣,老公的妹妹穿的比我還像新娘。我一直安慰自己川抡,他們只是感情好辐真,可當我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布须尚。 她就那樣靜靜地躺著,像睡著了一般侍咱。 火紅的嫁衣襯著肌膚如雪耐床。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天楔脯,我揣著相機與錄音撩轰,去河邊找鬼。 笑死昧廷,一個胖子當著我的面吹牛堪嫂,可吹牛的內容都是我干的。 我是一名探鬼主播木柬,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼皆串,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了眉枕?” 一聲冷哼從身側響起恶复,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎速挑,沒想到半個月后谤牡,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡姥宝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年拓哟,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伶授。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖流纹,靈堂內的尸體忽然破棺而出糜烹,到底是詐尸還是另有隱情,我是刑警寧澤漱凝,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布疮蹦,位于F島的核電站,受9級特大地震影響茸炒,放射性物質發(fā)生泄漏愕乎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一壁公、第九天 我趴在偏房一處隱蔽的房頂上張望感论。 院中可真熱鬧,春花似錦紊册、人聲如沸比肄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽芳绩。三九已至掀亥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間妥色,已是汗流浹背搪花。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留嘹害,地道東北人撮竿。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像吼拥,于是被迫代替她去往敵國和親倚聚。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,472評論 2 348

推薦閱讀更多精彩內容