如何使用 \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那裡,而且無論如何在執行\MakeUppercase時都會被忽略,並且沒有任何效果。替換為其中一個或兩者都不起作用。我缺什麼?\showDF{mycmd}\Mycmd{Hi}\csname @#1\endcsname@nameuse{@#1}\csuse{@#1}

答案1

簡而言之,你使用失敗的原因\expandafter是你沒有足夠的它們。有關更多詳細信息,請參閱本網站其他地方對此的雄辯解釋,例如為什麼我們需要十一個擴充來以正確的順序擴展四個令牌

您遇到的問題\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 定義的
測試

定義用a給出\show

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


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

相關內容