理解 LaTeX 2e 的 \@esphack

理解 LaTeX 2e 的 \@esphack

我正在阅读 source2e.pdf 但在理解 LaTeX 2ε 的定义的含义方面遇到了问题\@esphack

类似 的命令\label{...}本身不会/不应产生任何可见的输出,在源/.tex 输入文件中可能会被空格包围。如果是这样,您不需要两个空格标记,因为这会使水平粘连加倍。

如果我理解正确的话,\@bsphack那么\@esphack在标记和处理命令之后,它们可以避免出现空间标记,以防在标记和处理命令之前已经出现了一个空间标记。

让我们看看的定义\@bsphack

> \@bsphack=macro:
->\relax \ifhmode \@savsk \lastskip \@savsf \spacefactor \fi .
l.1    \show\@bsphack

换句话说:

当处于水平模式或受限水平模式时,将 的值保存为\lastskip\@savsk并将 的值保存\spacefactor\@savsf

现在让我们看一下的定义\@esphack

> \@esphack=macro:
->\relax \ifhmode \spacefactor \@savsf \ifdim \@savsk >\z@ \ifdim \lastskip =\z
@ \nobreak \hskip \z@skip \fi \ignorespaces \fi \fi .
l.2    \show\@esphack

换句话说:

当处于水平模式或受限水平模式时,则:

  • 将 的值恢复\spacefactor为 保存的值\savsf
  • 如果\@savsk大于零,即,如果在执行开始时调用的命令之前存在一些水平粘连\@bsphack,则执行
    \ifdim \lastskip =\z@ \nobreak \hskip \z@skip \fi \ignorespaces

我理解这\ignorespaces很有意义,因为在执行开始时调用的命令之前已经存在一些水平粘连\@bsphack

但我不明白
\ifdim \lastskip =\z@ \nobreak \hskip \z@skip \fi -part 有什么用处。

这有什么意义呢?

在执行这种情况时, 中保存的值\@savsk大于\z@,因此可以得出结论,在启动时调用的命令中的某些内容确实将\@bsphack的值更改为。\lastskip\z@

但执行类似的操作\nobreak\hskip\z@skip不会撤销该更改/不会恢复\lastskip到其先前的值。

\hskip因此:执行零宽度的要点/意义/好处是什么?

如果您希望\lastskip恢复,那么不应该是这样的:

\def\@esphack{%
  \relax
  \ifhmode
    \spacefactor\@savsf
    \ifdim\@savsk>\z@
      \ifdim\lastskip=\z@
        \nobreak
        \hskip-\@savsk
        \nobreak
        \hskip\@savsk
        % the total skip is zero and \lastskip is restored.
      \fi
      \ignorespaces
    \fi
  \fi
}%

或者也许只是

\def\@esphack{%
  \relax
  \ifhmode
    \spacefactor\@savsf %now \spacefactor is restored.
    \nobreak
    \hskip-\@savsk
    \nobreak
    \hskip\@savsk
    % the total skip is zero and \lastskip is restored.
    \ifdim\@savsk>\z@
      \ignorespaces
    \fi
  \fi
}%

是否有可能在之前的某个版本中,是这样完成的,而在后来的版本中,有人错误地将其“优化” \nobreak\hskip-\@savsk\nobreak\hskip\@savsk\nobreak\hskip\z@skip,而忽略了不再恢复这一功能\lastskip

如果我理解正确的话,您需要\lastskip正确恢复,否则具有连续序列的东西\@bsphack..\@esphack(例如)\label{foo}\label{bar}将无法正常工作:

如果现在你做了类似
A \label{foo} \label{bar} A
或的事情
A \label{foo}\label{bar} A
\lastskip在任何情况下都会为 0,之后\label{foo}会影响的行为,\label{bar}因为\label{bar} \@savsk不会大于\z@任何,所以\ignorespaces虽然应该执行,但不会执行!

通过下面的例子,你可以看到重新定义时出现的细微差别,\@esphack从而可以恢复\lastskip

\documentclass{article}

\newsavebox\mybox

