我想在每段末尾执行一些操作(将某物写入辅助文件)。所以我修改了\par
使用
\let\oldpar\par
\def\par{%
\protected@write\@auxout{}{\string\ParHere}%
\oldpar% original version of \par
}%
效果很好,直到使用了基于的环境trivlist
(enumerate
、、itemize
等) center
,quote
它似乎神奇地重置\par
为原始的\par
。
梅威瑟:
\documentclass{article}
\usepackage{etoolbox}
\makeatletter
\def\ParHere{}
\let\oldpar\par
\def\par{%
\protected@write\@auxout{}{\string\ParHere}%
\oldpar% original version of \par
}
%Attempt for solution which doesn't work:
%\appto\endtrivlist{%
% \gdef\par{%
% \protected@write\@auxout{}{\string\ParHere}%
% \oldpar%
% }%
%}
\makeatother
\begin{document}
Hallo % Makes \ParHere appearing in aux-file.
\begin{center}
Centered Text.
\end{center}
More paragraphs
But not more ParHere entries created in the auxfile.
asd
\end{document}
因此,它似乎\par
在某种trivlist
环境中以某种方式重置了——很好。但真正让我头疼的是:即使我添加了
\appto\endtrivlist{%
\gdef\par{%
\protected@write\@auxout{}{\string\ParHere}%
\oldpar%
}%
}
到序言,命令在(或任何其他基于)环境\par
之后不会恢复正常。但是如果我手动引入center
trivlist
\makeatletter
\def\par{%
\protected@write\@auxout{}{\string\ParHere}%
\oldpar%
}
\makeatother
之后\end{center}
,一切正常。
那么,为什么我不能\par
使用重置为我的自定义定义\appto\endtrivlist
?有人有解决方案吗?
PS:我猜全局重新定义通常不太优雅\par
?如果是这样,我还能做什么?请注意,重新定义\everypar
对我来说不起作用,因为我确实需要在段落末尾执行 aux-file-write。
答案1
您的第一个问题“为什么我不能\par
使用重置为我的自定义定义\appto\endtrivlist
?”很容易回答,但您的第二个问题,即您询问该怎么做的建议,要求您更详细地说明您想要实现的目标——请注意,这只是必要条件,但不是充分条件。(:-)
让我们详细看看当你执行时会发生什么,比如说,
\end{center}
TeX 编译器实际上做的第一件事就是扩展宏\end
,它在 LaTeX 内核中定义如下:
\def\end#1{%
\csname end#1\endcsname\@checkend{#1}%
\expandafter\endgroup\if@endpe\@doendpe\fi
\if@ignore\@ignorefalse\ignorespaces\fi}
(参见文件latex.ltx
第 4157-4160 行;但请注意,我目前正在编写的计算机上安装的此文件的版本是 2017/01/01,补丁级别 1:我不确定它是否是最新版本)。因此,下一步是执行
\csname endcenter\endcsname
也就是说,\endcenter
这是在第 4171 行定义的:
\def\endcenter{\endtrivlist}
好的,此时\endtrivlist
执行了,我们假设它包含您自定义的附加内容,用于重置 的含义\par
。但作为 扩展的一部分,还有更多代码等待执行\end{center}
。下一部分是
\@checkend{center}
\begin
这是检查和声明是否正确配对的宏\end
,在我们当前的讨论中不起作用。接下来是
\expandafter\endgroup\if@endpe\@doendpe\fi
这个结构扩展了\if@endpe
条件前结束当前组(即由 启动的组\begin{center}
):这是什么?好吧,如果你看一下 的定义\endtrivlist
(在我的版本中latex.ltx
,从第 4616 行开始),你会看到最后执行的一件事是宏\@endparenv
(@noparlist
在我们的上下文中,switch 肯定为 false — 前提是你的center
环境不为空 — 我必须请你相信我,因为我无法解释所有细节);这个宏在下面的第 4638-4639 行定义:
\def\@endparenv{%
\addpenalty\@endparpenalty\addvspace\@topsepadd\@endpetrue}
您可以看到,@endpe
开关最后被设置为 true。
现在让我们回到我们的
\expandafter\endgroup\if@endpe\@doendpe\fi
因此,我们刚刚看到 switch@endpe
在执行过程中被设置为 true \endtrivlist
,并且——请注意——我们现在正在检查它的值前结束该组,所以我们确实发现它仍然为真。此时,以下标记仍在等待执行:
\engroup \@doendpe \fi \if@ignore \@ignorefalse \ignorespaces \fi
具有真条件待定。因此,接下来发生的事情是当前组结束(请注意,这会撤消您对 含义的自定义重置\par
,但这还不是解释,因为此时,您希望\par
恢复它在环境开始之前的含义center
)。然后\@doendpe
执行。
和这里解释就在于此。
事实上,\@doendpe
定义如下(第 4640 至 4645 行,即\@endparenv
我们刚才看到的定义的正下方):
\def\@doendpe{\@endpetrue
\def\par{\@restorepar
\clubpenalty\@clubpenalty
\everypar{}\par\@endpefalse}\everypar
{{\setbox\z@\lastbox}%
\everypar{}\@endpefalse}}
我们看到,当\@doendpe
执行时,\par
它会被再次重新定义。
现在这些应该足够了。当然,还需要更多的洞察力来决定下一步做什么,甚至判断是否还有希望实现你的意图,即找出一种可接受的重新定义方法\par
,同时,这种方法要足够强大,能够经受住这个控制序列在 LaTeX 内核中经历的所有曲折,而不仅仅是在那里。如果你现在正在考虑将补丁从 移动\endtrivlist
到\@doendpe
,请记住,这个宏设置了 的定义是\par
有充分理由的,所以你不能简单地用你自己的定义覆盖它。我很遗憾地说,和 DC 一样,我倾向于认为你的想法注定要失败。
如果可以的话,最后我想提出几个你应该问自己的问题:
列表中的段落应该如何处理?它们应该与其他段落以相同方式处理,还是以不同方式处理?
与问题 1 相同,针对构成节标题的段落。
(深呼吸。)