
70
|
第二章
而這個做法失敗的原因則在於
state
只有在
Block
仍有效時才有定義,因為
Block
指
令 採 用 動 態 變 數 範 圍(dynamic scoping), 但 閉 包 需 要 的 是 詞 法 變 數 範 圍(lexical
scoping),你可能記得
Module
指令採用的便是詞法變數範圍,說不定使用
Module
指令
即可成功。
看起來似乎成功了,但缺點在於如果直接執行
counter
時,會出現下列結果。
[Page-070]
閉包中的
state
變數以
state$<n>
模擬,這是因為
Module
指令會在同名稱的全域變數已
存在的情況下,自動建立新變數,但新變數並沒有保護措施,使用者可以隨時隨地改變
它,這個缺陷讓使用
Module
指令的實作變得沒這麼優雅,而且此缺陷在 JavaScript 與
Lisp 語言中完全不會出現。
因此在本文的程式碼中使用了另一個策略,利用
HoldAll
屬性建立一個容器作為閉包的
詞法環境。因為變數與函數以未執行的形式儲存,所以不必擔心是否有全域變數使用了
相同的變數名稱。當閉包需要進行求值時,
evaluate
函數會使用
Block
環境建立區域變
數或函數,接著便將這些區域定義和已存數值的變數及函數進行連接,並呼叫適當的區
域函數。
實際上在 Mathematica 中實作閉包有何應用價值呢?計數器這種應用似乎太過於簡單,
但即便是計數器應用也能展現出一些重要的功能;首先,如果我們用全域變數做為計數
器,很可能會不小心存取到它,以閉包方式將計數器封裝,可限制其存