我有以下包裹
\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}
我希望用宏定义前两个命令,以便生成多个命令对。
\@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
那里,而且 anyway在执行\MakeUppercase
时会被忽略,并且没有任何效果。用 either或both 替换都可以。我遗漏了什么?\showDF{mycmd}
\Mycmd{Hi}
\csname @#1\endcsname
@nameuse{@#1}
\csuse{@#1}
答案1
简而言之,你使用失败的原因\expandafter
是你没有足够的使用它们。有关更多详细信息,请参阅本网站其他地方对此的雄辩解释,例如为什么我们需要十一个 expandafters 来按正确顺序扩展四个标记。
您遇到的问题\MakeUppercase
是因为它不可扩展,这意味着您不能直接在\csname...\endcsname
定义中使用它。这在帖子的答案中有详细解释
使用 csname 创建大写宏标记。
Werner 给出了一个解决方案,他通过将大写和非大写名称都传递给宏创建者来避免大写问题。这是另一个解决方案,可以自动将宏名称“大写”。
我并没有使用\providescommand
来检查 是否\@my<command>
已经定义,而是直接使用 来检查,\ifcsdef
因为使用\providescommand
需要一些\expandafter
我宁愿避免的 。最后,作为奖励,下面的代码还定义了一个“工厂函数”,\@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}.