Como usar \csname, \expandafter, \csuse, \@namedef ...?

Como usar \csname, \expandafter, \csuse, \@namedef ...?

Eu tenho o seguinte pacote

\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{mypkg}

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

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

\endinput

Ele fornece ao usuário o comando \Mycmd{}para renovar \@mycmde \showDF{mycmd}mostrar seu conteúdo:

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

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

\end{document}

Gostaria que os dois primeiros comandos fossem definidos com uma macro, para gerar vários pares de cmd.

\@newDF{mycmd}{Mycmd}

Eu tentei com

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

mas não 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, mas realmente não sei por que os coloquei \expandafterlá e, de qualquer forma, \MakeUppercaseé ignorado quando \showDF{mycmd}executado e \Mycmd{Hi}não tem efeito. Substituir \csname @#1\endcsnamepor um @nameuse{@#1}ou \csuse{@#1}nenhum funciona. o que estou perdendo?

Responder1

Resumidamente, a razão pela qual o seu uso \expandafterfalha é que você não tem o suficiente deles. Para obter mais detalhes, consulte as explicações eloqüentes sobre isso em outras partes deste site, comopor que precisamos de onze expansões para expandir quatro tokens na ordem certa.

O problema que você está enfrentando \MakeUppercaseé porque ele não é expansível, o que significa que você não pode usá-lo diretamente dentro de uma \csname...\endcsnamedefinição. Isso é explicado em detalhes nas respostas da postagem crie um macro token maiúsculo usando csname.

Werner deu uma solução onde evita o problema de letras maiúsculas, passando o nome maiúsculo e não maiúsculo para o criador da macro. Aqui está outra solução que coloca o nome da macro em maiúsculas automaticamente.

Em vez de usar \providescommandpara verificar se o \@my<command>já foi definido, verifico isso diretamente usando, \ifcsdefpois o uso \providescommandexigiria alguns \expandafters que eu preferiria evitar. Finalmente, como bônus, o código abaixo também define uma “função de fábrica” \@DefineMyCommandspara criar uma família de tais comandos a partir de uma lista separada por vírgulas.

\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}

Aqui está a saída "esperada":

insira a descrição da imagem aqui

EditarNa verdade, definir um comando de fábrica é um pouco bobo se for usado apenas uma vez. A menos que haja um grande número destas macros, seria mais inteligente escrever

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

pois isso aplica o comando \@DefineMyCommandaos elementos da lista sem criar uma macro desnecessária.

Responder2

Aqui está como você pode fazer isso:

\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}

A saída é:

Nenhum
teste definido pelo MYCMD

Oas definições são dadas com um\show:

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


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

informação relacionada