編寫巢狀反引號的巨集

2021-09-13 08:18:19 字數 4909 閱讀 1048

當在common lisp中定義巨集的時候,常常會使用到反引號(`)。比方說,我有這麼乙個函式

(defun foobar ()

(+ 1 1)

(+ 2 3)

(+ 5 8))

它被呼叫後會返回最後乙個表示式的結果——13。如果我希望在第二個表示式計算後就把結果返回給外部的呼叫者的話,可以用return-from

(defun foobar ()

(+ 1 1)

(return-from foobar (+ 2 3))

(+ 5 8))

當然了,這屬於沒事找事,因為完全可以把最後兩個表示式放到乙個prog1(這也是沒事找事),或者直接點,把最後乙個表示式刪掉來做到同樣的效果——但如果是這樣的話這篇東西就寫不下去了,所以我偏要用return-from

還有乙個更加沒事找事的辦法,就是用macrolet定義乙個區域性的巨集來代替return-from——我很想把這個新的巨集叫做return,但這樣sbcl會揍我一頓,所以我只好把這個巨集叫做bye(叫做exit也會被揍)

(defun foobar ()

(macrolet ((bye (&optional value)

`(return-from foobar ,value)))

(+ 1 1)

(bye (+ 2 3))

(+ 5 8)))

如果我有另乙個叫做foobaz的函式

(defun foobaz ()

(+ 1 2)

(+ 3 4)

(+ 5 6))

也想要擁有bye這種想來就來想走就走的能力的話,可以依葫蘆畫瓢地包含乙個macrolet

