É normal que um comando sorva mais argumentos do que é passado?

É normal que um comando sorva mais argumentos do que é passado?

É correto que uma sequência de controle \fooinclua outra sequência de controle \slurpque suga mais argumentos do que \foorealmente passa para ela?

Por exemplo, está tudo bem fazer isso:

\documentclass{article}

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

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

Em vez disso?

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

Responder1

Está tudo bem? Sim, de fato! Na verdade, há uma abundância de usos para tais definições de macro. Mais notavelmente as definições fundamentais para variantes de comandos com estrela. Por exemplo,articledefine \sectioncomo

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

Veja como ézeroargumentos, embora normalmente os especifiquemos/usemos como \section[<toc>]{<title>}?! Isso porque \@startsectionpega 6 argumentos e depois faz um teste para ver se o usuário adicionou uma estrela ou não. Delatex.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}}}}

Como tal, os argumentos que normalmente especificamos \sectionsó são engolidos por uma macro dois estágios adiante.

Outro bom exemplo de por que esta é uma boa prática tem a ver com mudanças nos códigos de categoria. Depois que os argumentos são consumidos para uso, seus códigos de categoria não podem ser alterados. Portanto, uma macro auxiliar geralmente é usada para modificar os códigos de categoriaantesengolindo quaisquer argumentos.

Existem inúmeros outros exemplos noKernel LaTeX, desde macros básicas de fontes até lidar com o ToC, até mesmo definir um novo comando via \newcommand:

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

Novamente, outra macro que não aceita nenhum argumento, mas executa alguma operação antes de passar a tocha para outra macro. Em geral, este princípio é bem utilizado em todo o kernel e pacotes.

Responder2

Conforme explicado na resposta de Werner, esta é uma prática comum. Todas as macros que possuem uma variante * são definidas desta forma:

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

ou variantes dos mesmos. Da mesma forma, macros com mais de um argumento opcional, como \makeboxdevem seguir um longo caminho para decidir se não há, um ou dois argumentos opcionais:

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

A xparsesituação é bem diferente: como variantes * e argumentos opcionais podem ser especificados de uma maneira bastante geral, é preferível carregartodosargumentos reais:

\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}%
    }%
  }%
}

Este é “o futuro” com o LaTeX3.

informação relacionada