私は以下のパッケージを持っています
\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 ペアを生成するために、最初の 2 つのコマンドをマクロで定義したいと思います。
\@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
が十分ではないからです。詳細については、このサイトの他の場所での雄弁な説明を参照してください。4 つのトークンを正しい順序で拡張するために、なぜ 11 個の expandafter が必要なのか。
あなたが抱えている問題は、\MakeUppercase
それが拡張可能ではないため、定義内で直接使用できないことです\csname...\endcsname
。これは投稿への回答で詳しく説明されています。
csname を使用して大文字のマクロ トークンを作成する。
Werner は、大文字と小文字の両方の名前をマクロ作成者に渡すことで大文字の問題を回避する 1 つの解決策を示しました。マクロ名を自動的に「大文字にする」別の解決策を次に示します。
がすでに定義されている\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}.