是否有“附加 \let”?

是否有“附加 \let”?

\def\MyText{\textbf{My Text}}
\let\MySaved\MyText

\MySaved\MyText具有相同的效果\meaning。我希望在文档中进一步说明的是

% \MyText = \textbf{My Text}
\def\MyText{\textit{More Text}}
\let\MySaved{\MySaved \MyText}
% \MySaved = \textbf{My Text} \textit{More Text}

但显然\let不允许在第二个参数中使用一组标记。

有没有办法像这样附加具有相同特定(非)扩展属性的内容\let

答案1

\def获得\expandafter帮助:

\expandafter\def\expandafter\MySaved\expandafter{\MySaved \MyText}

如果您使用 LaTeX ( \textbf\textit,...),那么 \g@addto@macro可以使用:

\makeatletter
\g@addto@macro\MySaved{\MyText}
\makeatother

或者如果您需要完全扩展:

\edef\MySaved{\MySaved \MyText}

但这可能会破坏脆弱的东西,因此 LaTeX 提供\protected@edef

\makeatletter
\protected@edef\MySaved{\MySaved \MyText}
\makeatother

或者如果\Mysaved\MyText应该精确扩展一次,e-TeX 可以\unexpanded帮助:

\edef\MySaved{%
  \unexpanded\expandafter{\MySaved}%
  \unexpanded\expandafter{\MyText}%
}

通过 vanilla TeX 中的标记寄存器可以实现相同的功能:

\toks0\expandafter{\MySaved}
\toks2\expandafter{\MyText}
\edef\MySaved{\the\toks0 \the\toks2}

答案2

无需重新发明轮子,我们可以使用etoolbox

\appto{\MySaved}{\textit{More text}}

\gappto如果更改应该是全局的,则使用。如果您想添加到\MySaved的(第一级)扩展\MyText,则有

\eappto{\MySaved}{\expandonce{\MyText}}

并将\xappto在全球范围内采取同样的措施。让我们看看:

\documentclass{article}
\usepackage{etoolbox}
\def\MyText{\textbf{My Text}}
\let\MySaved\MyText
\def\MyText{\textit{More Text}}
\eappto\MySaved{\MyText}
\show\MySaved

终端上的输出将是

> \MySaved=macro:
->\textbf {My Text}\textit {More Text}.

为了使事物更加对称,你可以使用

\eappto{\MySaved}{\expandonce{\MyText}}

也可以代替\let,但这不会重新初始化\MySaved

如果只需要 Plain TeX,只需复制实现:

\protected\def\appto#1#2{%
  \ifundef{#1}
    {\edef#1{\unexpanded{#2}}}
    {\edef#1{\expandonce#1\unexpanded{#2}}}}
\protected\def\eappto#1#2{%
  \ifundef{#1}
    {\edef#1{#2}}
    {\edef#1{\expandonce#1#2}}}
\protected\def\gappto#1#2{%
  \ifundef{#1}
    {\xdef#1{\unexpanded{#2}}}
    {\xdef#1{\expandonce#1\unexpanded{#2}}}}
\protected\def\xappto#1#2{%
  \ifundef{#1}
    {\xdef#1{#2}}
    {\xdef#1{\expandonce#1#2}}}
\def\ifundef#1{%
  \ifdefined#1%
    \ifx#1\relax
      \expandafter\expandafter
      \expandafter\@firstoftwo
    \else
      \expandafter\expandafter
      \expandafter\@secondoftwo
    \fi
  \else
    \expandafter\@firstoftwo
  \fi}
\long\def\@firstoftwo#1#2{#1}
\long\def\@secondoftwo#1#2{#2}
\def\expandonce#1{%
  \unexpanded\expandafter{#1}}

答案3

由于问题是关于纯 TeX 的,我建议使用标记列表寄存器而不是宏,因为标记列表专门用于此目的,即保存标记列表。使用以下方法创建一个

\newtoks\MyList

例如,为其分配一个明确的列表

\MyList={\textbf{My text}}

或括号之间的任何其他(平衡)标记列表;因为这是关于收集标记,所以你可以确保不会尝试扩展那里的任何内容。与宏不同,标记列表寄存器不会自发扩展到它们所持有的标记(如果 TeX 在期望操作时遇到标记列表寄存器的名称,它会假定这会启动一个任务到寄存器,如果没有开括号,则表示抗议),因此要获取内容,必须使用 显式展开\the。因此,如果最终您想将标记传输到宏的主体,您可以说

\edef\macro{\the\MyList}

(之所以\edef使用 ,是因为 的内容\MyList现在就需要,而不是在\macro扩展时需要)。请注意,尽管\edef通常会尽可能地保持扩展,但\the应用于标记列表寄存器的结果明确排除在任何进一步的扩展尝试之外,因此您可以确保\macro会扩展为 中收集的标记\MyList

现在扩展标记列表寄存器的内容相当简单;您只需确保在进行分配之前将旧内容扩展到新标记列表中即可。为此,只需\expandafter执行以下操作:

\MyList=\expandafter{\the\Mylist more tokens here}

当你需要扩展标记列表末尾的某些内容时,需要付出更多努力。假设你想将内容附加到\MyList另一个标记列表寄存器中\AnotherList。然后,你可以确保最右边的列表得到扩展,然后是最左边的列表(你能明白为什么这个顺序是必要的吗?),所有这些都是在分配之前完成的,通过一整行\expandafters:

\let\x=\expandafter
\MyList=\x\x\x{\x\the\x\MyList\the\AnotherList}

但是,还有一种更简单的方法,将标记列表的扩展性质\edef和被动性质混合在一起,以获得对扩展的详细控制。它需要一个辅助宏,例如\temp

\edef\temp{\the\MyList\the\AnotherList}
\MyList=\exandafter{\temp}

我最后要说的是这个问题;你建议\let将一个宏的扩展复制到另一个宏,而无需进一步扩展该扩展(就像使用\edef一样)。虽然\let可以用于此,但这不是它的主要目的,事实上,这只是\let一般用途的必然结果:将一个标记的当前含义转移到另一个标记(前提是后者可以修改其含义,因此它不应该是非活动字符)。这不仅限于宏:你可以让控制序列代表普通字符,或者代表 TeX 基元(正如我上面所做的那样,让 代表\x基元\expandafer;请注意,使用\x 扩张将会\expandafer非常不同,也非常无用)或用于标记可以具有的其他含义。作为最后一种可能性的一个例子,说

\let\newname=\MyList

会将 的当前含义(而不是内容!)转移\MyList\newname。由于\MyList代表 TeX 的内部标记列表寄存器,这使得\newname代表相同的寄存器:它成为一个别名对于\MyList,并且分配给其中一个也会改变另一个的内容。

相关内容