
É correto que uma sequência de controle \foo
inclua outra sequência de controle \slurp
que suga mais argumentos do que \foo
realmente 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,article
define \section
como
\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 \@startsection
pega 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 \section
só 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 \makebox
devem 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 xparse
situaçã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.