基於文本替換的解譯器:引入continuation

來源:互聯網
上載者:User

當我寫到這裡的時候,我自己都吃了一驚。 環境、儲存這些比較讓人耳熟的還沒講到,continuation先出來了。 維基百科裡對continuation的翻譯是“延續性”。 這翻譯看著總有些違和感而且那個條目也令人不忍直視。 總之continuation似乎沒有好的中文翻譯,彷彿中國的電腦科學裡沒有continuation這個概念似的。

Continuation這個概念相當於過程式語言裡的函數調用棧。 它是用於儲存“現在沒空處理,待會再處理的事”的資料結構。 這樣說有點抽象,舉個例子,函數應用那條運算式的求值過程(call-by-value)是這樣: \begin{equation*}\begin{array}{lcl} eval((M \; N)) &=& eval(L[X \leftarrow eval(N)]) \\ && \text{其中} eval(M) = \lambda X.L \end{array}\end{equation*}這個過程依次做這三件事:

1.計算$eval(M)$,結果是$\lambda X.L$,
2.計算$eval(N)$,
3.做替換$L[X \leftarrow eval(N)]$。
解譯器一次只能計算一個運算式,所以當它計算$eval(M)$前,要先把第二步和第三步要做的事備忘到continuation。

假設$M$還是個函數調用$M=(M_1 \; M_2)$。 那麼,計算$eval(M)$又要分三步: 先計算$eval(M_1)=\lambda X.L_1$,第二步計算$eval(M_2)$,第三步做替換$L_1[X \leftarrow eval(M_2)]$。 解譯器只能先計算$eval(M_1)=\lambda X.L_1$,將第二步計算$eval(M_2)$,第三步做替換$L_1[X \leftarrow eval(M_2)]$備忘到continuation。 於是,continuation備忘的事情按待處理的順序依次是:

1.計算$eval(M_2)$,
2.做替換$L_1[X \leftarrow eval(M_2)]$。
3.計算$eval(N)$,
4.做替換$L[X \leftarrow eval(N)]$。
可以看到,continuation是一個FILO(先進後出)的資料結構,也就是棧。

在之前的解譯器裡並沒有提到continuation,但是解譯器仍然能正常工作, 這是因為解釋這個解譯器的解譯器——Racket解譯器——幫我們管理了continuation。 這一節的目標是自己管理continuation。

從一個簡單的遞迴函式做起

先說一個概念:尾調用。 假設一個函數體中有一句函數調用運算式$(f \; n)$,這個函數調用被稱為尾調用,如果這條運算式是函數體裡最後求值的運算式。 如$\lambda n.(f \; n)$,和$\lambda n.({if} \; ({iszero} \; n) \; 0 \; (f \; n))$中的$(f \; n)$都是尾調用。 而$\lambda n.(+ \; 1 \; (f \; n))$中的$(f \; n)$不是尾調用,因為計算完$(f \; n)$後還要再算個加法。 一個簡單的判斷是否尾調用的方法是看這條調用運算式是不是不在一個參數位置(let運算式和if運算式先做宏展開)。

學過C或者C++都知道,調用函數的時候是要儲存資訊到函數調用棧的(我上學時學的這倆,不知道其他語言的課上會不會講函數調用棧)。 尾調用的特點是:尾調用理論上不需要儲存資訊到函數調用棧——實際上也不需要,但是有些語言就不支援,比如說Python。 換句話說,尾調用不會導致continuation增長。 這個很好理解,因為尾調用是最後求值的運算式了,不需要備忘後面要做的事(後面沒有事了),所以也就不會導致continuation增長。 後面會從continuation的角度說明為什麼尾調用不會令continuation增長。