(defun foobaz ()

(macrolet ((bye (&optional value)

`(return-from foobaz ,value)))

(+ 1 2)

(bye (+ 3 4))

(+ 5 6)))

好了,現在我覺得每次都需要在函式體內貼上乙份bye的實現**太麻煩了,想要減少這種重複勞作。於是乎,我打算寫乙個巨集來幫我複製貼上**。既然要定義巨集,那麼首先應當定義這個巨集的名字以及用法,姑且是這麼用的吧

(with-bye foobar

(+ 1 1)

(bye (+ 2 3))

(+ 5 8))

with-bye這個巨集需要能夠展開成上面的手動編寫的foobar中的函式體的**形式,那麼with-bye的定義中,就一定會含有macrolet的**,同時也就含有了反引號——好了,現在要來處理巢狀的反引號了。

這篇文章有個不錯的講解,各位不妨先看看。現在,讓我來機械化地操作一遍,給出with-bye的定義。首先,要確定生成的目標**中,那一些部分是可變的。對於with-bye而言,return-from的第乙個引數已經macrolet的函式體是可變的,那麼不妨把這兩部分先抽象為引數

(let ((name 'foobar)

(body '((+ 1 1) (bye (+ 2 3)) (+ 5 8))))

`(macrolet ((bye (&optional value)

`(return-from ,name ,value)))

,@body))

但這樣是不夠的,因為name是乙個在最外層繫結的,但它被放在了兩層的反引號當中,如果它只有乙個字首的逗號,那麼它就無法在外層的反引號求值的時候被替換為目標的foobar符號。因此,需要在,name之前再新增乙個反引號

(let ((name 'foobar)

(body '((+ 1 1) (bye (+ 2 3)) (+ 5 8))))

`(macrolet ((bye (&optional value)

`(return-from ,,name ,value)))

,@body))

如果你在emacs中對上述的表示式進行求值,那麼它吐出來的結果實際上是

(macrolet ((bye (&optional value)

`(return-from ,foobar ,value)))

(+ 1 1)

(bye (+ 2 3))

(+ 5 8))

顯然,這還是不對。如果生成了上面這樣的**,那麼對於bye而言foobar就是乙個未繫結的符號了。之所以會這樣,是因為

name在繫結的時候輸入的是乙個符號,並且

name被用在了巢狀的反引號內,它會被求值兩次——第一次求值得到符號foobar,第二次則是foobar會被求值

因此,為了對抗第二次的求值,需要給,name加上乙個字首的引號(『),最終效果如下

(let ((name 'foobar)

(body '((+ 1 1) (bye (+ 2 3)) (+ 5 8))))

`(macrolet ((bye (&optional value)

`(return-from ,',name ,value)))

,@body))

所以with-bye的定義是這樣的

(defmacro with-bye (name &body body)

`(macrolet ((bye (&optional value)

`(return-from ,',name ,value)))

,@body))

我大言不慚地總結一下,剛才的操作步驟是這樣的。首先,找出一段有規律的、需要被用巨集來實現的目標**;然後,識別其中的可變的**,給這些可變的**的位置起乙個名字(例如上文中的namebody),將它們作為let表示式的繫結,把目標**裝進同乙個let表示式中。此時,目標**被加上了一層反引號,而根據每個名字出現的位置的不同,為它們適當地補充乙個字首的逗號;最後,如果在巢狀的反引號**現的名字無法被求值多次——比如符號或者列表,那麼還需要給它們在第乙個逗號後面插入乙個引號,避免被求值兩次招致未繫結的錯誤。

就用上面所引用的文章裡的例子好了。有一天我覺得common lisp中一些常用的巨集的名字實在是太長了想要精簡一下——畢竟敲鍵盤也是會累的——假裝沒有自動補全的功能。我可能會定義下面這兩個巨集

(defmacro d-bind (&body body)

`(destructuring-bind ,@body))

(defmacro mv-bind (&body body)

`(multiple-value-bind ,@body))

顯然,這裡的**的寫法出現了重複模式,不妨試用按照機械化的操作手法來提煉出乙個巨集。第一步,先識別出其中可變的內容。對於上面這個例子而言,變化的地方其實只有兩個名字——新巨集的名字(d-bindmv-bind),以及舊巨集的名字(destructuring-bindmultiple-value-bind)。第二步,給它們命名並剝離成let表示式的繫結,得到如下的**

(let ((new-name 'd-bind)

(old-name 'destructuring-bind))

`(defmacro ,new-name (&body body)

`(,old-name ,@body)))

因為old-name處於巢狀的反引號中,但是它是由最外層的let定義的,所以應當添上乙個字首的逗號,得到

(let ((new-name 'd-bind)

(old-name 'destructuring-bind))

`(defmacro ,new-name (&body body)

`(,,old-name ,@body)))

最後,因為old-name繫結的是乙個符號,不能被兩次求值(第二次是在defmacro定義的新巨集中展開,此時old-name已經被替換為了destructuring-bind,而它對於新巨集而言是乙個自由變數,並沒有被繫結),所以需要有乙個單引號來阻止第二次的求值——因為需要的就是符號destructuring-bind本身。所以,最終的**為

(defmacro define-abbreviation (new-name old-name)

`(defmacro ,new-name (&body body)

`(,',old-name ,@body)))

試一下就可以確認這個define-abbreviation是能用的(笑

能夠指導編寫巨集的、萬能的、機械化的操作方法,我想應該是不存在的

Shell中的引號,反引號,雙引號,反斜槓

轉貼自 http hi.baidu.com kfeng21 blog item 3b3fcc3fc1bc85f2838b131e.html shell可以識別4種不同型別的引字符號 單引號字元 雙引號字元 反斜槓字元 反引號字元 1.單引號 howard 0 script grep susan ph...

shell中單引號 雙引號 反引號 反斜槓的區別

shell 可以識別4種不同型別的引字符號 單引號字元 雙引號字元 反斜槓字元 反引號字元 1.單引號 grep susan phonebook susan goldberg 403 212 4921 susan topple 212 234 2343 如果我們想查詢的是susan goldberg...

shell中單引號 雙引號 反引號 反斜槓的區別

url shell 可以識別4種不同型別的引字符號 單引號 字元 雙引號字元 反斜槓字元 反引號字元 color red b 1.單引號 b color grep susan phonebook susan goldberg 403 212 4921 susan topple 212 234 234...