Допустимо ли, чтобы команда выдавала больше аргументов, чем ей передано?

Допустимо ли, чтобы команда выдавала больше аргументов, чем ей передано?

Допустимо ли, чтобы управляющая последовательность \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этим ситуация совершенно иная: поскольку *-варианты и необязательные аргументы могут быть указаны довольно общим образом, предпочтительнее загружатьвсефактические аргументы:

\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.

Связанный контент