
Estoy intentando definir un nuevo comando, pero especificando la nueva cadena de control "sobre la marcha" usando csname
y endcsname
.(Esto tiene como objetivo implementar un patrón de inyección de dependencia). **Alternativamente, ¿hay alguna manera de hacer esto con expl3
?
Normalmente, he estado usando \expandafter\newcommand
... pero en este caso de uso, me gustaría definir el primer parámetro de csname
Within \newcommand
, así:
\newcommand{\expandbefore\csname \GetCommandName \endcsname}[1]{\small I did it!!!}
\newcommand\expandbefore\csname \GetCommandName \endcsname{\small I did it again!!!}
Me gustaría que todo lo que está dentro de las llaves se expanda ANTES de \newcommand
...pero sin depender de \expandafter
antes\newcommand
.
Asuntos:
Tengo curiosidad por saber si existe una forma normal de hacer esto en LaTeX, sin tener que depender de otro truco del búfer de entrada del proceso (con Lua).
Agregar
expandafter
,nameuse
,edef
,let
,csname
, etc, dentro del\newcommand
parámetro 's solo da como resultado un error al redefinir esos comandos. (Incluso si está en{}
obegingroup
cierres.Intentar
\meaning
\expandafter
descubrir cómo funciona falla (de manera predecible y también divertida).
Respuesta1
Yo (con ligeras modificaciones) cito mirespuestaa la preguntaDefinir una secuencia de control después de que un espacio importeya que parece aplicarse también a su pregunta:
Al aplicar la #{
notación -, puede definir macros cuyo último argumento esté delimitado por una llave de apertura. A diferencia de otros delimitadores de argumentos que se eliminan al recopilar argumentos, TeX dejará una llave de apertura delimitadora en su lugar.
(En realidad, el mecanismo no se limita a abrir tokens de caracteres de llave. Puede usar cualquier token cuyo código de categoría sea 1 en el momento de la definición. También podría ser #\WeIrd
posterior \let\WeIrd={
).
Los argumentos delimitados pueden estar vacíos.
Por lo tanto, para obtener un token de secuencia de control a partir de un conjunto de tokens que se expande a un conjunto de tokens de caracteres que forma el nombre del token de secuencia de control en cuestión, tanto para definir como para llamar a ese token de secuencia de control, puede (aplicando la #{
notación - ) inventa una secuencia de control única \name
que procesa un argumento delimitado por llaves seguido por un argumento no delimitado (que está anidado entre llaves). Después de que TeX obtenga los argumentos, puede hacer que TeX los haga girar y los aplique \csname..\endcsname
al argumento proporcionado entre llaves. El nombre del token de secuencia de control en cuestión también puede contener tokens de espacio.
\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}
→ paso de expansión 1:
\UD@innername{foo}{bar}
→ paso de expansión 2:
\expandafter\UD@exchange\expandafter{\csname bar\endcsname}{foo}
→ paso de expansión 3:
\UD@exchange{\bar}{foo}
→ paso de expansión 4:
foo\bar
.
En contextos de expansión, necesitaría cuatro \expandafter
cadenas para obtener el resultado.
Como \romannumeral
no se produce ningún token cuando se encuentra un número no positivo, puede agregar un poco de \romannumeral
expansión para reducir la cantidad de \expandafter
cadenas.
O hazlo \romannumeral\name0 foo{bar}
. De esta manera, solo se necesita una \expandafter
cadena que golpee el \romannumeral
token.
O tenga la \romannumeral
expansión "codificada" dentro de la definición; de esta manera \expandafter
se necesitan dos cadenas. El primero para obtener la expansión de nivel superior de \name
. El segundo para inducir \romannumeral
la expansión.
\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
Con una macro de este tipo no estás obligado a comandos de definición específicos:
\name{foo}
→ \foo
. (←Esta es la forma en la que no defines sino que simplemente llamas/usas secuencias de control mediante \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
.
También puede utilizar una macro de este tipo para definir/llamar macros cuyos nombres contengan espacios:
\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␣
.
Al recopilar el nombre del token de secuencia de control en cuestión, \name
se activará la expansión de tokens expandibles:
\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!!!}
También puedes anidar las llamadas a \name
:
Ejemplo 1:
\name\name\expandafter{f o o }{b a r }
Procesando los primeros \name
rendimientos:
\name\expandafter\f␣o␣o␣{b a r }
.
Al procesar el segundo \name
se obtiene:
\expandafter\f␣o␣o␣\b␣a␣r␣
.
(De manera análoga: \name\name\let{f o o }={b a r }
→ \let\f␣o␣o␣=\b␣a␣r␣
.)
Ejemplo 2:
\name\name\name\expandafter\expandafter\expandafter{f o o }\expandafter{b a r }{c r a z y }
Procesando los primeros \name
rendimientos:
\name\name\expandafter\expandafter\expandafter\f␣o␣o␣\expandafter{b a r }{c r a z y }
.
Al procesar el segundo \name
se obtiene:
\name\expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣{c r a z y }
.
Procesando el tercero \name
se obtiene:
\expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣\c␣r␣a␣z␣y␣
.
Ejemplo 3:
En contextos de expansión, puede utilizar \romannumeral
-expansion para que todo siga funcionando.
\romannumeral\name\name\name0 \expandafter\expandafter\expandafter{f o o }\expandafter{b a r }{c r a z y }
\romannumeral
continúa expandiéndose hasta que encuentra algún número. Al final encontrará el número 0
mientras que con números no positivos \romannumeral
no entregará ningún token:
%\romannumneral-expansion in progress
\name\name\name0 \expandafter\expandafter\expandafter{f o o }\expandafter{b a r }{c r a z y }
Procesando los primeros \name
rendimientos:
%\romannumneral-expansion in progress
\name\name0 \expandafter\expandafter\expandafter\f␣o␣o␣\expandafter{b a r }{c r a z y }
.
Al procesar el segundo \name
se obtiene:
%\romannumneral-expansion in progress
\name0 \expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣{c r a z y }
.
Procesando el tercero \name
se obtiene:
%\romannumneral-expansion in progress
0 \expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣\c␣r␣a␣z␣y␣
.
Ahora \romannumeral
encuentra el número 0
. Por lo tanto, \romannumeral
la expansión se cancela y \romannumeral
no entregará ningún token:
\expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣\c␣r␣a␣z␣y␣
.
Tenga en cuenta que \name
se aplica internamente \csname
mientras
La expansión de los tokens expandibles se lleva a cabo mientras,
\csname
durante su búsqueda de la coincidencia,\endcsname
reúne los tokens de personajes que forman el nombre del token de secuencia de control en cuestión.aplicar
\csname
como efecto secundario produce asignar a la secuencia de control en cuestión el significado de la\relax
primitiva en caso de que la secuencia de control en cuestión no estuviera definida antes de la aplicación\csname
. Esa asignación se restringirá al alcance actual incluso si el\globaldefs
parámetro tenía un valor positivo en el momento de la solicitud\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}
Respuesta2
LaTeX ya tiene un formulario de comando que toma el nombre de un comando en lugar del token csname:
\@namedef{\GetCommandName}{\small I did it!!!}
deberías hacer lo que quieras esto es simplemente\expandafter\def\csname\GetCommandName\endcsname{..}