用於新增帶有星號的命令版本的宏

用於新增帶有星號的命令版本的宏

如何編寫一個元宏來將帶有星號的版本加入到命令中?

預期用途如下

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

答案1

suffixDavid Kastrup 的軟體包中已經提供了一種方法。不用說,它充滿了巧妙的技巧。

你可以說

\usepackage{suffix}

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

了解如何實現目標可能會有所啟發。

如果我們\show\foo在第二條指令之後執行,我們會發現

> \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@aand

\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@bis \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未定義,因此遵循 false 分支,即

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

結果:

微波能量輸出

解釋:

  • 命令目前定義的副本\foo儲存為\\foo@nostar.
  • 該命令\foo被重新定義為檢查星號並呼叫\\foo@star\\foo@nostar。這樣做是edef為了可以就地擴展構造的令牌名稱,而不是每次呼叫命令時都會擴展。
  • for開始,並將採用如下定義的其餘\newcommand部分。\\foo@star\addstarred\foo

相關內容