[Emacs] Emacs之魂(八):反引用與嵌套反引用

1. 反引用

上文我們介紹了如何使用defmacro定義宏蒙畴,

(defmacro inc (var)
    (list 'setq var (list '1+ var)))

我們定義了inc宏受楼,(inc x)會(huì)被展開(kāi)為(setq x (1+ x))转唉,因此,

(defvar x 0)
(inc x)

x    ; 1

宏做的是語(yǔ)法對(duì)象的變換操作贩绕,因此幾乎每個(gè)宏最后都返回一個(gè)列表火的,
可是,類似上述inc宏那樣丧叽,每次都使用list來(lái)創(chuàng)建列表卫玖,是一件麻煩的事情,
所以踊淳,Lisp提供了反引用(quasiquote/backquote)假瞬,可以便捷的生成列表。

例如迂尝,以上inc宏使用反引用來(lái)生成列表脱茉,可以修改為,

(defmacro inc (var)
    `(setq ,var (1+ ,var)))

可以看到垄开,反引用``(setq ,var (1+ ,var)))(inc x)的展開(kāi)式(setq x (1+ x))非常相像琴许, 我們只需要將反引號(hào)` 去掉,然后將反引用表達(dá)式中的逗號(hào)表達(dá)式,var溉躲,替換為var綁定的值x`即可榜田。

2. 反引用表達(dá)式的求值規(guī)則

下面我們通過(guò)幾個(gè)例子來(lái)說(shuō)明反引用的使用方式益兄,其中=>表示“求值為”。

求值規(guī)則:
(1)如果反引用表達(dá)式中不包含逗號(hào),箭券,那么它和引用表達(dá)式是一樣的净捅,
因此反引用通常被看做是一種特殊的引用(quote)

`(a list of (+ 2 3) elements)
=> (a list of (+ 2 3) elements)

(2)反引用表達(dá)式中的逗號(hào)表達(dá)式會(huì)被求值

`(a list of ,(+ 2 3) elements)
=> (a list of 5 elements)

(3)反引用表達(dá)式中的,@表達(dá)式,也會(huì)被求值辩块,但是要求其結(jié)果必須是一個(gè)列表蛔六,
,@會(huì)去掉列表的括號(hào),將列表中的元素放到,@表達(dá)式出現(xiàn)的位置

(defvar x '(2 3))

`(1 ,@x 4)
=> (1 2 3 4)

`(1 ,@(cdr '(1 2 3)) 4)
=> (1 2 3 4)

3. 生成宏定義的宏

以上废亭,我們定義了宏inc国章,
宏調(diào)用(inc x),會(huì)被展開(kāi)為(setq x (1+ x))豆村。

在編寫(xiě)宏的時(shí)候液兽,一個(gè)常用的思路是,
先考慮展開(kāi)關(guān)系你画,即我們期望將A展開(kāi)為B抵碟,再根據(jù)這個(gè)線索編寫(xiě)相應(yīng)的宏

那么坏匪,我們可否編寫(xiě)一個(gè)宏,讓它展開(kāi)成(defmacro ...)呢撬统?
是可以的适滓,這是一種展開(kāi)為宏定義的宏,它可以作為defmacro來(lái)使用恋追。

考慮展開(kāi)關(guān)系凭迹,我們期望將(create-inc)展開(kāi)為

(defmacro inc (var) 
    `(setq ,var (1+ ,var)))

于是,宏create-inc就應(yīng)該被這樣定義苦囱,

(defmacro create-inc ()
    `(defmacro inc (var)
        `(setq ,var (1+ ,var))))

我們來(lái)試驗(yàn)一下嗅绸,

(create-inc)    ; 定義了inc

(defvar x 0)
(inc x)    ; 使用inc

x    ; 1

我們還可以給create-inc加上參數(shù)。
考慮展開(kāi)關(guān)系撕彤,我們將(create-inc-n y)展開(kāi)為鱼鸠,

