我有以下包
\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 定義的
測試
> \@mycmd=macro:
->No \MakeUppercase {Mycmd} defined.
> \Mycmd=\long macro:
#1->\expandafter \renewcommand \csname @mycmd\endcsname {#1}.