У меня есть следующий пакет
\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
Короче говоря, причина, по которой вы используете \expandafter
fails, заключается в том, что у вас их недостаточно. Для получения более подробной информации см. красноречивые объяснения этого в других местах на этом сайте, напримерзачем-нам-нужно-одиннадцать-развёртываний-для-развёртывания-четырёх-токенов-в-правильном-порядке.
Проблема, с которой вы столкнулись, \MakeUppercase
заключается в том, что он не расширяемый, что означает, что вы не можете использовать его непосредственно внутри \csname...\endcsname
определения. Это подробно объясняется в ответах на пост
создать-заглавный-макрос-токен-используя-csname.
Вернер дал одно решение, в котором он избегает проблемы с заглавными буквами, передавая создателю макроса как заглавные, так и строчные имена. Вот еще одно решение, которое автоматически «заглавляет» имя макроса.
Вместо того, чтобы использовать \providescommand
для проверки, \@my<command>
определен ли уже, я проверяю это напрямую, используя, \ifcsdef
поскольку использование \providescommand
потребовало бы некоторых \expandafter
s, которых я бы предпочел избежать. Наконец, в качестве бонуса, код ниже также определяет «фабричную функцию» \@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 не определен
> \@mycmd=macro:
->No \MakeUppercase {Mycmd} defined.
> \Mycmd=\long macro:
#1->\expandafter \renewcommand \csname @mycmd\endcsname {#1}.