我想\temp
使用以下代码在宏中存储并递归更新数学表达式:
\documentclass{article}
\begin{document}
\def\temp{}
\def\update#1_#2{
\xdef\temptemp{\temp}
\gdef\temp{\temptemp\frac{#1}{#2}}}
\update{a}_{b}
\[\temp\]
\update{c}_{d}
\[\temp\]
\end{document}
该代码运行良好,给出 a/b 和 a/bc/d。
但是,一旦我将其更改\frac{#1}{#2}
为\underbrace{#1}_{#2}
,它就会返回错误“!的定义中的参数数量非法\temptemp
。”
这是什么问题?我该如何解决?提前致谢。
答案1
您的代码似乎开始工作。让我们添加一些跟踪,以便更好地查看发生了什么:
\documentclass{article}
\begin{document}
\def\temp{}
\def\update#1_#2{
\xdef\temptemp{\temp}
\gdef\temp{\temptemp\frac{#1}{#2}}}
\update{a}_{b}
\texttt{\meaning\temp}
\[\temp\]
\update{c}_{d}
\texttt{\meaning\temp}
\[\temp\]
\update{e}_{f}
\texttt{\meaning\temp}
\[\temp\]
\texttt{\meaning\temptemp}
\end{document}
如您所见,您并没有\temp
使用新部分进行更新;您\temptemp
实际上正在更新。图片中的最后一部分显示您并没有真正获得\frac{a}{b}
,但是它如何\xdef
处理它,这有点难以预测,因为绝大多数排版命令不应该在\edef
或中使用\xdef
。而 的情况更糟\underbrace
。
你真的不需要任何特殊的东西:LaTeX 内核有\g@addto@macro
\documentclass{article}
\newcommand{\container}{}
\newcommand\update{}
\makeatletter
\def\update#1_#2{%
\g@addto@macro\container{\underbrace{#1}_{#2}}%
}
\makeatother
\setlength{\parindent}{0pt}% just for this example
\begin{document}
\update{a}_{b}
\texttt{\meaning\container}
\[\container\]
\update{c}_{d}
\texttt{\meaning\container}
\[\container\]
\update{e}_{f}
\texttt{\meaning\container}
\[\container\]
\end{document}
答案2
您正在\xdef
对由 声明的前一个宏进行完全扩展(),\update
但是\underbrace
它是一个更复杂的宏,它在前言中使用了\halign
with ,如果不立即处理,就无法完全扩展。#
您可以更新不带扩展的数学公式列表:
\def\update#1_#2{\addto\temp{\underbrace{#1}_{#2}}}
其中\addto
定义为:
\long\def\addto#1#2{\expandafter\def\expandafter#1\expandafter{#1#2}}
答案3
underbrace
是受 LaTeX\protect
机制保护的强大命令。要使该机制正常工作,您需要以下protected@
函数。
\documentclass{article}
\begin{document}
\def\temp{}
\makeatletter
\def\update#1_#2{%
\protected@xdef\temptemp{\temp}%
\gdef\temp{\temptemp\underbrace{#1}_{#2}}}
\update{a}_{b}
\[\temp\]
\update{c}_{d}
\[\temp\]
\end{document}
TeX 中的扩展控制很复杂。此外,在这种情况下,您根本不需要 x 扩展。有专门的宏用于此目的。
\documentclass{article}
\usepackage{etoolbox}
\begin{document}
\def\temp{}
\makeatletter
\def\update#1_#2{%
\appto\temp{\underbrace{#1}_{#2}}}
\update{a}_{b}
\[\temp\]
\update{c}_{d}
\[\temp\]
\end{document}
或者参见\tl_put_right:Nn
expl3,但此处下划线的 catcode 存在一些“复杂情况”。
答案4
\temp
许多 LaTeX 软件包都有一个临时宏。因此,不是源自您的代码可能会覆盖\temp
源自您编写的代码的(重新)定义。
为了防止此类冲突,下文中\Mytemp
使用 代替\temp
。
Donald E. Knuth 在他的 TeXbook 中将 TeX 比喻为有眼睛、嘴巴和消化道的野兽。
我尝试用这个类比来概述 .tex 文件处理的不同阶段,以回答
\def
使用/\gdef
时,当组成定义文本的标记进入 TeX 的食道时,扩展会被抑制。因此,使用/\def
时\gdef
,组成定义文本的标记在到达执行分配的胃部时不会扩展。
当构成定义文本的标记进入 TeX 的食道时,带/\edef
的扩展不会被抑制。因此,带/ 的构成定义文本的标记在“反流过程”中到达执行分配的胃部时会“完全”扩展。\xdef
\edef
\xdef
关键点在于:\underbrace
本身是一个宏,在某个扩展阶段会产生一些#
。 当扩展完成并在 TeX 的胃中尝试执行底层赋值时\xdef
,这些#
会被错误地用作要定义/重新定义的宏的某些参数的引用。
除此之外,您可能还想应对这样一个事实,即定义可以嵌套在定义内部,从而每个“嵌套级别”都需要另一级别的双倍哈希来表示该定义级别中的定义参数,并且扩展宏会将该宏的定义文本中出现的两个连续哈希减少为一个哈希。
例如,如果你这样做
\long\def\hashcarrier{################A}
\long\xdef\hashcarrier{\hashcarrier B}
\show\hashcarrier
\long\xdef\hashcarrier{\hashcarrier C}
\show\hashcarrier
\long\xdef\hashcarrier{\hashcarrier D}
\show\hashcarrier
%% If you activate the following lines you get error-message about
%% illegal parameter number in definition of \hashcarrier because
%% things were reduced to single hashes that are taken for reference
%% to parameters of the current definition-level while the parameter-text
%% of the current definition-level doesn't denote any parameter at all
%% and parameters must be numbered consecutively from 1 to 9 instead of
%% a thing like A:
%\long\xdef\hashcarrier{\hashcarrier E}
%\show\hashcarrier
\csname bye\endcsname
\stop
您会在终端/.log 文件中得到以下输出
> \hashcarrier=\long macro:
->########AB.
l.3 \show\hashcarrier
?
> \hashcarrier=\long macro:
->####ABC.
l.5 \show\hashcarrier
?
> \hashcarrier=\long macro:
->##ABCD.
l.7 \show\hashcarrier
?
(\long
意味着如果所讨论的宏处理参数,则每个参数可能包含控制字标记\par
。)
\xdef
您可以通过和来防止不必要的扩展并减少哈希数量\unexpanded
:
\documentclass{article}
\newcommand\Mytemp{}%
\long\def\updateMytemp#1_#2{%
\long\xdef\Mytemp{\unexpanded\expandafter{\Mytemp\underbrace{#1}_{#2}}}%
}%
\begin{document}
\updateMytemp{a}_{b}
\show\Mytemp
\[\Mytemp\]
\updateMytemp{c}_{d}
\show\Mytemp
\[\Mytemp\]
\end{document}
或者 -- 传统风格 -- 通过标记寄存器的扩展\xdef
:\the
\documentclass{article}
\newtoks\MyScratchtoks
\newcommand\Mytemp{}%
\long\def\updateMytemp#1_#2{%
\MyScratchtoks\expandafter{\Mytemp\underbrace{#1}_{#2}}%
\long\xdef\Mytemp{\the\MyScratchtoks}%
}%
\begin{document}
\updateMytemp{a}_{b}
\show\Mytemp
\[\Mytemp\]
\updateMytemp{c}_{d}
\show\Mytemp
\[\Mytemp\]
\end{document}
我建议使用 LaTeX 做一些更通用的事情\g@addto@macro
:
\documentclass{article}
\newcommand\Mytemp{}%
\makeatletter\newcommand\updateMytemp[1]{\g@addto@macro{\Mytemp}{#1}}\makeatother
\begin{document}
\updateMytemp{\underbrace{a}_{b}}
\show\Mytemp
\[\Mytemp\]
\updateMytemp{\underbrace{c}_{d}}
\show\Mytemp
\[\Mytemp\]
\end{document}
通过所有三个示例,您会在控制台和 .log 文件中看到类似以下消息:
> \Mytemp=macro:
->\underbrace {a}_{b}.
l.9 \show\Mytemp
?
> \Mytemp=macro:
->\underbrace {a}_{b}\underbrace {c}_{d}.
l.13 \show\Mytemp
通过所有三个示例,您将获得以下 pdf 输出:
另一个提示:
如果要定义的命令已定义或由于名称中包含前导短语“end”而无法定义,则LaTeX 的\newcommand
等会触发错误消息。通过将通过/ / /执行的分配包装到 LaTeX 的-test 中,您可以实现相同的行为。\NewDocumentCommand
\def
\gdef
\edef
\xdef
\@ifdefinable
例如,不仅仅是
\long\def\updateMytemp#1_#2{%
\long\xdef\Mytemp{\unexpanded\expandafter{\Mytemp\underbrace{#1}_{#2}}}%
}%
你会做
\makeatletter
\@ifdefinable{\updateMytemp}{%
\long\def\updateMytemp#1_#2{%
\long\xdef\Mytemp{\unexpanded\expandafter{\Mytemp\underbrace{#1}_{#2}}}%
}%
}%
\makeatother
如果你想知道\makeatletter
/\makeatother
是关于什么的:
简化版的 TeX/LaTeX 通常只允许普通字母a..z
/A..Z
作为控制字标记名称的组成部分。
\makeatletter
是一个指令,用于告诉 LaTeX,从今以后@
也应允许将其作为控制字标记名称的组成部分。
\makeatother
是一个指令,用于告诉 LaTeX,从今以后@
将不允许将 a 作为控制字标记名称的组成部分。
如果没有\makeatletter
/,\makeatother
您可以使用它\csname..\endcsname
来从介于两者之间的内容中收集控制序列标记的名称\csname..\endcsname
并传递该控制序列标记:
\csname @ifdefinable\endcsname{\updateMytemp}{%
\long\def\updateMytemp#1_#2{%
\long\xdef\Mytemp{\unexpanded\expandafter{\Mytemp\underbrace{#1}_{#2}}}%
}%
}%