\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}

複数の 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}.

関連情報