LaTeX 命令的可選參數不能有方括弧么?
我的例子是
documentclass{article}
ewcommand{ estcmd}[2][abc]{(#1)(#2)}
egin{document}
estcmd[[123]]{test}
end{document}
結果是
([123)(])test
看樣子 #1 是 [123,#2 是 ]。這是怎麼回事?
可選參數的左右方括弧在外觀上是一對括弧,直觀上它應該滿足成對出現嵌套配對之類括弧所應該有的語義。但實際上左右方括弧是兩個模式匹配的定界符,TeX 找到第一個左方括弧和第一個右方括弧,分別作為它取參數的左右邊界。
——而與之相對,花括弧在 TeX 中真的是括弧,真的要滿足左右必須配對,嵌套內外有別之類的特性的。
這是什麼原因呢?我們可以剝開 LaTeX 的外衣,看看在下面 TeX 幹了什麼。必需參數所使用的花括弧,是 TeX 宏語言的一部分,是一種規定的語法。在 TeX 中一個必需參數是這樣定義的:
deffoo#1{使用參數#1}
這裡 foo#1 表示 foo 後面有一個參數。這裡並沒有指出花括弧,後面的參數,或者是一個字元,或者是一個宏,或者是一個用花括弧括起來的整體。花括弧是一種確定的語法,表示 TeX 的分組,當宏展開時要讀參數的時候遇到分組,就會把這個分組剝開,把分組的內容作為整個參數。
而用來定義可選參數的方括弧,實際只是 TeX 宏命令模式的一部分,不是特殊的語法。左右方括弧沒有特殊性,用兩個左括弧或別的什麼也是一樣的。實際上,可選參數是一種用宏構造出來的語法糖,在定義中是先預讀後面的字元,判斷字元是什麼,然後用模式匹配的宏定義。用 TeX 的語法寫,就是:deffoo{futurelet
extfooAux}
deffooAux{ifx
ext[%
expandafterfooOptArg
else
expandafterfooArg
fi}
deffooArg#1{使用必需參數#1}
deffooOptArg[#1]#2{使用可選參數#1,和必需參數#2}
下面可以用
foo{abc}
或者
foo[opt]{abc}
在上面的例子中:
- foo 用來執行 futurelet,預讀 foo 後面的一個字元,把這個字元保存在
ext 中,然後把控制權交給 fooAux。 - fooAux 則判斷剛才保存的預讀字元是不是左方括弧,如果是就說明後面是可選參數,把控制權交給 fooOptArg;如果不是就說明沒有可選參數,把控制權交給 fooArg。
- fooArg 就是一個普通的宏,接受一個參數。
- fooOptArg 是一個具有命令模式的宏,它接收兩個參數,前一個參數 #1 用兩個字元串即左右方括弧隔開,後一個參數是右方括弧後面的普通參數。對於 fooOptArg 來說兩個參數都是必需的,如果 fooOptArg 後面不是這個模式就會報錯。
因此,可選參數的魔法之處分別來自於 futurelet 的預讀、ifx 的判斷、以後面 fooOptArg 的模式匹配。在這裡,如果把 fooOptArg 的參數模式換成別的以 [ 開頭的形式,也是可以的。
LaTeX 的
ewcommand 內部實現比上面說得複雜一些,但原理是一樣的。
按我們前面的示意性定義,現在就可以分析
estcmd[[123]]{test}
到底發生了什麼了。
- 首先,TeX 使用 futurelet 預讀了 estcmd 後面的一個字元,保存了起來。
- TeX 檢查了保存的那個字元,經過比較,發現確實是左方括弧。把控制權交給一個處理可選參數的宏。
- 處理可選參數的宏的定義形如 fooOptArg[#1]#2,所以要進行模式匹配,也就是字元串查找啦。找到第一個左方括弧與第一個右方括弧之間的內容作為 #1,後面的作為 #2。所以我們看: estcmd [ [123 ] ] {test}
第一個左方括弧後面的第二個左方括弧與數字 [123 是第一個參數 #1,而第一個右方括弧後面的單個字元第二個右方括弧 ] 是第二個參數 #2。至於後面的 {test},根本沒有在這個宏展開的分析之列。
這就是出現原來題目中結果的原因。
那麼,可選參數里到底能不能用方括弧呢?能。用花括弧的分組把它包起來,這樣用就可以了:
estcmd[{[123]}]{test}
推薦閱讀:
※表示內積時,應該選擇leftlangle, left< 和 langle 中的哪一個?
※有啥辦法能讓listings去檢查源代碼是不是和tex文件里一樣,而不是直接把那幾行input進來?
※TeX Live 2015 有哪些值得關注的新特性?
※如何從零開始,入門 LaTeX?
※有沒有和writelatex(現在叫overleaf)一樣好用的本地tex IDE?如果沒有,為什麼沒有?
TAG:LaTeX |