
Estou tentando definir um novo comando, mas especificando a nova string de controle "on the fly" usando csname
e endcsname
.(Isso tem como objetivo implementar um padrão de injeção de dependência.) **Alternativamente, existe uma maneira de fazer isso com expl3
?
Normalmente, tenho usado \expandafter\newcommand
... mas neste caso de uso, gostaria de definir o primeiro parâmetro do csname
WITHIN \newcommand
, assim:
\newcommand{\expandbefore\csname \GetCommandName \endcsname}[1]{\small I did it!!!}
\newcommand\expandbefore\csname \GetCommandName \endcsname{\small I did it again!!!}
Eu gostaria que tudo dentro do aparelho fosse expandido ANTES \newcommand
-mas sem depender de \expandafter
antes\newcommand
.
Problemas:
Estou curioso para saber se existe uma maneira normal de fazer isso no LaTeX, sem ter que depender de outro hack do buffer de entrada do processo (com Lua).
Adicionar
expandafter
,nameuse
,edef
,let
,csname
, etc, dentro do\newcommand
parâmetro apenas resulta em um erro ao redefinir esses comandos. (Mesmo que esteja dentro{}
oubegingroup
fechado.Tentar
\meaning
\expandafter
descobrir como funciona falha (previsivelmente e engraçado também).
Responder1
Eu (com pequenas modificações) cito meuresponderpara a perguntaDefina uma sequência de controle depois que um espaço for importantecomo parece se aplicar à sua pergunta também:
Ao aplicar a #{
notação -, você pode definir macros cujo último argumento é delimitado por uma chave de abertura. Ao contrário de outros delimitadores de argumentos que são removidos ao coletar argumentos, o TeX deixará uma chave de abertura delimitadora no lugar.
(Na verdade, o mecanismo não está restrito à abertura de tokens de caracteres entre chaves. Você pode usar qualquer token cujo código de categoria seja 1 no momento da definição. Poderia muito bem ser #\WeIrd
depois de \let\WeIrd={
.)
Os argumentos delimitados podem estar vazios.
Portanto, para obter um token de sequência de controle de um conjunto de tokens que se expande para um conjunto de tokens de caracteres que forma o nome do token de sequência de controle em questão, tanto para definir quanto para chamar esse token de sequência de controle, você pode (aplicando a #{
notação - ) inventam uma única sequência de controle \name
que processa um argumento delimitado por colchetes seguido por um argumento não delimitado (que está aninhado entre colchetes). Depois de fazer com que o TeX busque os argumentos, você pode fazer com que o TeX os gire e aplique \csname..\endcsname
ao argumento fornecido entre colchetes. O nome do token de sequência de controle em questão também pode conter tokens de espaço.
\makeatletter
%
\newcommand\name{}%
\long\def\name#1#{\UD@innername{#1}}%
%
\newcommand\UD@innername[2]{%
\expandafter\UD@exchange\expandafter{\csname#2\endcsname}{#1}%
}%
%
\newcommand\UD@exchange[2]{#2#1}%
%
\makeatother
\name foo{bar}
→ etapa de expansão 1:
\UD@innername{foo}{bar}
→ etapa de expansão 2:
\expandafter\UD@exchange\expandafter{\csname bar\endcsname}{foo}
→ etapa de expansão 3:
\UD@exchange{\bar}{foo}
→ etapa de expansão 4:
foo\bar
.
Em contextos de expansão, você precisaria de quatro \expandafter
cadeias para obter o resultado.
Como \romannumeral
não produz nenhum token ao encontrar um número não positivo, você pode adicionar um pouco de \romannumeral
-expansão para reduzir a quantidade de \expandafter
-chains.
Ou faça \romannumeral\name0 foo{bar}
. Dessa forma, apenas uma \expandafter
-chain atingindo o \romannumeral
-token é necessária.
Ou tenha a \romannumeral
-expansão "codificada" dentro da definição - dessa forma, duas \expandafter
cadeias são necessárias. O primeiro para obter a expansão de nível superior de \name
. O segundo para induzir \romannumeral
a expansão.
\makeatletter
%
\newcommand\name{}%
\long\def\name#1#{\romannumeral0\UD@innername{#1}}%
%
\newcommand\UD@innername[2]{%
\expandafter\UD@exchange\expandafter{\csname#2\endcsname}{ #1}%
}%
%
\newcommand\UD@exchange[2]{#2#1}%
%
\makeatother
Com essa macro você não está vinculado a comandos de definição específicos:
\name{foo}
→ \foo
. (←Esta é a maneira pela qual você não define, mas apenas chama/usa sequências de controle por meio de \name
.)
\name\newcommand{foo}
→ \newcommand\foo
.
\name\DeclareRobustCommand{foo}
→ \DeclareRobustCommand\foo
.
\name\global\long\outer\def{foo}
→ \global\long\outer\def\foo
.
\name\expandafter{foo}\bar
→ \expandafter\foo\bar
.
\name\let{foo}=\bar
→ \let\foo=\bar
.
\name\string{foo}
→ \string\foo
.
\name\meaning{foo}
→ \meaning\foo
.
Você também pode usar essa macro para definir/chamar macros cujos nomes contenham espaços:
\name{foo }
→ \foo␣
.
\name\newcommand{foo }
→ \newcommand\foo␣
.
\name\DeclareRobustCommand{foo }
→ \DeclareRobustCommand\foo␣
.
\name\global\long\outer\def{foo }
→ \global\long\outer\def\foo␣
.
\name\expandafter{foo }\bar
→ \expandafter\foo␣\bar
.
\name\let{foo }=\bar
→ \let\foo␣=\bar
.
\name\string{foo }
→ \string\foo␣
.
\name\meaning{foo }
→ \meaning\foo␣
.
Ao coletar o nome do token de sequência de controle em questão, \name
acionará a expansão de tokens expansíveis:
\def\GetCommandName{FooBar}
\name\newcommand{\GetCommandName}[1]{\small I did it!!!}
→\newcommand\FooBar[1]{\small I did it!!!}
\def\GetCommandName{\CommandNamePartA\CommandNamePartB}
\def\CommandNamePartA{Ba}
\def\CommandNamePartB{r\InnerCommandNamePart o}
\def\InnerCommandNamePart{Fo}
\name\newcommand{\GetCommandName}{\small I did it again!!!}
→\newcommand\BarFoo{\small I did it again!!!}
Você também pode aninhar as chamadas para \name
:
Exemplo 1:
\name\name\expandafter{f o o }{b a r }
Processando os primeiros \name
rendimentos:
\name\expandafter\f␣o␣o␣{b a r }
.
Processando os segundos \name
rendimentos:
\expandafter\f␣o␣o␣\b␣a␣r␣
.
(Análogos: \name\name\let{f o o }={b a r }
→ \let\f␣o␣o␣=\b␣a␣r␣
.)
Exemplo 2:
\name\name\name\expandafter\expandafter\expandafter{f o o }\expandafter{b a r }{c r a z y }
Processando os primeiros \name
rendimentos:
\name\name\expandafter\expandafter\expandafter\f␣o␣o␣\expandafter{b a r }{c r a z y }
.
Processando os segundos \name
rendimentos:
\name\expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣{c r a z y }
.
Processando o terceiro \name
rendimento:
\expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣\c␣r␣a␣z␣y␣
.
Exemplo 3:
Em contextos de expansão você pode usar \romannumeral
-expansion para manter as coisas funcionando.
\romannumeral\name\name\name0 \expandafter\expandafter\expandafter{f o o }\expandafter{b a r }{c r a z y }
\romannumeral
continua se expandindo até encontrar algum número. No final ele encontrará o número 0
enquanto com números não positivos \romannumeral
não entregará nenhum token:
%\romannumneral-expansion in progress
\name\name\name0 \expandafter\expandafter\expandafter{f o o }\expandafter{b a r }{c r a z y }
Processando os primeiros \name
rendimentos:
%\romannumneral-expansion in progress
\name\name0 \expandafter\expandafter\expandafter\f␣o␣o␣\expandafter{b a r }{c r a z y }
.
Processando os segundos \name
rendimentos:
%\romannumneral-expansion in progress
\name0 \expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣{c r a z y }
.
Processando o terceiro \name
rendimento:
%\romannumneral-expansion in progress
0 \expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣\c␣r␣a␣z␣y␣
.
Agora \romannumeral
encontra o número 0
. Portanto \romannumeral
-expansion é abortado e \romannumeral
não entrega nenhum token:
\expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣\c␣r␣a␣z␣y␣
.
Esteja ciente de que \name
se aplica internamente \csname
enquanto
a expansão dos tokens expansíveis ocorre enquanto
\csname
durante sua busca pela correspondência\endcsname
reúne os tokens de caracteres que formam o nome do token da sequência de controle em questão.aplicar
\csname
como efeito colateral resulta em atribuir à sequência de controle em questão o significado do\relax
-primitivo caso a sequência de controle em questão tenha sido indefinida antes da aplicação\csname
. Essa atribuição será restrita ao escopo atual, mesmo que o\globaldefs
parâmetro -tenha um valor positivo no momento da aplicação\csname
.
%%\errorcontextlines=1000
\documentclass[a4paper]{article}
\usepackage{textcomp}%
\parindent=0cm
\parskip=\medskipamount
\makeatletter
\newcommand\name{}%
\long\def\name#1#{\romannumeral0\UD@innername{#1}}%
\newcommand\UD@innername[2]{%
\expandafter\UD@exchange\expandafter{\csname#2\endcsname}{ #1}%
}%
\newcommand\UD@exchange[2]{#2#1}%
\makeatother
\name\newcommand{foo}[2]{%
Control sequence whose name does not contain any space.\\
Argument 1: \textit{\textlangle#1\textrangle}\\
Argument 2: \textit{\textlangle#2\textrangle}
}%
\name\newcommand{foo }[2]{%
Control sequence whose name has a trailing space.\\
Argument 1: \textit{\textlangle#1\textrangle}\\
Argument 2: \textit{\textlangle#2\textrangle}
}%
\name\newcommand{ f o o }[2]{%
Control sequence whose name is interspersed with spaces.\\
Argument 1: \textit{\textlangle#1\textrangle}\\
Argument 2: \textit{\textlangle#2\textrangle}
}%
\newcommand*\GetCommandName{\CommandNamePartA\CommandNamePartB}
\newcommand*\CommandNamePartA{Ba}
\newcommand*\CommandNamePartB{r\InnerCommandNamePart o}
\newcommand*\InnerCommandNamePart{Fo}
\name\newcommand{\GetCommandName}{\small I did it again!!!}
\begin{document}
\name{foo}{Arg 1}{Arg 2}
\name{foo }{Arg 1}{Arg 2}
\name{ f o o }{Arg 1}{Arg 2}
Nesting \texttt{\string\name}:
\name\expandafter\newcommand\expandafter*\expandafter{C o N f u SiO n}\expandafter{%
\romannumeral\name\name\name0 %
\expandafter\expandafter\expandafter{F O O}\expandafter{B A R}{C R A Z Y}%
}%
\texttt{\name\string{C o N f u SiO n} is \name\meaning{C o N f u SiO n}}%
\\
Playing around with expandable tokens:
\texttt{\name\string{\GetCommandName}:}
\texttt{\name\meaning{\GetCommandName}}
\name{\GetCommandName}%
Playing around with grouping:
%Be aware that \texttt itself opens up a new scope for typesetting its argument.
%\globaldefs=1\relax
\texttt{%
\begingroup\name\string{w e i r d } is \name\endgroup\meaning{w e i r d }%
}%
\texttt{%
\name\string{w e i r d } is \name\meaning{w e i r d }%
}%
\end{document}
Responder2
O LaTeX já possui um formulário de comando que recebe o nome de um comando em vez do token csname:
\@namedef{\GetCommandName}{\small I did it!!!}
deve fazer o que quiser, isso é simplesmente\expandafter\def\csname\GetCommandName\endcsname{..}