題外話: 剛才提到“理論上”和“實際上”。 經常能聽到這種句式:理論上怎麼怎麼,可惜,實際上是吧啦吧啦。 其中“吧啦吧啦”是一句對“怎麼怎麼”的否定。 我覺得這是反科學以及讀書無用論的潛意識在作祟,另外有時候是用來逃避責任的借口。 理論上怎麼樣,實際上就應該怎麼樣。 如果有套理論的結論和現實的結果不同,那麼這就不能稱為理論,最多算個猜想,而且是個錯誤的猜想。

如果一個尾調用是一個遞迴調用,那麼就稱為尾遞迴。 尾遞迴又叫迭代。 我們現在要做的事其實就是把一個遞迴程式(之前寫的解譯器)中的非尾遞迴全改成尾遞迴, 也就是遞迴轉迭代。

為了熟悉遞迴轉迭代的套路,先拿一個簡單的遞迴函式練練手。 這個函數就是之前用到的double函數: \begin{equation*}\begin{array}{lcl} double(0) &=& 0 \\ double(n) &=& 2 + double(n-1), \text{其中} n \neq 0 \end{array}\end{equation*} 計算double函數的過程有一個狀態量:當前計算的運算式$double(n)$或者一個數字$n$(計算結果)。 現在加入另一個狀態量continuation,記為$\kappa$(因為continuation第一個字母c發音k,所以用了個長的像k的希臘字母……)。 計算double函數的過程只有第二行是遞迴過程,備忘的事是加2。 $\kappa$定義為: \begin{equation*}\begin{array}{lcl} \kappa &=& {mt} \\ &|& \left<\kappa, +2\right> \end{array}\end{equation*} mt表示空的continuation,也就是說沒有後續的事情了。

加入continuation後的求值過程如下: \begin{equation*}\begin{array}{lcl} \left<double(0), \kappa\right>_v &\rightarrow_{v}& \left<0, \kappa\right>_c \\ \left<double(n), \kappa\right>_v &\rightarrow_{v}& \left<double(m), \left<\kappa, +2\right>\right>_v \\ && \text{其中} n \neq 0, m = n - 1 \\ \left<n, \left<\kappa, +2\right>\right>_c &\rightarrow_{c}& \left<m, \kappa\right>_c \\ && \text{其中} m = n + 2 \\ \left<n, {mt}\right>_c &\rightarrow_{c}& \text{輸出} n \end{array}\end{equation*} 下面解釋一下。 計算開始時的狀態表示為$\left<double(n), {mt}\right>_v$。 下標v表示第一個狀態量是一個運算式,要對這個運算式求值。 用箭頭$\rightarrow$表示一步,帶下標v的箭頭$\rightarrow_v$表示這一步對錶達式求值, 帶下標c的箭頭$\rightarrow_c$表示這一步從continuation中取出一件備忘的事執行。 對於$n \neq 0$,$\left<double(n), \kappa\right>_v$的下一步要做的是,計算$double(n-1)$,同時將加2備忘到$\kappa$, 也就是$\left<double(m), \left<\kappa, +2\right>\right>_v$,其中$m=n-1$。 當$n = 0$時,$double(0)$求得值$0$,所以$\left<double(0), \kappa\right>_v \rightarrow_{v} \left<0, \kappa\right>_c$。 用下標c表示第一個狀態是一個數字,下一步該從continuation取出備忘的事來辦了。 最後是$\left<n, \kappa\right>_c$的情況,如果$\kappa$不是空:$\kappa=\left<\kappa',+2\right>$,就加2到$n$上; 如果$\kappa$是空:$\kappa={mt}$,說明continuation沒事了,而這時運算式也求完了,於是返回最終結果$n$。

然後是寫代碼。 針對$\rightarrow_v$和$\rightarrow_c$需要兩個函數。 函數value-of/k是$\rightarrow_v$(Lisp命名規範裡一般用斜杠表示with的意思)。 函數apply-cont是$\rightarrow_c$。

本欄目更多精彩內容:http://www.bianceng.cnhttp://www.bianceng.cn/Programming/project/

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.