iT邦幫忙

0

Javascript SICP 3.2 - The Environment Model of Evaluation (完整解釋什麼是closure)

圖片皆摘錄於 https://www.comp.nus.edu.sg/~cs1101s/sicp/

前言:

經過3.1賦值之後,光用“名字”對應值已經不再適用了,需要一個“位置”去儲存這些值,這些“位置”會維持在一個結構,稱為“environments”

Environment, frame, binding

environments 就是一個 “frame”的序列,而每個 frame 是一個 “binding”的表格(可能為空),binding將變量與值對應起來,一個frame中的binding最多只會對應一個變量,如果變量在任何frame中都沒有定義binding,就稱為“unbound”。
https://ithelp.ithome.com.tw/upload/images/20190510/20117516nnjCyAuUtV.png

Figure 3.1 有三個 frame I, II, III,觀察x,在每個frame裡面的值 I: 3, II:7, III: 3
可看到 x 在 frame III中並沒有 binding, ,所以沿著D回去找發現 frame I有x的binding,因此x在frame II中的值與frame I中相同。

The Rules for Evaluation

  1. Evaluate the subexpressions of combination 對組合式的子表達式求值
  2. Apply the value of the function subexpression to the values of the argument subexpressions. 將子表達式function的值應用在子表達式的參數上

其實跟1.1.3說的是一樣的事情,但這次用environment model的求值方式,不一樣的是,會將compound function帶入argument。

Function definition

在environment model中,一個function是一個pair,一邊指向程式碼與指向environment的pointer所組成,只有一個方法能創造function: by evaluating a function definition expression。

funciton square(x) {
    return x * x;
}

其實是以下方式的syntactic sugar:

const square = x = x * x; 

https://ithelp.ithome.com.tw/upload/images/20190510/20117516qJnzatUsmS.png

Function evaluating

https://ithelp.ithome.com.tw/upload/images/20190510/201175160YHKZdCoTK.png

當對square(5)求值

  1. 創造新的Environment E1,且有x變數在初始的frame中,bound to 5,pointer指向global environment.

  2. square在E1對body進行求值,回傳xx,x值已綁定5,55=25。

Applying Simple Functions

function square(x) {
    return x * x;
}

function sum_of_squares(x, y) {
    return square(x) + square(y);
}

function f(a) {
    return sum_of_squares(a + 1, a * 2);
}

https://ithelp.ithome.com.tw/upload/images/20190510/20117516pvCBoUJSZp.png

https://ithelp.ithome.com.tw/upload/images/20190510/20117516IoUi9xYFOA.png

其實就是上一個段落的複雜版,只要記得對一個function求值的規則就對了!另一個有趣的地方就是,square(x) + square(y)其實是創造了兩個environment各自有自己的binding。

Frames as the Repository of Local State

利用Environment來分析3.1.1的 “withdrawal processor”

function make_withdraw(balance) {
    function withdraw(amount) {
        if (balance >= amount) {
            balance = balance - amount;
            return balance;
        } else {
            return "Insufficient funds";
        }
    }
    return withdraw;
}

在加入

const w1 = make_withdraw(100);

在對其求值
w1(50);

以environment model來解析:
https://ithelp.ithome.com.tw/upload/images/20190510/20117516hIUrqKEamc.png

const w1 = make_withdraw(100); 開始就非常有趣了
https://ithelp.ithome.com.tw/upload/images/20190510/20117516SDeL0IBmIe.png

首先一樣新建一個E1環境且存在一個參數balance綁定100,接著對make_withdraw求值,產生了一個新的function object,且被w1綁定在global環境中。

求值 w1(50);
https://ithelp.ithome.com.tw/upload/images/20190510/20117516OfJAGBKrZg.png

同樣新建了一個環境,包含amount bound to 50,但這個環境的pointer是指向E1而不是global環境,而在這個環境中對function的body求值,求值後如下圖

https://ithelp.ithome.com.tw/upload/images/20190510/20117516TMA1oWC1Mk.png

求值後由於function body含有一個assignment balance = balance — amount ,執行後改變了E1中的 balance 變成50,且function object w1依然指向E1,原先的amout binding的frame已經不復存在,下次再求值w1,就又會產生新的frame、binding amount to 新的值,且指向E1。

如果再定義新的 w2 = make_withdraw(100); 呢?
https://ithelp.ithome.com.tw/upload/images/20190510/20117516eeLB3hAgkI.png

將產生一個新的environment E2,其綁定著balance = 100,w2的指針一面指向E2、一面指向與w1相同的function。

最好玩的部分就是寫習題惹~
https://ithelp.ithome.com.tw/upload/images/20190510/20117516QI4C34QlvW.png
https://ithelp.ithome.com.tw/upload/images/20190510/20117516jap3EadHFU.png
手畫了一個,有點醜XD
https://ithelp.ithome.com.tw/upload/images/20190510/20117516Y5YSexCfkE.png

Internal Definitions

function abs(x) {
    return x >= 0 ? x : -x;
}
                
function square(x) {
    return x * x;
}
                
function average(x,y) {
    return (x + y) / 2;
}
              
function sqrt(x) {
    function good_enough(guess,x) {
        return abs(square(guess) - x) < 0.001;
    }
    function improve(guess) {
        return average(guess,x/guess);
    }
    function sqrt_iter(guess){
        if (good_enough(guess,x)) {
            return guess;
        } else {
            return sqrt_iter(improve(guess));
        }
    }
    return sqrt_iter(1.0);
}

sqrt(5);

https://ithelp.ithome.com.tw/upload/images/20190510/20117516MvU2EODFWh.png

當 sqrt(2) 執行,創造了一個E1環境,當中的binding 有x 及一些function object,接著對 sqrt_iter(1.0) 求值,創造了E2環境,接著對 good_enough 求值,又創造了E3環境,雖然 sqrt_iter 與 good_enough 的參數都叫做guess ,但其實是創造在不一樣的環境裡的不同變數。

總結來說environment model提供了一些技巧:

  1. function內部的名稱不會與外部環境名稱相互干擾

  2. function 內部名稱可以直接使用外部環境的,只要單純的將變數是free variables就好


尚未有邦友留言

立即登入留言