Как использовать \csname, \expandafter, \csuse, \@namedef ...?

Как использовать \csname, \expandafter, \csuse, \@namedef ...?

У меня есть следующий пакет

\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{mypkg}

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

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

\endinput

Он предоставляет пользователю команду \Mycmd{}на обновление \@mycmdи \showDF{mycmd}отображение его содержимого:

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

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

\end{document}

Я бы хотел определить первые две команды с помощью макроса, чтобы сгенерировать несколько пар cmd.

\@newDF{mycmd}{Mycmd}

Я пробовал с

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

но он не запускается.

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

компилируется, но я действительно не знаю, зачем я их \expandafterтуда поместил, и в любом случае \MakeUppercaseигнорируется при \showDF{mycmd}выполнении и \Mycmd{Hi}не имеет никакого эффекта. Замена \csname @#1\endcsnameна любой @nameuse{@#1}или \csuse{@#1}ни на один из них не работает. Что я упускаю?

решение1

Короче говоря, причина, по которой вы используете \expandafterfails, заключается в том, что у вас их недостаточно. Для получения более подробной информации см. красноречивые объяснения этого в других местах на этом сайте, напримерзачем-нам-нужно-одиннадцать-развёртываний-для-развёртывания-четырёх-токенов-в-правильном-порядке.

Проблема, с которой вы столкнулись, \MakeUppercaseзаключается в том, что он не расширяемый, что означает, что вы не можете использовать его непосредственно внутри \csname...\endcsnameопределения. Это подробно объясняется в ответах на пост создать-заглавный-макрос-токен-используя-csname.

Вернер дал одно решение, в котором он избегает проблемы с заглавными буквами, передавая создателю макроса как заглавные, так и строчные имена. Вот еще одно решение, которое автоматически «заглавляет» имя макроса.

Вместо того, чтобы использовать \providescommandдля проверки, \@my<command>определен ли уже, я проверяю это напрямую, используя, \ifcsdefпоскольку использование \providescommandпотребовало бы некоторых \expandafters, которых я бы предпочел избежать. Наконец, в качестве бонуса, код ниже также определяет «фабричную функцию» \@DefineMyCommandsдля создания семейства таких команд из списка, разделенного запятыми.

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

Вот «ожидаемый» результат:

введите описание изображения здесь

РедактироватьНа самом деле, определение команды фабрики немного глупо, если она будет использоваться только один раз. Если только нет большого количества этих макросов, было бы разумнее написать

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

так как это применяет команду \@DefineMyCommandк элементам в списке, не создавая ненужный макрос.

решение2

Вот как это можно сделать:

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

Выходные данные:


Тест MYCMD не определен

TheОпределения даны с\show:

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


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

Связанный контент