(defmacro inc-n (var)
    `(setq ,var (+ y ,var)))

那么create-inc-n應(yīng)該怎么定義呢?事實(shí)上羹铅,

(defmacro create-inc-n (num)
    `(defmacro inc-n (var)
        `(setq ,var (+ ,',num ,var))))

第一次看到,',num的時(shí)候蚀狰,我非常驚訝,這到底是什么职员?

4. 嵌套反引用

嵌套反引用指的是麻蹋,一個(gè)反引用表達(dá)式中嵌套出現(xiàn)了另一個(gè)反引用表達(dá)式。
在生成宏定義的宏中焊切,嵌套反引用經(jīng)常出現(xiàn)扮授。

嵌套反引用表達(dá)式中芳室,經(jīng)常會(huì)出現(xiàn)類似,',num這樣的表達(dá)式,
它不能被寫(xiě)成,num刹勃,也不能被寫(xiě)成,,num堪侯,下面我們進(jìn)行仔細(xì)的分析。

(1),num為什么不正確

先看一下展開(kāi)關(guān)系深夯,我們期望將(create-inc-n y)展開(kāi)為抖格,

(defmacro inc-n (var)
    `(setq ,var (+ y ,var)))

即,嵌套反引用表達(dá)式咕晋,應(yīng)該按下述方式求值雹拄,

`(defmacro inc-n (var)
    `(setq ,var (+ ,',num ,var))))

=> (defmacro inc-n (var)
    `(setq ,var (+ y ,var)))

其中,,var是不應(yīng)該被求值的掌呜,因?yàn)檫@是內(nèi)層反引用需要的滓玖,
如果我們將,',num寫(xiě)成,num,那么它就和,var一樣不會(huì)被求值了质蕉,

`(defmacro inc-n (var)
    `(setq ,var (+ ,num ,var))))

=> (defmacro inc-n (var)
    `(setq ,var (+ ,num ,var)))

這和我們期望的展開(kāi)關(guān)系不同势篡。

(2),,num為什么不正確

寫(xiě)成,,num在求值最外層反引用表達(dá)式的時(shí)候,確實(shí)會(huì)求值num的值模暗,
但是禁悠,在求值內(nèi)層反引用表達(dá)式的時(shí)候,這個(gè)值還會(huì)被再求值一次兑宇。

(create-inc-n y)將被展開(kāi)為碍侦,

`(defmacro inc-n (var)
    `(setq ,var (+ ,,num ,var)))

=> (defmacro inc-n (var)
    `(setq ,var (+ ,y ,var)))

可是,在進(jìn)行宏調(diào)用(create-inc-n y)的時(shí)候隶糕,我們不應(yīng)該關(guān)心y的值是什么瓷产,
因?yàn)樵诤暾归_(kāi)階段,y可能還沒(méi)有值枚驻。

而且濒旦,該展開(kāi)式和我們預(yù)期的展開(kāi)結(jié)果也不相同。

(3),',num是怎么來(lái)的

綜上分析再登,我們需要在外層反引用表達(dá)式被求值的時(shí)候尔邓,求值num
而在內(nèi)層反引用表達(dá)式被求值的時(shí)候霎冯,不再繼續(xù)求值num的值铃拇,
因此,我們需要給num的值加上一個(gè)引用來(lái)“阻止”求值沈撞。

因此慷荔,(create-inc-n y)會(huì)被展開(kāi)為,

`(defmacro inc-n (var)
    `(setq ,var (+ ,',num ,var))))

=> (defmacro inc-n (var)
    `(setq ,var (+ ,'y ,var)))

而內(nèi)層反引用表達(dá)式被求值的時(shí)候,,'y將求值為y显晶。

所以贷岸,(inc-n x)將被展開(kāi)為

`(setq ,var (+ ,'y ,var))
=> (setq x (+ y x))

和我們期望的展開(kāi)結(jié)果相同。

5. 嵌套反引用的求值規(guī)則

在生成宏定義的宏中磷雇,經(jīng)常會(huì)出現(xiàn)嵌套反引用偿警,
如果我們定義了另一個(gè)宏other-macro來(lái)生成create-inc-n的定義,

(defmacro other-macro ()
    `(defmacro create-inc-n (num)
        `(defmacro inc-n (var)
            `(setq ,var (+ ,',num ,var)))))

那么唯笙,將出現(xiàn)三層嵌套反引用螟蒸。
不過(guò),不用擔(dān)心崩掘,嵌套反引用也是有求值規(guī)則的七嫌,以下我們用兩層嵌套反引用作為例子來(lái)說(shuō)明。

求值規(guī)則:
(1)嵌套反引用被求值的時(shí)候苞慢,一次求值诵原,只去掉一層反引用,內(nèi)層反引用不受影響挽放,

`(defmacro inc-n (var)
    `(setq ,var (+ ,',num ,var))))

=> (defmacro inc-n (var)
    `(setq ,var (+ ,'y ,var)))

(2)嵌套反引用表達(dá)式中的逗號(hào)表達(dá)式绍赛,是否被求值,要根據(jù)情況來(lái)定辑畦,
如果最外層嵌套反引用總共有n層吗蚌,那么一定不會(huì)出現(xiàn)包含大于n個(gè)逗號(hào)的表達(dá)式,
且包含逗號(hào)數(shù)目小于n的表達(dá)式不會(huì)被求值纯出,只有逗號(hào)數(shù)目等于n的表達(dá)式才會(huì)被求值褪测。

`(defmacro inc-n (var)
    `(setq ,var (+ ,',num ,var))))

=> (defmacro inc-n (var)
    `(setq ,var (+ ,'y ,var)))

最外層嵌套反引用總共有n=2層,
,var表達(dá)式包含一個(gè)逗號(hào)潦刃,1<n,不會(huì)被求值懈叹,
,',num表達(dá)式包含兩個(gè)逗號(hào)乖杠,2=n,會(huì)被求值澄成。

(3)被求值的逗號(hào)表達(dá)式胧洒,其求值方式是,
去掉最右邊的一個(gè)逗號(hào)墨状,然后將表達(dá)式替換成它的值卫漫。

`(defmacro inc-n (var)
    `(setq ,var (+ ,',num ,var))))

=> (defmacro inc-n (var)
    `(setq ,var (+ ,'y ,var)))

,',num,去掉最右邊的逗號(hào),'num肾砂,然后將num替換成它的值y列赎,
于是得到了,'y

參考

GNU Emacs Lisp Reference Manual
ANSI Common Lisp
On Lisp
Let Over Lambda

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末镐确,一起剝皮案震驚了整個(gè)濱河市包吝,隨后出現(xiàn)的幾起案子饼煞,更是在濱河造成了極大的恐慌,老刑警劉巖诗越,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件砖瞧,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡嚷狞,警方通過(guò)查閱死者的電腦和手機(jī)块促,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)床未,“玉大人竭翠,你說(shuō)我怎么就攤上這事〖磁穑” “怎么了逃片?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)只酥。 經(jīng)常有香客問(wèn)我褥实,道長(zhǎng),這世上最難降的妖魔是什么裂允? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任损离,我火速辦了婚禮,結(jié)果婚禮上绝编,老公的妹妹穿的比我還像新娘僻澎。我一直安慰自己,他們只是感情好十饥,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布窟勃。 她就那樣靜靜地躺著,像睡著了一般逗堵。 火紅的嫁衣襯著肌膚如雪秉氧。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,370評(píng)論 1 302
  • 那天蜒秤,我揣著相機(jī)與錄音汁咏,去河邊找鬼。 笑死作媚,一個(gè)胖子當(dāng)著我的面吹牛攘滩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播纸泡,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼漂问,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起级解,我...
    開(kāi)封第一講書(shū)人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤冒黑,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后勤哗,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體抡爹,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年芒划,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了冬竟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡民逼,死狀恐怖泵殴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情拼苍,我是刑警寧澤笑诅,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站疮鲫,受9級(jí)特大地震影響吆你,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜俊犯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一妇多、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧燕侠,春花似錦者祖、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至茫舶,卻和暖如春烂瘫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背奇适。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留芦鳍,地道東北人嚷往。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像柠衅,于是被迫代替她去往敵國(guó)和親皮仁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容