\begin{document}

\par\noindent\begin{lrbox}{\mybox}
\verb*|A\label{1}B|:
\end{lrbox}\hbox to 4.5cm{\usebox\mybox\hfil} A\label{1}B

\par\noindent\begin{lrbox}{\mybox}
\verb*|A \label{2} B|:
\end{lrbox}\hbox to 4.5cm{\usebox\mybox\hfil} A \label{2} B

\par\noindent\begin{lrbox}{\mybox}
\verb*|A \label{3}\label{4} B|:
\end{lrbox}\hbox to 4.5cm{\usebox\mybox\hfil} A \label{3}\label{4} B

\par\noindent\begin{lrbox}{\mybox}
\verb*|  \label{5} \label{6} B|:
\end{lrbox}\hbox to 4.5cm{\usebox\mybox\hfil}  A \label{5} \label{6} B

\makeatletter
\def\@esphack{%
  \relax
  \ifhmode
    \spacefactor\@savsf
    \ifdim\@savsk>\z@
      \ifdim\lastskip=\z@
        \nobreak
        \hskip-\@savsk
        \nobreak
        \hskip\@savsk
        % the total skip is zero and \lastskip is restored.
      \fi
      \ignorespaces
    \fi
  \fi
}%
\makeatother

\noindent\null\hrulefill\null

\par\noindent\begin{lrbox}{\mybox}
\verb*|A\label{a}B|:
\end{lrbox}\hbox to 4.5cm{\usebox\mybox\hfil} A\label{a}B

\par\noindent\begin{lrbox}{\mybox}
\verb*|A \label{b} B|:
\end{lrbox}\hbox to 4.5cm{\usebox\mybox\hfil} A \label{b} B

\par\noindent\begin{lrbox}{\mybox}
\verb*|A \label{c}\label{d} B|:
\end{lrbox}\hbox to 4.5cm{\usebox\mybox\hfil} A \label{c}\label{d} B

\par\noindent\begin{lrbox}{\mybox}
\verb*|A \label{e} \label{f} B|:
\end{lrbox}\hbox to 4.5cm{\usebox\mybox\hfil}  A \label{e} \label{f} B

\end{document}

在此处输入图片描述

我的修改\@esphack有缺点吗?

我是否忽略了一些警告/相关内容?

答案1

您可以在源代码中看到更多更改注释和旧版本:

