¿Cómo utilizar \csname, \expandafter, \csuse, \@namedef...?

¿Cómo utilizar \csname, \expandafter, \csuse, \@namedef...?

tengo el siguiente paquete

\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{mypkg}

\providecommand*\@mycmd{No {\MakeUppercase Mycmd} defined}
\newcommand*\Mycmd[1]{\renewcommand*\@mycmd{#1}}

\newcommand*\showDF[1]{\csname @#1\endcsname}

\endinput

Proporciona al usuario el comando \Mycmd{}para renovar \@mycmdy \showDF{mycmd}mostrar su contenido:

\documentclass[a4paper,titlepage,11pt,twoside,openright]{report}
\usepackage{mypkg}
\begin{document}

\showDF{mycmd}\par
\Mycmd{Test}
\showDF{mycmd}

\end{document}

Me gustaría tener los dos primeros comandos definidos con una macro para generar varios pares de cmd.

\@newDF{mycmd}{Mycmd}

lo he intentado con

\newcommand*\@newDF[2]{
 \providecommand*\csname @#1\endcsname{No {\MakeUppercase #2} defined}
 \newcommand*\csname #2\endcsname[1]{\renewcommand\csname @#1\endcsname{##1}}
}

pero no funcionará.

\newcommand*\newDF[2]{
 \expandafter\providecommand\expandafter*\csname @#1\endcsname{No {\MakeUppercase #2} defined}
 \expandafter\newcommand\expandafter*\csname #2\endcsname[1]{\renewcommand\csname @#1\endcsname{##1}}
}

compila, pero realmente no sé por qué los puse \expandafterallí y, de todos modos, \MakeUppercasese ignora cuando \showDF{mycmd}se ejecuta y \Mycmd{Hi}no tiene ningún efecto. Reemplazar \csname @#1\endcsnamecon uno @nameuse{@#1}o \csuse{@#1}ninguno funciona. ¿Qué me estoy perdiendo?

Respuesta1

Brevemente, la razón por la cual su uso \expandafterfalla es que no tiene suficientes. Para obtener más detalles, consulte las elocuentes explicaciones de esto en otras partes de este sitio, como¿por qué-necesitamos-once-expandafters-para-expandir-cuatro-tokens-en-el-orden-correcto?.

El problema que tiene \MakeUppercasees que no es expandible, lo que significa que no puede usarlo directamente dentro de una \csname...\endcsnamedefinición. Esto se explica detalladamente en las respuestas al post. crear-un-token-macro-en-mayúscula-usando-csname.

Werner ha dado una solución en la que evita el problema de las mayúsculas al pasar tanto el nombre en mayúscula como el nombre sin mayúscula al creador de la macro. Aquí hay otra solución que "pone en mayúsculas" el nombre de la macro automáticamente.

En lugar de usar \providescommandpara verificar si \@my<command>ya se ha definido, verifico esto directamente usando, \ifcsdefya que usar \providescommandrequeriría algunos \expandaftermensajes de texto que preferiría evitar. Finalmente, como beneficio adicional, el siguiente código también define una "función de fábrica" \@DefineMyCommands​​para crear una familia de dichos comandos a partir de una lista separada por comas.

\documentclass{article}
\usepackage{etoolbox}

\makeatletter

\newcommand\@DefineMyCommand[1]{\@ReallyDefineMyCommand#1\relax}
\def\@ReallyDefineMyCommand#1#2\relax{%
  % Define command <#1#2> to set @my#1#2={arg of <#1#2>}
  % Here #1 is the first character of the comamnd and #2 is the rest.
  % We have isolated #1 so that we can uppercase it on the next line
  \uppercase{\expandafter\gdef\csname #1}#2\endcsname##1{\@namedef{@my#1#2}{##1}}
  % if \@my#1#2 is not already defined then define it
  \ifcsdef{@my#1#2}{\relax}{\@namedef{@my#1#2}{No \MakeUppercase{#1#2} defined!}}
}
\newcommand{\@DefineMyCommands}{\forcsvlist{\@DefineMyCommand}}% factory function

\@DefineMyCommands{mycmd, test, fred, julie}% define a bunch of macros

\makeatother

\newcommand*\showDF[1]{\csname @my#1\endcsname}

\begin{document}

\showDF{mycmd}

\Mycmd{A command!}

\showDF{mycmd}

\showDF{test}

\Test{A test!}

\showDF{test}

\end{document}

Aquí está el resultado "esperado":

ingrese la descripción de la imagen aquí

EditarEn realidad, definir un comando de fábrica es un poco tonto si sólo se va a utilizar una vez. A menos que haya una gran cantidad de estas macros, sería más inteligente escribir

\forcsvlist{\@DefineMyCommand}{mycmd, test, fred, julie}

ya que esto aplica el comando \@DefineMyCommanda los elementos de la lista sin crear una macro innecesaria.

Respuesta2

Así es como puedes hacer esto:

\documentclass{article}
\makeatletter
%\providecommand*\@mycmd{No {\MakeUppercase Mycmd} defined}
%\newcommand*\Mycmd[1]{\renewcommand*\@mycmd{#1}}

\newcommand{\@newDF}[2]{%
  \@namedef{@#1}{No \MakeUppercase{#2} defined}%
  %\expandafter\newcommand\csname @#1\endcsname{No \MakeUppercase{#2} defined}% Alternative to above
  \expandafter\newcommand\csname #2\endcsname[1]{%
    \expandafter\renewcommand\csname @#1\endcsname{##1}}
}
\@newDF{mycmd}{Mycmd}
\newcommand*\showDF[1]{\csname @#1\endcsname}
\makeatother
\begin{document}

\showDF{mycmd}\par
\Mycmd{Test}
\showDF{mycmd}

\end{document}

La salida es:


No hay prueba definida por MYCMD

ElLas definiciones se dan con un\show:

> \@mycmd=macro:
->No \MakeUppercase {Mycmd} defined.


> \Mycmd=\long macro:
#1->\expandafter \renewcommand \csname @mycmd\endcsname {#1}.

información relacionada