我很高兴能想出一种方法tabular
使用结构构建环境的内容\foreach \x in {...} {...}
。
但我很好奇到底需要什么才能破解我的代码。这是我的序言,适用于一般情况非复杂内容(无论其含义是什么):
\documentclass{article}
\usepackage[margin=0.5in]{geometry}
\usepackage{amsmath,amssymb,array}
\usepackage{booktabs}
\usepackage{etoolbox}
\usepackage{pgfkeys,pgffor}
\pgfkeys{/ae/agenda/.cd,
items for the agenda/.initial=,
title/.initial=,
}
\def\aeset#1{\pgfkeys{/ae/agenda/.cd,#1}}
\def\aeget#1{\pgfkeysvalueof{/ae/agenda/#1}}
\makeatletter
\newcounter{agendaitemcounter}
\def\agenda@item@number{%%
\stepcounter{agendaitemcounter}%%
\arabic{agendaitemcounter}.)}
\def\full@agenda{}
\long\def\build@agenda{%%
\foreach \x in \list@of@agenda@items {
\xdef\full@agenda{%%
\expandonce\full@agenda
\expandonce\agenda@item@number
\expandonce\aeamp
\expandonce\x
\noexpand\\
\noexpand\midrule}
}}
\def\setagenda#1{%%
\bgroup
\aeset{#1}%%
\edef\list@of@agenda@items{\aeget{items for the agenda}}%%
\build@agenda
\begin{minipage}[t]{4in}\centering
{\large \aeget{title}}%%
\par\vspace{1ex}
\begin{tabular}{cp{3in}}\toprule
\full@agenda \bottomrule
\end{tabular}%%
\end{minipage}%%
\setcounter{agendaitemcounter}{0}%%
\egroup
\def\full@agenda{}%%
}
\makeatother
\renewcommand{\arraystretch}{1.5}
\def\aeamp{&}
\def\aepar{\par}
\def\dollars#1{\$#1}%$
\def\mypoly{\[x^2+1\]}
\pagestyle{empty}
因此,我开始尝试一些东西来查看序言中的宏会在哪里中断:找到某些东西并不难。
下面的示例代码按照上面的序言编写,可以很好地编译(除了包含的一行注释掉的行\noexpand\par
)。但是,如果我开始\noexpand
从这些中删除命令在职的行,错误开始弹出。
我不太明白为什么某些结构不会破坏(例如\frac{...}{...}
,x^2
和\infty
),而其他表面上相似或更简单的结构(例如\cos
或\$
)会破坏我的代码。
\begin{document}
\setagenda{
title={To-do list},
items for the agenda={%%
{Line of random content, but nothing too fancy.},
{Not too involved math: $x\cdot y$ },
{A bit more involved, but it works! $\displaystyle{}\frac{1}{2d}$ },
{ Random collection of math symbols: $\inf \infty x^2+x$ },
%% I need to use `\noexpand` to avoid getting an error.
{This line works!\noexpand\aepar Hello world }
%%{This line fails!\noexpand\par Hello world }
{ what follows fails without \noexpand\texttt{noexpand}},
{ Trying something which covers multiple lines
\belowdisplayskip=0pt%%
\belowdisplayshortskip=0pt%%
\noexpand\mypoly
},
{Transfer \noexpand\dollars{3000} for trip to Venice},
{ $x\noexpand\cos(x)$ How many suitcases do I need?},
{ What's this going to result in?
\belowdisplayskip=0pt%%
\belowdisplayshortskip=0pt%%
\noexpand\[ x^2+1\noexpand\]
}
}%%
}
\vspace{1cm}
\setagenda{
title={something else},
items for the agenda={%%
{first item},
{second item}}}
\end{document}
为什么有些数学命令有效,而其他的无效。我认为,因为我正在发出\expandonce
用于构建环境内容的宏tabular
,所以不应该出现这些错误。
更详细地讲,当我尝试以微型视角观察这一现象时,在没有环境噪音的情况下tabular
,我会更加困惑。
以下 MWE 可以编译:
\documentclass{article}
\usepackage{etoolbox}
\usepackage{pgffor}
\begin{document}
\let\mytrial\relax
\foreach \x in {$\cos$,
$x^2+x$}
{\xdef\mytrial{\x}}
\mytrial
\end{document}
\cos
因此,和一起使用并没有什么不好的\xdef
。但是当我尝试让这段代码看起来更像上面的代码时\setagenda
,事情就变得有些糟糕了,我无法理解。
\documentclass{article}
\usepackage{etoolbox}
\usepackage{pgffor}
\begin{document}
\let\mytrial\relax
\foreach \x in {$\cos$,
$x^2+x$}
{\xdef\mytrial{\mytrial\x}}
\mytrial
\end{document}
我甚至没有收到任何错误。TeX 只是进入了一个无限循环(就好像里面有一个 LaTeX3'ish 夸克之类的东西)。
好吧,我有\let
一些东西要放在\relax
那里。所以我尝试了
\documentclass{article}
\usepackage{etoolbox}
\usepackage{pgffor}
\begin{document}
\def\mytrial{\relax}
%%\def\mytrial{}%%<--- this line works too!
\foreach \x in {$\cos$,
$x^2+x$}
{\xdef\mytrial{\mytrial\x}}
\mytrial
\end{document}
它确实有效,这让我大吃一惊!最后一个例子和我在里面做的有什么不同?里面\setagenda
甚至没有任何命令来\expandonce
节省以后再说。
我的理解\noexpand\x
是
\xdef\mycmd{\noexpand\somecmd}
结果是
\mycmd=\somecmd
我的理解\expandonce\x
是\x=\noexpand\cos
\xdef\mycmd{\expandonce\x}
结果是
\mycmd=\noexpand\cos
所以如果我有类似的东西
\mycmd=\noexpand\cos
\x=\noexpand\tan
然后尝试
\xdef\mycmd{\expandonce\mycmd \expandonce\x}
我期望结果是
\mycmd=\noexpand\cos \noexpand\tan
发生了什么?我不明白什么?我想我应该得出结论,环境内部发生了一些事情tabular
,我还没有用我的例子挖掘出来。但我真的不确定那会是什么。
答案1
这个问题涉及的内容很多,很难一一分析。
让我们解决这个\cos
问题。如果没有amsmath
,它的定义是
*\show\cos
\cos:
macro:->\mathop {\operator@font cos}\nolimits
(此处以及下文中,我将输出报告为交互式会话;*
是交互式会话中的提示)。 的扩展\operator@font
是\mathgroup\symoperators
两个不可扩展的标记。如果我们尝试,我们会得到
*\xdef\x{\cos}\show\x
> \x=macro:
->\mathop {\mathgroup \symoperators cos}\nolimits .
然而,当amsmath
加载时,情况就不同了:
*\RequirePackage{amsmath}
*\show\cos
\cos=macro:
->\qopname \relax o{cos}.
并且\qopname
是通过 定义的,\DeclareRobustCommand
所以它很可能不会存在\edef
(或者\xdef
只是\global\edef
)。事实上我们发现
% amsopn.sty, line 49:
\DeclareRobustCommand{\qopname}[3]{%
\mathop{#1\kern\z@\operator@font#3}%
\csname n#2limits@\endcsname}
和
% amsopn.sty, line 38:
\def\nolimits@{\@ifnextchar\limits{\nolimits\@gobble}{\nolimits}}
最终,罪魁祸首是:它包含\@ifnextchar
。在进行扩展定义时,应该使用\protected@xdef
来克服这个问题。例如,我们会得到
*\protected@xdef\x{\cos}\show\x
> \x=macro:
->\protect \qopname \relax o{cos}.
这并没有说明问题。
那\relax
问题呢?这很容易解释。当一个控制序列不可扩展时,\edef
不会对它做任何事情。并且\relax
不可扩展。您的代码
\let\mytrial\relax
\foreach \x in {a,b}
{\xdef\mytrial{\mytrial\x}}
(我使用 simplya
和b
你使用的 token 只会让事情变得更加复杂,但增加的复杂性无关紧要)相当于
\let\mytrial\relax
\xdef\mytrial{\mytrial a}
\xdef\mytrial{\mytrial b}
请注意,在处理 时\edef\cs{<balanced text>}
,TeX 首先通过进行扩展来计算替换文本,直到剩下不可扩展的标记为止;我们将结果称为<balanced text expanded>
;只有在执行此操作后,TeX 才会执行 的等效操作\def\cs{<balanced text expanded>}
。在执行最后一步时,TeX 不会以任何方式解释替换文本中的标记。
第一个 之后,的\xdef
展开式为,现在不再是不可展开的了。因此第二个会尝试展开;猜猜会发生什么?它如下\mytrial
\mytrial a
\mytrial
\xdef
\mytrial
\mytrial b -> \mytrial ab -> \mytrial aab -> ...
因为\edef
和\xdef
会“一直”进行下去,正如前面所说:它们会一直进行扩展,直到只剩下不可扩展的标记。当然,当你有 时\def\mytrial{}
,第一步的扩展将很简单a
,不会出现无限循环。