在每个段落的末尾执行命令?

在每个段落的末尾执行命令?

我想在每段末尾执行一些操作(将某物写入辅助文件)。所以我修改了\par使用

\let\oldpar\par
\def\par{%
    \protected@write\@auxout{}{\string\ParHere}%
    \oldpar% original version of \par
}%

效果很好,直到使用了基于的环境trivlistenumerate、、itemize等) centerquote它似乎神奇地重置\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之后不会恢复正常。但是如果我手动引入centertrivlist

\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. 列表中的段落应该如何处理?它们应该与其他段落以相同方式处理,还是以不同方式处理?

  2. 与问题 1 相同,针对构成节标题的段落。

(深呼吸。)

相关内容