Macro para adicionar versão estrelada do comando

Macro para adicionar versão estrelada do comando

Como alguém pode escrever uma meta-macro que adicione uma versão com estrela a um comando?

O uso pretendido seria nos moldes de

\newcommand\foo[1]{foo is #1}
\addstarred\foo[2]{foo is #1, bar is #2}

Responder1

Um método já está disponível com o pacote suffixde David Kastrup. Escusado será dizer que está cheio de truques inteligentes.

Você pode dizer

\usepackage{suffix}

\newcommand{\foo}[1]{foo is #1}
\WithSuffix\newcommand\foo*[2]{foo is #1, bar is #2}

e pode ser instrutivo ver como o objetivo é alcançado.

Se fizermos isso \show\fooapós a segunda instrução, encontraremos

> \foo=\protected macro:
->\WSF@suffixcheck \foo .

então aprendemos que isso suffixrequer e-TeX (não é um problema hoje em dia) e redefine \foopara significar \WSF@suffixcheck\foo. Então adicionamos \makeatlettere tentamos \show\WSF@suffixcheck, obtendo

> \WSF@suffixcheck=macro:
#1->\begingroup \def \reserved@a {#1}\futurelet \reserved@b \WSF@suffixcheckii 

então o argumento é salvo em \reserved@ae

\futurelet\reserved@b\WSF@suffixcheckii

É executado. Isso faz com \reserved@bque seja equivalente ao token a seguir \WSF@suffixcheckii. Se a chamada for

\foo{foo}

então \reserved@bserá \bgroup; se a chamada for

\foo*{foo}{bar}

então \reserved@bserá *. Agora precisamos saber o que \WSF@suffixcheckiifaz:

> \WSF@suffixcheckii=macro:
->\ifcsname \expandafter \SuffixName \reserved@a \reserved@b \endcsname
  \expandafter \WSF@suffixcheckiii \else \expandafter \WSF@suffixcheckiv \fi .

OK, vamos ver o que acontece no \foo{foo}caso: \reserved@aexpande para \foo, while \reserved@bis \bgroup(inexpansível), então o TeX é apresentado pela primeira vez com

\ifcsname\SuffixName\foo\reserved@b\endcsname

e \SuffixNameé definido por

> \SuffixName=\long macro:
#1->WSF:\string #1 \meaning .

então o próximo passo é

\ifcsname WSF:\string\foo \meaning\reserved@b\endcsname

e finalmente conseguimos

\ifcsname WSF:\foo begin-group character {\endcsname

onde todos os caracteres possuem código de categoria 12 (mas os espaços possuem 10). No \foo*{foo}{bar}caso obteríamos

\ifcsname WSF:\foo the character *\endcsname

O comando \csname WSF:\foo begin-group character {\endcsnamenão está definido, então o ramo falso é seguido, ou seja

\expandafter \WSF@suffixcheckiv \fi

que simplesmente deixa

\WSF@suffixcheckiv{foo}

no fluxo de entrada. Agora \show\WSF@suffixcheckiv

> \WSF@suffixcheckiv=macro:
->\expandafter \endgroup \csname \expandafter \NoSuffixName \reserved@a \endcsname .

então o grupo aberto anteriormente está fechado, mas primeiro

\csname \expandafter \NoSuffixName \reserved@a \endcsname

é formado. Lembre-se de que isso \reserved@ase expande para \foo, então obtemos

\csname \NoSuffixName \foo \endcsname

e \NoSuffixNameé

> \NoSuffixName=macro:
->WSF:\string .

então finalmente obtemos

\csname WSF:\string\foo\encsname

OK, vamos emitir \expandafter\show\csname WSF:\string\foo\endcsname:

> \WSF:\foo=\long macro:
#1->foo is #1.

isto é, esta macro complicada é uma cópia do original \foo.

No caso de \foo*{foo}{bar}teríamos

\ifcsname WSF:\foo the character *\endcsname

mas neste caso issoédefiniram; de fato

\expandafter\show\csname WSF:\string\foo\space the character *\endcsname

produz

> \WSF:\foo the character *=\long macro:
#1#2->foo is #1, bar is #2.

então esta macro com um nome complicado é o que você definiu como *-variant.

Quase qualquer token pode ser usado como sufixo com este pacote. Mas a ideia essencial não é diferente daquela que você imaginou; as proteções contra a substituição de possíveis nomes de macro existentes são melhores. O que o pacote faz quando

\WithSuffix\newcommand\foo*[2]{foo is #1, bar is #2}

é processado é

  1. Salve o comando original \fooem

    \csname WSF:\string\foo\endcsname
    

    (se isso já existir devido a um procedimento anterior \WithSuffixaplicado a \fooesta etapa, é claro, omitido)

  2. Salve a nova definição em

    \csname WSF:\string\foo\space the character *\endcsname
    
  3. Use a interface abstrata descrita acima para escolher entre diferentes sufixos.

Responder2

Minha própria tentativa de solução está abaixo, com melhorias gentilmente fornecidas por @egreg e @DavidCarlisle.

\documentclass{standalone}

\makeatletter
\newcommand\addstarred[1]{%
    \expandafter\let\csname\string#1@nostar\endcsname#1%
    \edef#1{\noexpand\@ifstar\expandafter\noexpand\csname\string#1@star\endcsname\expandafter\noexpand\csname\string#1@nostar\endcsname}%
    \expandafter\newcommand\csname\string#1@star\endcsname%
}
\makeatother

\newcommand\foo[1]{foo is #1}
\addstarred\foo[2]{foo is #1, bar is #2}

\begin{document}
    \foo{red} --- \foo*{red}{green}
\end{document}

Resultado:

Saída MWE

Explicação:

  • Uma cópia da definição atual do comando \fooé armazenada como arquivo \\foo@nostar.
  • O comando \fooé redefinido para verificar uma estrela e chamar \\foo@starou \\foo@nostar. Isso é feito edefpara que os nomes dos tokens construídos possam ser expandidos no local e não sempre que o comando for invocado.
  • Um \newcommandfor \\foo@staré iniciado e assumirá o resto da definição como segue \addstarred\foo.

informação relacionada