使用 \outer 宏的“巧妙技巧”

使用 \outer 宏的“巧妙技巧”

TeX 按主题分类,第 111 页:

然而,对于某些应用来说,一些普通宏位于外部会带来不便,特别是如下宏\newskip。一种补救措施是重新定义它们,而不使用“外部”选项,例如在 LaTeX 中就是这样做的,但更聪明的技巧也是可能的。

\outer在通常不允许使用宏的地方使用宏的技巧有哪些例子?

答案1

正如 egreg 所说,标准方式是使用\csname:只要\outer定义时没有标记,那么就是安全的。

因此,另一种可能性是,在一个组中,使宏非外部,定义你的宏,然后重置所有内容:

\begingroup
  \let\newdimen\relax
  \gdef\makedimenandgivevalue#1#2{%
    \newdimen#1%
    #1=#2\relax}
\endgroup

此版本的问题在于,如果您需要将\outer宏作为参数传递给辅助函数,则无法做到这一点。更方便的版本是为宏定义一个非外部包装器\outer,然后您就可以自由使用它:

\edef\mynewdimen{\noexpand\newdimen}
%
\def\makedimenandgivevalue#1#2{%
  \mynewdimen#1%
  #1=#2\relax
}

答案2

标准技巧是\csname

\def\makedimenandgivevalue#1#2{%
  \csname newdimen\endcsname#1%
  #1=#2\relax
}

或者\noexpand

\edef\myproclaim#1#2\endmyproclaim{\noexpand\proclaim #1. \ignorespaces #2\par}

\myproclaim{Theorem}
This is better syntax.
\endmyproclaim

\proclaim Theorem.
This is worse syntax.

\bye

在此处输入图片描述

答案3

著名的\cleartabs\settabs\+plain.tex展示了这样一个技巧(TeXbook 第 354 页):

\let\+=\relax % in case this file (plain.tex) is being read in twice
\def\sett@b{\ifx\next\+ \let\next=\relax % turn off \outerness
    \def\next{\afterassignment\s@tt@b\let\next}%
  \else\let\next=\s@tcols\fi\next}

...

\outer\def\+{\tabalign}
  1. \let\+=\relax在定义之前完成,\sett@b以防\+已经是一个\outer标记(否则,\+的替换文本中的标记在定义\sett@b时会导致错误)。\sett@b

  2. 当文档中 的\ifx\next\+正常使用过程中测试为真时,是一个标记,因为 在正常使用中,是。因此,Knuth在用重新定义之前会这样做,否则此定义中替换文本末尾的标记将导致错误。\settabs\next\outer\+\outer\let\next=\relax\next\def\next{\afterassignment\s@tt@b\let\next}\next

答案4

正如 egreg 在回答中指出的那样:

如果 \outer-tokens 被 击中\noexpand,它们将被变成\relax(仅)用于下一次扩展:

\documentclass{article}

\outer\def\myoutermacro{My outer macro's definition.}

\def\FirstOfOne#1{#1}%

\newtoks\myscratchtoks

\begin{document}

% This does not work:
%
% \FirstOfOne{\myoutermacro}.
%
% These do work:

\expandafter\FirstOfOne\expandafter{\noexpand\myoutermacro}

\myscratchtoks\expandafter{\noexpand\myoutermacro}
\FirstOfOne{\the\myscratchtoks}

\edef\myscratchmacro{\noexpand\myoutermacro}
\FirstOfOne{\myscratchmacro}

\end{document}

相关内容