% \begin{macro}{\@esphack}
%   Companion to |\@bsphack|.  If this command is not properly paired
%   with |\@bsphack| one might end up with a low-level \TeX{} error:
%   ``BAD spacefactor''. One possible cause is calling |\@bsphack| in
%   vertical mode, then doing something that gets you (sometimes) into
%   horizontal mode and finally calling |\@esphack|. Even if no error
%   is generated that is wrong, because |\@esphack| will then use the
%   saved values for |\@savsk| and |\@savsf| from some earlier
%   invocation of |\@bsphack| which will have nothing to do with the
%   current situation.
% \changes{v1.3d}{2015/01/11}{Allow hyphenation (Donald Arseneau pr/3498) (latexrelease)}
%    \begin{macrocode}
%</2ekernel>
%<latexrelease>\IncludeInRelease{2018/10/10}%
%<latexrelease>                 {\@esphack}{hyphenation and nobreak after space hack}%
%<*2ekernel|latexrelease>
\def\@esphack{%
  \relax
  \ifhmode
    \spacefactor\@savsf
    \ifdim\@savsk>\z@
%    \end{macrocode}
% \changes{v1.3f}{2015/11/07}
%         {Only space if there is no space at the end of the hlist latex/4443}
%    \begin{macrocode}
      \ifdim\lastskip=\z@ 
        \nobreak \hskip\z@skip
      \fi
      \ignorespaces
    \fi
%    \end{macrocode}
% \changes{v1.3i}{2018/10/10}
%         {Don't introduce breakpoints if @nobreak is true and after sections}
%    \begin{macrocode}
  \else
    \ifvmode
      \if@nobreak\nobreak\else\if@noskipsec\nobreak\fi\fi
    \fi
%    \end{macrocode}
%
%    \begin{macrocode}
  \fi}%
%</2ekernel|latexrelease>
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2015/10/01}%
%<latexrelease>                 {\@esphack}{hyphenation and nobreak after space hack}%
%<latexrelease>\def\@esphack{%
%<latexrelease>  \relax
%<latexrelease>  \ifhmode
%<latexrelease>    \spacefactor\@savsf
%<latexrelease>    \ifdim\@savsk>\z@
%<latexrelease>      \ifdim\lastskip=\z@ 
%<latexrelease>        \nobreak \hskip\z@skip
%<latexrelease>      \fi
%<latexrelease>      \ignorespaces
%<latexrelease>    \fi
%<latexrelease>  \fi}%
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2015/01/01}%
%<latexrelease>                 {\@esphack}{hyphenation and nobreak after space hack}%
%<latexrelease>\def\@esphack{%
%<latexrelease>  \relax
%<latexrelease>  \ifhmode
%<latexrelease>    \spacefactor\@savsf
%<latexrelease>    \ifdim\@savsk>\z@
%<latexrelease>      \nobreak \hskip\z@skip
%<latexrelease>      \ignorespaces
%<latexrelease>    \fi
%<latexrelease>  \fi}%
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{0000/00/00}%
%<latexrelease>                 {\@esphack}{hyphenation and nobreak after space hack}%
%<latexrelease>\def\@esphack{%
%<latexrelease>  \relax
%<latexrelease>  \ifhmode
%<latexrelease>    \spacefactor\@savsf
%<latexrelease>    \ifdim\@savsk>\z@
%<latexrelease>      \ignorespaces
%<latexrelease>    \fi
%<latexrelease>  \fi}%
%<latexrelease>\EndIncludeInRelease
%<*2ekernel>
%    \end{macrocode}
% \end{macro}

基本上,您查询的位,您需要添加一个空格(或零宽度)以免抑制连字符,然后需要添加\nobreak以便空格不会引入断点。


由于您知道如何使用\hskip-\zzz\hskip\zzz以下测试,因此将 0pt 跳过拆分为此处效果更好,但是,这会使水平列表稍微容易受到任何以下删除跳过(而不仅仅是测试它)的影响,因为它只会删除一半的对,从而在列表中留下可能意外的负跳过。\lastskip\unskip

我想出了这个表格示例,尽管可以说在这种情况下结果也更好,因为剩下的负跳过抵消了第一个索引之前的原始空间。但其他合理用途\unskip需要检查......

在此处输入图片描述

\documentclass{article}

\newsavebox\mybox
\makeindex
\tabcolsep=0pt
\begin{document}

\par\noindent\begin{lrbox}{\mybox}
\verb*|A\label{1}B|:
\end{lrbox}\hbox to 4.5cm{\usebox\mybox\hfil} A\label{1}B

\par\noindent\begin{lrbox}{\mybox}
\verb*|A \label{2} B|:
\end{lrbox}\hbox to 4.5cm{\usebox\mybox\hfil} A \label{2} B

\par\noindent\begin{lrbox}{\mybox}
\verb*|A \label{3}\label{4} B|:
\end{lrbox}\hbox to 4.5cm{\usebox\mybox\hfil} A \label{3}\label{4} B

\par\noindent\begin{lrbox}{\mybox}
\verb*|  \label{5} \label{6} B|:
\end{lrbox}\hbox to 4.5cm{\usebox\mybox\hfil}  A \label{5} \label{6} B

\begin{tabular}{|r|}
A A \index{A}\\
B B \index{B}\\
\end{tabular}


\makeatletter
\def\@esphack{%
  \relax
  \ifhmode
    \spacefactor\@savsf
    \ifdim\@savsk>\z@
      \ifdim\lastskip=\z@
        \nobreak
        \hskip-\@savsk
        \nobreak
        \hskip\@savsk
        % the total skip is zero and \lastskip is restored.
      \fi
      \ignorespaces
    \fi
  \fi
}%
\makeatother

