赫塔菲比利亚雷尔:LaTeX技巧894:多個 \expandafter 的展開過程是怎樣的?

雖然宏展開這方面有更好的解決方案,如etoolbox和LaTeX3,但是想把底層這些東西弄懂 就舉當前CTAN上的ctex1.02d里的一個替換宏名的例子:
 巴萨vs赫塔菲全场录像 www.vsttod.com.cn \def\[email protected]#1#2#3{%
  \expandafter\expandafter\expandafter\let\expandafter
    \csname #1#3\expandafter\endcsname
    \csname #2#3\endcsname
  \expandafter\expandafter\expandafter\def\expandafter
    \csname #2#3\expandafter\endcsname
    {\csname #1#3\endcsname}
}
回復: 作者:劉海洋 其實本來沒那么復雜,那個宏寫得不好,只不過后來沒有人較真,一直那么用而已。正確的寫法是:
\def\replacecommand#1#2#3{%
  \expandafter\let
    \csname #1#3\expandafter\endcsname
    \csname #2#3\endcsname
  \expandafter\def
    \csname #2#3\endcsname
    {\csname #1#3\endcsname}%
}
并不需要多重 \expandafter。上述定義可以分成形式類似的獨立的兩組,相互展開互不干涉。第一組中:
\expandafter\let
  \csname #1#3\expandafter\endcsname
  \csname #2#3\endcsname
