コマンドのスター付きバージョンを追加するマクロ

コマンドのスター付きバージョンを追加するマクロ

コマンドにスター付きバージョンを追加するメタマクロをどのように記述すればよいでしょうか?

想定される使用法は次の通りです

\newcommand\foo[1]{foo is #1}
\addstarred\foo[2]{foo is #1, bar is #2}

答え1

David Kastrup によるパッケージではすでにメソッドが利用可能ですsuffix。言うまでもなく、巧妙なトリックが満載です。

こう言うこともできる

\usepackage{suffix}

\newcommand{\foo}[1]{foo is #1}
\WithSuffix\newcommand\foo*[2]{foo is #1, bar is #2}

そして、その目的がどのように達成されるかを見ることは有益かもしれません。

\show\foo2番目の命令の後に実行すると、

> \foo=\protected macro:
->\WSF@suffixcheck \foo .

suffixそこで、 はe-TeX(最近は問題にならない)を必要とし、\fooを意味するように再定義することがわかります\WSF@suffixcheck\foo。そこで を追加し\makeatletterて試す\show\WSF@suffixcheckと、

> \WSF@suffixcheck=macro:
#1->\begingroup \def \reserved@a {#1}\futurelet \reserved@b \WSF@suffixcheckii 

引数は保存され\reserved@a

\futurelet\reserved@b\WSF@suffixcheckii

が実行されます。これにより、\reserved@bは に続くトークンと同等になります\WSF@suffixcheckii。呼び出しが

\foo{foo}

\reserved@bなるでしょう\bgroup。呼び出しが

\foo*{foo}{bar}

\reserved@bなるでしょう*。ここで、以下が何を意味するのかを知る必要があります\WSF@suffixcheckii

> \WSF@suffixcheckii=macro:
->\ifcsname \expandafter \SuffixName \reserved@a \reserved@b \endcsname
  \expandafter \WSF@suffixcheckiii \else \expandafter \WSF@suffixcheckiv \fi .

さて、 の場合に何が起こるか見てみましょう\foo{foo}\reserved@aは に展開されます\fooが、\reserved@b\bgroup(展開不可能)なので、TeXは最初に次のように表示されます。

\ifcsname\SuffixName\foo\reserved@b\endcsname

そして\SuffixName次のように定義される。

> \SuffixName=\long macro:
#1->WSF:\string #1 \meaning .

次のステップは

\ifcsname WSF:\string\foo \meaning\reserved@b\endcsname

そしてついに

\ifcsname WSF:\foo begin-group character {\endcsname

ここで、すべての文字のカテゴリコードは12です(ただし、スペースは10です)。この\foo*{foo}{bar}場合、

\ifcsname WSF:\foo the character *\endcsname

コマンド\csname WSF:\foo begin-group character {\endcsnameが定義されていないため、偽のブランチが実行されます。

\expandafter \WSF@suffixcheckiv \fi

それは単に

\WSF@suffixcheckiv{foo}

入力ストリームに次のもの\show\WSF@suffixcheckivを追加します。

> \WSF@suffixcheckiv=macro:
->\expandafter \endgroup \csname \expandafter \NoSuffixName \reserved@a \endcsname .

以前開いたグループは閉じられますが、まず

\csname \expandafter \NoSuffixName \reserved@a \endcsname

が形成される。 は\reserved@aに展開される\fooので、

\csname \NoSuffixName \foo \endcsname

そして\NoSuffixName

> \NoSuffixName=macro:
->WSF:\string .

最終的に得られるのは

\csname WSF:\string\foo\encsname

さて、発行しましょう\expandafter\show\csname WSF:\string\foo\endcsname:

> \WSF:\foo=\long macro:
#1->foo is #1.

つまり、この複雑なマクロは元のマクロのコピーです\foo

\foo*{foo}{bar}我々の場合、

\ifcsname WSF:\foo the character *\endcsname

しかし、この場合は定義済み; 確かに

\expandafter\show\csname WSF:\string\foo\space the character *\endcsname

生産する

> \WSF:\foo the character *=\long macro:
#1#2->foo is #1, bar is #2.

したがって、複雑な名前を持つこのマクロは、*-variant として定義されたものです。

このパッケージでは、ほぼすべてのトークンを接尾辞として使用できます。しかし、基本的な考え方はあなたが考案したものと変わりません。既存のマクロ名を上書きすることに対する保護がより優れています。パッケージが行うことは、

\WithSuffix\newcommand\foo*[2]{foo is #1, bar is #2}

処理される

  1. 元の\fooコマンドを以下に保存します

    \csname WSF:\string\foo\endcsname
    

    \WithSuffix(このステップに先行して適用されたものがすでに存在する場合は、\foo当然省略されます)

  2. 新しい定義を以下に保存します

    \csname WSF:\string\foo\space the character *\endcsname
    
  3. さまざまなサフィックスを選択するには、上記の抽象インターフェースを使用します。

答え2

以下に、@egreg と @DavidCarlisle が親切にも提供してくれた改善を加えた、私自身の解決策の試みを示します。

\documentclass{standalone}

\makeatletter
\newcommand\addstarred[1]{%
    \expandafter\let\csname\string#1@nostar\endcsname#1%
    \edef#1{\noexpand\@ifstar\expandafter\noexpand\csname\string#1@star\endcsname\expandafter\noexpand\csname\string#1@nostar\endcsname}%
    \expandafter\newcommand\csname\string#1@star\endcsname%
}
\makeatother

\newcommand\foo[1]{foo is #1}
\addstarred\foo[2]{foo is #1, bar is #2}

\begin{document}
    \foo{red} --- \foo*{red}{green}
\end{document}

結果:

MWE出力

説明:

  • コマンドの現在の定義のコピーは\fooとして保存されます\\foo@nostar
  • コマンドは、\foo星印をチェックし、 または を呼び出すように再定義されます\\foo@star\\foo@nostarこれは、edef構築されたトークン名を、コマンドが呼び出されるたびにではなく、その場で展開できるようにするために行われます。
  • for\newcommand\\foo@star開始され、定義の残りは次のように実行されます\addstarred\foo

関連情報