\noindent\null\hrulefill\null

\par\noindent\begin{lrbox}{\mybox}
\verb*|A\label{a}B|:
\end{lrbox}\hbox to 4.5cm{\usebox\mybox\hfil} A\label{a}B

\par\noindent\begin{lrbox}{\mybox}
\verb*|A \label{b} B|:
\end{lrbox}\hbox to 4.5cm{\usebox\mybox\hfil} A \label{b} B

\par\noindent\begin{lrbox}{\mybox}
\verb*|A \label{c}\label{d} B|:
\end{lrbox}\hbox to 4.5cm{\usebox\mybox\hfil} A \label{c}\label{d} B

\par\noindent\begin{lrbox}{\mybox}
\verb*|A \label{e} \label{f} B|:
\end{lrbox}\hbox to 4.5cm{\usebox\mybox\hfil}  A \label{e} \label{f} B

\begin{tabular}{|r|}
A A \index{A}\\
B B \index{B}\\
\end{tabular}

\end{document}

我认为我们可以使用 e-tex测试来避免添加虚假的 -ve/+ve 胶水对或虚假的 1sp 胶水节点,而不是将其用作\ifdim\lastskip>0pt“是否存在前置胶水”的测试:\ifnum\lastnodetype=11

\documentclass{article}

\newsavebox\mybox
\makeindex
\tabcolsep=0pt
\begin{document}

\par\noindent\begin{lrbox}{\mybox}
\verb*|A\label{1}B|:
\end{lrbox}\hbox to 4.5cm{\usebox\mybox\hfil} A\label{1}B

\par\noindent\begin{lrbox}{\mybox}
\verb*|A \label{2} B|:
\end{lrbox}\hbox to 4.5cm{\usebox\mybox\hfil} A \label{2} B

\par\noindent\begin{lrbox}{\mybox}
\verb*|A \label{3}\label{4} B|:
\end{lrbox}\hbox to 4.5cm{\usebox\mybox\hfil} A \label{3}\label{4} B

\par\noindent\begin{lrbox}{\mybox}
\verb*|  \label{5} \label{6} B|:
\end{lrbox}\hbox to 4.5cm{\usebox\mybox\hfil}  A \label{5} \label{6} B

\begin{tabular}{|r|}
A A \index{A}\\
B B \index{B}\\
\end{tabular}


\makeatletter
\def\@bsphack{%
  \relax
  \ifhmode
    \@savsk\lastskip
    \ifdim\@savsk=\z@\ifnum\lastnodetype=11 \@savsk1sp \fi\fi
    \@savsf\spacefactor
  \fi}


% \def\@esphack{%
%   \relax
%   \ifhmode
%     \spacefactor\@savsf
%     \ifdim\@savsk>\z@
%       \ifdim\lastskip=\z@
%         \nobreak
%         \hskip1sp
%         % the total skip is zero and \lastskip is restored.
%       \fi
%       \ignorespaces
%     \fi
%   \fi
% }%

\makeatother

\noindent\null\hrulefill\null

\par\noindent\begin{lrbox}{\mybox}
\verb*|A\label{a}B|:
\end{lrbox}\hbox to 4.5cm{\usebox\mybox\hfil} A\label{a}B

\par\noindent\begin{lrbox}{\mybox}
\verb*|A \label{b} B|:
\end{lrbox}\hbox to 4.5cm{\usebox\mybox\hfil} A \label{b} B

\par\noindent\begin{lrbox}{\mybox}
\verb*|A \label{c}\label{d} B|:
\end{lrbox}\hbox to 4.5cm{\usebox\mybox\hfil} A \label{c}\label{d} B

\par\noindent\begin{lrbox}{\mybox}
\verb*|A \label{e} \label{f} B|:
\end{lrbox}\hbox to 4.5cm{\usebox\mybox\hfil}  A \label{e} \label{f} B

\begin{tabular}{|r|}
A A \index{A}\\
B B \index{B}\\
\end{tabular}

\end{document}

相关内容