如何使用 \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}

我希望用宏定义前两个命令,以便生成多个命令对。

\@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 定义的
测试

定义如下\show

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


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

相关内容