指令可以吸收比傳遞給的參數更多的參數嗎?

指令可以吸收比傳遞給的參數更多的參數嗎?

控制序列\foo可以包含另一個控制序列,而控制序列吸收的參數比實際傳遞給它的\slurp參數多嗎?\foo

例如,這樣做可以嗎:

\documentclass{article}

\newcommand\foo  [1]{#1 \slurp}    
\newcommand\slurp[3]{#1 #2 #3}

\begin{document}
\foo{a}{b}{c}{d}
\end{document}

而不是這個?

\documentclass{article}

\newcommand\foo  [4]{#1 \slurp{#2}{#3}{#4}}    
\newcommand\slurp[3]{#1 #2 #3}

\begin{document}
\foo{a}{b}{c}{d}
\end{document}

答案1

這個可以嗎?確實是的!事實上,此類巨集定義有很多用途。最值得注意的是帶有星號的命令變體的基本定義。例如,article定義\section

\newcommand\section{\@startsection {section}{1}{\z@}%
                                   {-3.5ex \@plus -1ex \@minus -.2ex}%
                                   {2.3ex \@plus.2ex}%
                                   {\normalfont\Large\bfseries}}

看看需要怎樣參數,即使我們通常指定/使用它作為\section[<toc>]{<title>}?!這是因為\@startsection需要 6 個參數,然後進行測試以查看使用者是否添加了星號。從latex.ltx:

\def\@startsection#1#2#3#4#5#6{%
  \if@noskipsec \leavevmode \fi
  \par
  \@tempskipa #4\relax
  \@afterindenttrue
  \ifdim \@tempskipa <\z@
    \@tempskipa -\@tempskipa \@afterindentfalse
  \fi
  \if@nobreak
    \everypar{}%
  \else
    \addpenalty\@secpenalty\addvspace\@tempskipa
  \fi
  \@ifstar
    {\@ssect{#3}{#4}{#5}{#6}}%
    {\@dblarg{\@sect{#1}{#2}{#3}{#4}{#5}{#6}}}}

因此,我們通常指定的參數\section只會被接下來兩個階段的巨集所吞噬。

另一個很好的例子說明了為什麼這是一個好的做法,這與類別程式碼的變更有關。一旦參數被使用,它們的類別代碼就不可更改。因此,通常會使用輔助巨集來修改類別程式碼吞噬任何爭論。

書中還有很多其他例子LaTeX 內核,從基本字體巨集到處理目錄,甚至透過以下方式定義新命令\newcommand

\def\newcommand{\@star@or@long\new@command}

同樣,另一個巨集不帶任何參數,但在將火炬傳遞給另一個巨集之前執行一些操作。一般來說,這個原則在整個核心和套件中得到了很好的應用。

答案2

正如維爾納的回答所解釋的,這是常見的做法。所有具有 * 變體的巨集都以這種方式定義:

\newcommand{\foo}{\@ifstar{\@sfoo}{\@foo}}
\newcommand{\@sfoo}[1]{Foo with * applied to #1}
\newcommand{\@sfoo}[1]{Foo without * applied to #1}

或其變體。類似地,具有多個可選參數的宏,例如\makebox必須採取很長的路線來確定是否沒有、一個或兩個可選參數:

\newcommand{\bar}{\@ifnextchar[{\@bar@i}{\@bar}}
\def\@bar@i[#1]{\@ifnextchar[{\@bar@ii{#1}}{\@bar@iii{#1}}
\def\@bar@ii#1[#2]#3{Bar has two optional arguments, #1 and #2, and #3}
\def\@bar@iii#1#2{Bar has one optional argument, #1, and #2}
\def\@bar#1{Bar has no optional argument and #1}

情況xparse完全不同:由於 *-variants 和可選參數可以以相當通用的方式指定,所以最好加載全部實際參數:

\usepackage{xparse}
\NewDocumentCommand{\foo}{sm}{%
  \IfBooleanTF{#1}
    {Foo with * applied to #2}
    {Foo without * applied to #2}%
}

\NewDocumentCommand{\bar}{oom}{%
  \IfNoValueTF{#1}
    {Bar has no optional argument and #3}
    {\IfNoValueTF{#2}
       {Bar has one optional argument, #1, and #3}
       {Bar has two optional arguments, #1 and #2, and #3}%
    }%
  }%
}

這就是 LaTeX3 的「未來」。

相關內容