Макрос для добавления отмеченной звездочкой версии команды

Макрос для добавления отмеченной звездочкой версии команды

Как написать метамакрос, который добавляет к команде отмеченную звездочкой версию?

Предполагаемое использование будет осуществляться в соответствии с

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

решение1

Метод уже доступен в пакете suffixДэвида Каструпа. Само собой разумеется, он полон хитрых трюков.

Ты можешь сказать

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

Результат:

Выход МВЭ

Объяснение:

  • Копия текущего определения команды \fooсохраняется как \\foo@nostar.
  • Команда \fooпереопределена для проверки наличия звезды и вызова либо , \\foo@starлибо \\foo@nostar. Это делается для edefтого, чтобы сконструированные имена токенов можно было расширять на месте, а не каждый раз при вызове команды.
  • Начинается цикл \newcommandfor \\foo@star, который примет оставшуюся часть определения следующим образом \addstarred\foo.

Связанный контент