第一個 \expandafter 保證先展開 \csname 后進行 \let;第二個 \expandafter 保證先展開第二組 \csname ... \endcsname,再完成第一組 \csname ... \endcsname。于是兩組 \csname ... \endcsname 都完成了才進行 \let 的賦值,效果就是(etoolbox 中的)\csletcs{#1#3}{#2#3}。后一組代碼類似,效果是 \csdef{#2#3}{\csname#1#3\endcsname}。 原來的宏寫得更復雜了,多寫了幾個 \expandafter,就多了幾個展開步驟。 多重 \expandafter 的用處還是改變展開次序,不過就是人肉解釋起來更累一點而已。 以前多余的寫法可能是來自一個展開定式:
\expandafter\expandafter\expandafter\A\expandafter\B\C
它的效果是先展開 \C,然后是 \B,最后是 \A。我們首先來分析一下這個定式:
  1. 展開第一個 \expandafter,于是按定義,我們知道效果是第二個 \expandafter 后面跟上「第三個 \expandafter 的展開」。即
    ②\expandafter①\expandafter\A\expandafter\B\C
    其中用數字①②標出展開順序。
  2. 下面先展開前面①標出的 \expandafter,按定義,知道效果是 \A 后面跟上「最后一個 \expandafter 的展開」。即變成:
    ②\expandafter\A①\expandafter\B\C
  3. 然后 \expandafter\B\C 展開一步你已經懂了,相當于先展開 \C 然后前面放上 \B。所以得到:
    ②\expandafter\A\B①\C
    即
    \expandafter\A\B「\C的展開」
  4. 然后又展開 \expandafter\A\B,就是先展開 \B,前面再放上 \A。
最后總結一下,我們就知道,這個展開定式:
\expandafter\expandafter\expandafter\A\expandafter\B\C
的效果就是先展開 \C,然后 \B,最后 \A。展開次序與排列次序相反。 回到原來 ctex 宏包的例子,它的形式其實是:
\expandafter\expandafter\expandafter\let\expandafter
  \csname foo\expandafter\endcsname
  \csname bar\endcsname
最前面部分不就是我們講的定式么?效果是先展開 foo 中的 f,然后展開 \csname,最后展開 \let。展開 f 并沒有什么可展的,展開后還是 f;而到 \csname 的部分,按 \csname ... \endcsname 的語義,則需要向后展開到 \endcsname 為止——此時 \endcsname 之前的 \expandafter 起效果,就進入下一個部分了。后面的分析和最前面的簡化代碼其實一樣。
分析 ctex 原來的例子就可以看到,舊的代碼會最先展開 \csname 后面的內容(上面的分析是 f,原例子是 #1 傳參的結果中的第一個 token),這當然是多余的做法。實際上,只需要保證 \let 在兩個 \csname 之后被展開生效就足夠了——這也是一開始簡化代碼做的事。
關于 \csname 的語義,讀 TeXbook 第 7 章(或 TeX by Topic 相關章節)有解釋:對 \csname <tokens> \endcsname 的展開,是完全展開 <tokens> 到底,留下里面的字符部分,然后把這些字符生成一個宏。這對于理解上面的分析是有益的。這就是整個過程的詳細分析,希望你沒有被嚇到。 -------------------------------------------- 為了檢驗你已經理解了 \expandafter 的語義和上面說的逆轉 3 個 token 展開次序的定式,你可以再試著理解一下這段代碼:
\let\ep\expandafter % 簡化下面的記號

\ep\ep\ep\ep\ep\ep\ep\A
\ep\ep\ep\B
\ep\C
\D
第一行就是 7 個 \expandafter,有點嚇人是么?注意 \expandafter 是跳著生效的,所以上面的代碼的一輪展開之后,就變成了
\ep\ep\ep\A
\ep\B
\C
「\D 的展開」
是不是有點眼熟?所以其實就是把 \A\B\C\D 這 4 個記號的展開順序逆轉一遍。 現在你可以考慮:如果要逆轉 5 個記號 \A\B\C\D\E 的展開順序,一共需要用幾個 \expandafter?很有規律性不是么? 最后推介 TUGboat 1988 年的一篇很早的文章,叫《A Tutorial on \expandafter》,希望你會喜歡:tug.org/TUGboat/tb09-1/
作者:孟晨
答案分成兩個部分。 第一個部分講怎么看:怎樣判斷一堆 \expandafter 修飾的代碼的展開順序; 第二個部分講怎么寫:怎么根據展開順序的需要來寫 \expandafter。 以下討論用 \ep 代表 \expandafter,即
\let\ep\expandafter
有時為了方便,用 \ep1 代表代碼串中第一個 \expandafter。 1 判斷的步驟如下:
  1. 劃掉 \ep;
  2. 跳過一個記號;
  3. 如果該記號是 \ep,回到 1;如果該記號不是 \ep,展開它,然后找到代碼片段里第一個沒有被劃掉的 \ep,回到 1。
如此往復,直到所有的 \ep 都被劃掉,再依次展開剩下尚未展開的宏。 Ex.1
\ep1\ep2\ep3\A
\ep4\B
\C
步驟:
  1. 劃掉 \ep1,跳到 \ep3;
  2. 劃掉 \ep3,跳到 \ep4;
  3. 劃掉 \ep4,跳到 \C,展開 \C,跳到 \ep2;
  4. 劃掉 \ep2,跳到 \B,展開 \B;
  5. 沒有剩余的 \ep,展開剩下的 \A。
得到展開順序是 C - B - A。這正是題主問題里的內容。 Ex.2
\ep1\ep2\ep3\ep4\ep5\ep6\ep7\A
\ep8\ep9\ep10\B
\ep11\C
\D
步驟:
  1. 劃掉 \ep1,跳到 \ep3;
  2. 劃掉 \ep3,跳到 \ep5;
  3. 劃掉 \ep5,跳到 \ep7;
  4. 劃掉 \ep7,跳到 \ep8;
  5. 劃掉 \ep8,跳到 \ep10;
  6. 劃掉 \ep10,跳到 \ep11;
  7. 劃掉 \ep11,跳到 \D,展開 \D,跳到 \ep2;
  8. % 整理一下,此時剩下的代碼是
    \ep2\ep4\ep6\A
    \ep9\B
    \C
  9. 根據 Ex.1 得到 C - B - A 的展開順序。
因此展開順序是 D - C - B - A。這正是
@劉海洋?前輩在答案中舉出的例子。Ex.3
\ep1\ep2\ep3\A
\ep4\ep5\ep6\B
\ep7\C
\D
步驟:
  1. 劃掉 \ep1,跳到 \ep3;
  2. 劃掉 \ep3,跳到 \ep4;
  3. 劃掉 \ep4,跳到 \ep6;
  4. 劃掉 \ep6,跳到 \ep7;
  5. 劃掉 \ep7,跳到 \D,展開 \D,跳到 \ep2;
  6. 整理一下,此時剩下的代碼是
    \ep2\A
    \ep5\B
    \C
  7. 劃掉 \ep2,跳到 \ep5;
  8. 劃掉 \ep5,跳到 \C,展開 \C;
  9. 展開剩下的 \A 和 \B。
因此展開順序是 D - C - A - B。 2 先去做飯給母上大人吃,待續……

下載區

本站下載:tb20bechtolsheim 選自:https://www.zhihu.com/question/26916597
分享到:
未經允許不得轉載:LaTeX技巧894:多個 \expandafter 的展開過程是怎樣的?
已有 條意見

    最新文章

    加載中...
      本站提供專業LaTeX排版、咨詢、定制服務,請點擊下圖咨詢詳情


      全國首個精品的LaTeX視頻教程,大牛帶著你入門,讓LaTeX學習不再糾結,請點擊下圖咨詢詳情

      熱門評論

        聯系我們

        交流QQ群:91940767
        本站QQ號:343083553
        郵箱聯系[email protected]
        淘寶店鋪latexstudio.taobao.com 提供排版,模板定制,培訓,圖片處理,視頻教程等LaTeX服務。


        如果您投稿或者希望加入我們團隊,請發送您的簡歷到[email protected]。

        科技藝術的完美融合,專業精致的排版體驗

        聯系我們聯系我們