
Я пытаюсь определить новую команду, но указываю новую управляющую строку «на лету» с помощью csname
и endcsname
.(Это делается с целью реализации шаблона внедрения зависимостей.) **Или есть ли способ сделать это с помощью expl3
?
Обычно я использую \expandafter\newcommand
..., но в этом случае я хотел бы определить первый параметр csname
WITHIN \newcommand
, например так:
\newcommand{\expandbefore\csname \GetCommandName \endcsname}[1]{\small I did it!!!}
\newcommand\expandbefore\csname \GetCommandName \endcsname{\small I did it again!!!}
Я бы хотел, чтобы все, что находится внутри фигурных скобок, было расширено ДО того, как \newcommand
...но не полагаясь на \expandafter
ранее\newcommand
.
Проблемы:
Мне интересно, есть ли нормальный способ сделать это в LaTeX, не прибегая к другому хаку входного буфера процесса (с Lua).
Добавление
expandafter
,nameuse
,edef
,let
,csname
, и т. д. в\newcommand
параметр ' приводит только к ошибке переопределения этих команд. (Даже если они находятся в замыканиях{}
илиbegingroup
.Попытка
\meaning
\expandafter
понять, как это работает, не удалась (что предсказуемо и забавно).
решение1
Я (с небольшими изменениями) цитирую свойотвечатьна вопросОпределите последовательность управления, после которой пробел имеет значение.поскольку это, по-видимому, применимо и к вашему вопросу:
Применяя #{
-нотацию, вы можете определить макросы, последний аргумент которых отделен открывающей фигурной скобкой. В отличие от других разделителей аргументов, которые удаляются при сборе аргументов, TeX оставит на месте разделяющую открывающую фигурную скобку.
(На самом деле механизм не ограничивается токенами символов открывающей фигурной скобки. Вы можете использовать любой токен, код категории которого равен 1 во время определения. Также может быть #\WeIrd
после \let\WeIrd={
.)
Разделенные аргументы могут быть пустыми.
Поэтому для получения токена управляющей последовательности из набора токенов, который расширяется до набора токенов символов, который формирует имя рассматриваемого токена управляющей последовательности как для определения, так и для вызова этого токена управляющей последовательности, вы можете (применив -нотацию #{
) придумать одну управляющую последовательность \name
, которая обрабатывает аргумент, разделенный фигурными скобками, за которым следует неразделенный аргумент (который вложен в фигурные скобки). После того, как TeX извлечет аргументы, вы можете заставить TeX вращать их и применять \csname..\endcsname
к аргументу, указанному внутри фигурных скобок. Имя рассматриваемого токена управляющей последовательности может также содержать пробельные токены.
\makeatletter
%
\newcommand\name{}%
\long\def\name#1#{\UD@innername{#1}}%
%
\newcommand\UD@innername[2]{%
\expandafter\UD@exchange\expandafter{\csname#2\endcsname}{#1}%
}%
%
\newcommand\UD@exchange[2]{#2#1}%
%
\makeatother
\name foo{bar}
→ шаг расширения 1:
\UD@innername{foo}{bar}
→ шаг расширения 2:
\expandafter\UD@exchange\expandafter{\csname bar\endcsname}{foo}
→ шаг расширения 3:
\UD@exchange{\bar}{foo}
→ шаг расширения 4:
foo\bar
.
\expandafter
В контексте расширения для получения результата вам понадобятся четыре -цепочки.
Так как \romannumeral
при обнаружении неположительного числа не создается никаких токенов, можно добавить немного \romannumeral
-расширения, чтобы уменьшить количество \expandafter
-цепочек.
Либо сделайте \romannumeral\name0 foo{bar}
. Таким образом, нужна только одна \expandafter
-цепочка, достигающая -токена.\romannumeral
Или иметь \romannumeral
-expansion "жестко закодированным" в определении — таким образом, \expandafter
необходимы две -цепочки. Первая для получения расширения верхнего уровня \name
. Вторая для индуцирования \romannumeral
-expansion.
\makeatletter
%
\newcommand\name{}%
\long\def\name#1#{\romannumeral0\UD@innername{#1}}%
%
\newcommand\UD@innername[2]{%
\expandafter\UD@exchange\expandafter{\csname#2\endcsname}{ #1}%
}%
%
\newcommand\UD@exchange[2]{#2#1}%
%
\makeatother
С таким макросом вы не привязаны к конкретным командам определения:
\name{foo}
→ \foo
. (←Это способ, при котором вы не определяете, а просто вызываете/используете управляющие последовательности с помощью \name
.)
\name\newcommand{foo}
→ \newcommand\foo
.
\name\DeclareRobustCommand{foo}
→ \DeclareRobustCommand\foo
.
\name\global\long\outer\def{foo}
→ \global\long\outer\def\foo
.
\name\expandafter{foo}\bar
→ \expandafter\foo\bar
.
\name\let{foo}=\bar
→ \let\foo=\bar
.
\name\string{foo}
→ \string\foo
.
\name\meaning{foo}
→ \meaning\foo
.
Вы также можете использовать такой макрос для определения/вызова макросов, имена которых содержат пробелы:
\name{foo }
→ \foo␣
.
\name\newcommand{foo }
→ \newcommand\foo␣
.
\name\DeclareRobustCommand{foo }
→ \DeclareRobustCommand\foo␣
.
\name\global\long\outer\def{foo }
→ \global\long\outer\def\foo␣
.
\name\expandafter{foo }\bar
→ \expandafter\foo␣\bar
.
\name\let{foo }=\bar
→ \let\foo␣=\bar
.
\name\string{foo }
→ \string\foo␣
.
\name\meaning{foo }
→ \meaning\foo␣
.
При сборе имени рассматриваемого токена управляющей последовательности \name
будет запущено расширение расширяемых токенов:
\def\GetCommandName{FooBar}
\name\newcommand{\GetCommandName}[1]{\small I did it!!!}
→\newcommand\FooBar[1]{\small I did it!!!}
\def\GetCommandName{\CommandNamePartA\CommandNamePartB}
\def\CommandNamePartA{Ba}
\def\CommandNamePartB{r\InnerCommandNamePart o}
\def\InnerCommandNamePart{Fo}
\name\newcommand{\GetCommandName}{\small I did it again!!!}
→\newcommand\BarFoo{\small I did it again!!!}
Вы также можете вкладывать вызовы в \name
:
Пример 1:
\name\name\expandafter{f o o }{b a r }
Обработка первых \name
урожаев:
\name\expandafter\f␣o␣o␣{b a r }
.
Обработка второго \name
дает:
\expandafter\f␣o␣o␣\b␣a␣r␣
.
(Аналогично: \name\name\let{f o o }={b a r }
→ \let\f␣o␣o␣=\b␣a␣r␣
.)
Пример 2:
\name\name\name\expandafter\expandafter\expandafter{f o o }\expandafter{b a r }{c r a z y }
Обработка первых \name
урожаев:
\name\name\expandafter\expandafter\expandafter\f␣o␣o␣\expandafter{b a r }{c r a z y }
.
Обработка второго \name
дает:
\name\expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣{c r a z y }
.
Обработка третьего \name
дает:
\expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣\c␣r␣a␣z␣y␣
.
Пример 3:
В контексте расширения вы можете использовать \romannumeral
-expansion, чтобы все продолжалось.
\romannumeral\name\name\name0 \expandafter\expandafter\expandafter{f o o }\expandafter{b a r }{c r a z y }
\romannumeral
продолжает расширяться, пока не найдет какое-то число. В конце концов он найдет число 0
, а с неположительными числами \romannumeral
не доставит никакого токена:
%\romannumneral-expansion in progress
\name\name\name0 \expandafter\expandafter\expandafter{f o o }\expandafter{b a r }{c r a z y }
Обработка первых \name
урожаев:
%\romannumneral-expansion in progress
\name\name0 \expandafter\expandafter\expandafter\f␣o␣o␣\expandafter{b a r }{c r a z y }
.
Обработка второго \name
дает:
%\romannumneral-expansion in progress
\name0 \expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣{c r a z y }
.
Обработка третьего \name
дает:
%\romannumneral-expansion in progress
0 \expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣\c␣r␣a␣z␣y␣
.
Теперь \romannumeral
находит число 0
. Поэтому \romannumeral
-расширение прерывается и \romannumeral
не доставит никакого токена:
\expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣\c␣r␣a␣z␣y␣
.
Имейте в виду, что \name
внутреннее применяется \csname
во время
Расширение расширяемых токенов происходит, пока
\csname
во время поиска соответствия\endcsname
собираются токены символов, которые образуют имя рассматриваемого токена управляющей последовательности.применение
\csname
в качестве побочного эффекта приводит к назначению рассматриваемой управляющей последовательности значения -примитива\relax
в случае, если рассматриваемая управляющая последовательность была не определена до применения\csname
. Это назначение будет ограничено текущей областью действия, даже если\globaldefs
-параметр имел положительное значение во время применения\csname
.
%%\errorcontextlines=1000
\documentclass[a4paper]{article}
\usepackage{textcomp}%
\parindent=0cm
\parskip=\medskipamount
\makeatletter
\newcommand\name{}%
\long\def\name#1#{\romannumeral0\UD@innername{#1}}%
\newcommand\UD@innername[2]{%
\expandafter\UD@exchange\expandafter{\csname#2\endcsname}{ #1}%
}%
\newcommand\UD@exchange[2]{#2#1}%
\makeatother
\name\newcommand{foo}[2]{%
Control sequence whose name does not contain any space.\\
Argument 1: \textit{\textlangle#1\textrangle}\\
Argument 2: \textit{\textlangle#2\textrangle}
}%
\name\newcommand{foo }[2]{%
Control sequence whose name has a trailing space.\\
Argument 1: \textit{\textlangle#1\textrangle}\\
Argument 2: \textit{\textlangle#2\textrangle}
}%
\name\newcommand{ f o o }[2]{%
Control sequence whose name is interspersed with spaces.\\
Argument 1: \textit{\textlangle#1\textrangle}\\
Argument 2: \textit{\textlangle#2\textrangle}
}%
\newcommand*\GetCommandName{\CommandNamePartA\CommandNamePartB}
\newcommand*\CommandNamePartA{Ba}
\newcommand*\CommandNamePartB{r\InnerCommandNamePart o}
\newcommand*\InnerCommandNamePart{Fo}
\name\newcommand{\GetCommandName}{\small I did it again!!!}
\begin{document}
\name{foo}{Arg 1}{Arg 2}
\name{foo }{Arg 1}{Arg 2}
\name{ f o o }{Arg 1}{Arg 2}
Nesting \texttt{\string\name}:
\name\expandafter\newcommand\expandafter*\expandafter{C o N f u SiO n}\expandafter{%
\romannumeral\name\name\name0 %
\expandafter\expandafter\expandafter{F O O}\expandafter{B A R}{C R A Z Y}%
}%
\texttt{\name\string{C o N f u SiO n} is \name\meaning{C o N f u SiO n}}%
\\
Playing around with expandable tokens:
\texttt{\name\string{\GetCommandName}:}
\texttt{\name\meaning{\GetCommandName}}
\name{\GetCommandName}%
Playing around with grouping:
%Be aware that \texttt itself opens up a new scope for typesetting its argument.
%\globaldefs=1\relax
\texttt{%
\begingroup\name\string{w e i r d } is \name\endgroup\meaning{w e i r d }%
}%
\texttt{%
\name\string{w e i r d } is \name\meaning{w e i r d }%
}%
\end{document}
решение2
В LaTeX уже есть форма команды, которая принимает имя команды, а не токен csname:
\@namedef{\GetCommandName}{\small I did it!!!}
должен делать то, что хочешь, это просто\expandafter\def\csname\GetCommandName\endcsname{..}