如何实现 \expandbefore,类似于 \expandafter?

如何实现 \expandbefore,类似于 \expandafter?

csname我正在尝试定义一个新命令,但使用和“动态”指定新的控制字符串endcsname(这是为了实现依赖注入模式。) **或者,有没有办法做到这一点expl3

通常,我一直使用\expandafter\newcommand...但在这个用例中,我想定义csnameWITHIN\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 中是否有一种正常的方法来做到这一点,而不必依赖另一个进程输入缓冲区 hack(使用 Lua)。

  • 在 的参数中添加expandafternameuseedefletcsname\newcommand只会导致重新定义这些命令的错误。(即使在{}begingroup闭包中。)

  • 尝试\meaning \expandafter弄清楚它是如何工作的失败了(可以预见,而且很有趣)。

答案1

我(略作修改)引用我的回答针对这个问题定义控制序列,之后留有空格因为它似乎也适用于你的问题:


通过应用#{-notation,您可以定义最后一个参数由左括号分隔的宏。与在收集参数时被删除的其他参数分隔符不同,TeX 会保留分隔的左括号。
(实际上,该机制并不局限于左括号字符标记。您可以使用在定义时类别代码为 1 的任何标记。也可以#\WeIrd在 之后\let\WeIrd={  。)
分隔的参数可以为空。

因此,为了从一组标记中获取控制序列标记,该标记扩展为一组字符标记,该字符标记构成了所讨论的控制序列标记的名称,既可用于定义也可用于调用该控制序列标记,您可以(通过应用 -notation #{)发明一个控制序列\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-expansion以减少\expandafter-chains的数量。

要么这样做\romannumeral\name0 foo{bar}。这样,只需要一个命中令牌\expandafter的链即可。\romannumeral

或者将\romannumeral-expansion “硬编码”在定义中 — 这样就\expandafter需要两个 -chains。第一个用于获取 的顶层扩展\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,不会传递任何 token:
   %\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-expansion 被中止并且\romannumeral不会传递任何令牌:
   \expandafter\expandafter\expandafter\f␣o␣o␣\expandafter\b␣a␣r␣\c␣r␣a␣z␣y␣  。

请注意,\name内部\csname适用

  • 可扩展标记的扩展发生在\csname搜索匹配期间,\endcsname收集形成相关控制序列标记名称的字符标记。

  • \csname作为副作用,应用会产生将\relax-primitive 的含义分配给相关控制序列的效果,以防相关控制序列在应用之前未定义。即使在应用时 -parameter 具有正值,\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{..}

相关内容