Scheme 中的 quasiquote, unquote, unquote-splicing

先談談 Scheme 中的 Quote

每次我向人介紹 Scheme 的時候,我總是喜歡跟人說 Scheme 語法只有一個:

(函數名稱 參數1 參數2 參數n...)

在 scheme 如果不是數字或是文字,則會被當成 symbol ,而 symbol 簡言就是被當成某個內容的名稱,像是函數的命名、變數的命名都被稱為 symbol。

只要遇到 symbol ,也就是一個像是變數名稱或是函數名稱的文字,Scheme 都會嘗試去找找看有沒有對應的變數值或是函數。

而今天 Scheme 是可以用同一個語法去表達資料的,所以當你想把同樣語法的東西當成一個 list 或者是你不想讓某個 symbol 被 scheme 當成函數或者變數的時候,此時就能用 ' 放在 symbol 或 list 前面。

如:

'a ;會被當成一個 atom
'(1 2 3) ;當成一個 list

如果上述的例子都沒有加上 ' 的話,scheme 就會去看看有沒有 a 這個變數,而 1 也會被誤會成是一個函數,開始進行尋找是不是有跟 a 和 1 能夠配對的變數值以及函數內容

補充一下,其實 ' 符號只是 quote 函數的語法蜜糖而已,實際上它會包裝回 (quote a) 和 (quote (1 2 3)) 是不是看起來就跟原本我說的語法無差異呢?不管是程式或者是資料,都能以函數的方式包裝起來,所以 lisp 才被認為是程式與資料共構的語言。

再稍微補充一點,或許有人會產生一個疑問,'(1 2 3)(list 1 2 3) 到底是差在哪裡? 其實這個狀況換個例子更容易讓人明瞭,如果今天是 '(a b c)(list a b c) 前者基本上就是會保留成 ( a b c ) 的樣子,應為 quote 是表示裡面任何一個元素都不要去動他,保持符號原本的模樣即可。但是後者則會開始被搜尋是否有對應的 a b c 符號的內容,如果發現沒有對應的內容,則會出現錯誤。

以上就是基本 quote 的介紹。

Quasiquote 是什麼?

基本上 quosiquote 在 scheme 裡面是用 ` 這個符號來表示的,這是在鍵盤上與 ~ 同一個位置的那個符號。這個符號的功能與 quote 幾乎沒有什麼分別。如果單純使用 quote ,則裡頭所有的 symbol 和計算式都不會被計算或是轉換。舉個例子如下:

'(a (+ 1 2))
=> (a (+ 1 2)) ; 原封不動的還給你

而上面的例子裡,仔細看可能會覺得我想將裏頭的 (+ 1 2) 給計算出來。如果要達到這個目的則需要 ` (quasiquote) 以及 , (unquote) 這兩個特殊的符號來幫忙。

* \` (quasiquote):與 quote 無異,但是期待裡頭會有個 unquote
* , (unquote): 顧名思義,無視 quote 規則,會算出 unquote 後面的數值

因此上面的例子會變成下面的狀況:

`(a ,(+ 1 2))
=> (a 3) ; unquote 後面的計算式被算出一個數值來了

那什麼是 unquote-splicing 呢?

在 unquote 遇到計算的結果是個 list 的時候,會原封不動的將 list 放在裡面。如果想要將 unquote 的 list 給拆掉,則需要使用 ,@ (unquote-splicing) 來幫忙。 舉例如下:

`(a ,(list 'b 'c))
=> (a (b c)) ; 明顯與我想要的結果 (a b c) 不同

`(a ,@(list 'b 'c))
=> (a b c)

以上就是 quasiquote, unquote, unquote-splicing 的介紹。 補充一下,上面的這些符號跟 quote 一樣都只是一個蜜糖,因此 `(a ,(+ 1 2) 是會被轉換回 (quasiquote (a (unquote (+ 1 2))) 如此的形式,依然符合我一開始所說的 scheme 語法‧