将宏 \gsetlength 定义为全局 setlength,可靠吗?

将宏 \gsetlength 定义为全局 setlength,可靠吗?

正如其他问题所指出的那样,例如全局范围或永久长度或保存箱,该\setlength命令并不总是具有全局效果。在许多情况下,这是可取的。但是当效果必须是全局时,使用\global\setlength通常不起作用。

可以说,我倾向于从全局考虑。在大多数情况下,当我设定一个长度时,我希望它能够被确定下来。所以我经常不得不写这样的表达式:\setlength\something{value}\global\something=\something。这很有效。

\gsetlength所以我问自己,为什么不定义一个可以完成所有工作的宏呢?MWE:

% !TeX TS-program = LuaLaTeX
% !TeX encoding = UTF-8
% Using LuaLaTeX because I use it in real documents. Probably the same in pdflatex.
\documentclass{article}
\usepackage{calc} % Because I use it in real documents.
\gdef\gsetlength#1#2{\setlength#1{#2}\global#1=#1} % Should this always be OK ?
%
\newlength\mylengthone
\setlength\mylengthone{1pt}
\newlength\mylengthtwo
\setlength\mylengthtwo{2pt}
%
\newlength\mylengththree
\gsetlength\mylengththree{3pt}
\newlength\mylengthfour
\gsetlength\mylengthfour{4pt}
%
\newlength\mytesta % one level of global
\gsetlength\mytesta{\mylengthone+\mylengthtwo}
\newlength\mytestb % two levels of global
\gsetlength\mytestb{\mylengththree+\mylengthfour}
%
\newlength\mytestc
{\setlength\mytestc{\mylengthone+\mylengthtwo}} % Grouped, not global
\newlength\mytestd
{\gsetlength\mytestd{\mylengthone+\mylengthtwo}} % Grouped, global.
%
\begin{document}
\the\mytesta\par % Expecting 3.0pt.
\the\mytestb\par % Expecting 7.0pt.
\the\mytestc\par % Expecting 0.0pt because \setlength was within group.
\the\mytestd\par % Expecting 3.0pt because \gsetlength.
\end{document}

上述代码按预期工作。现在我要问的是:我是否总能期望它正常工作,特别是因为我使用了该calc软件包?或者我是否有遇到隐藏扩展或 catcode 问题的危险?

使用\gsetlength可以使我的代码更短,更易读。但我很紧张...

编辑:根据 DC 的请求提供更长的 MWE。

EDIT2:对于大多数用户来说,David Carlisle 提供的信息就是您所需要的。也就是说,检查您的代码,确保您没有以这样的方式设置长度,即它被本地限制为一个组,而这个组可能会被多余的括号分隔开。

但就我而言,我接受了 Heiko 提供的答案。这是因为我有一个非常大的文档类,其中包含大量嵌套的条件和值,这些条件和值会被操纵并从一个地方传递到另一个地方。它们的数量并不多,不足以使用所有 TeX 资源,因为一旦我的主要文档文本开始,它就不会使用大量会阻塞 TeX 的代码。特别是,我没有使用 TiKz、参考书目或类似的东西。

后期编辑:我刚刚在包\deflength中发现了该命令etoolbox。根据其描述,它\global在请求时支持(与 不同\setlength)。

答案1

  • 本地和全球的混合任务已经由 David 的回答

  • #1宏定义以in结尾\global#1=#1非常危险。考虑#1\dimen1小于 10 的奇数寄存器编号用于全局分配)并且下一个标记是 '012345foo'。然后,TeX 尝试分配\global\dimen1=\dimen1012345。通过以 结尾分配可以解决这个问题\relax

改进版本的建议:

\makeatletter
\gdef\gsetlength#1#2{%
  \begingroup
    \setlength\skip@{#2}% Local assignment to a scratch register.
    \global#1=\skip@    % Global assignement to #1;
                        % \relax is not necessary because of the following \endgroup.
  \endgroup             % \skip@ is restored by end of group.
}
\makeatother

将 的本地赋值给组内的\setlength本地暂存寄存器。然后对 进行全局赋值。组结束后, 的本地更改值将恢复,但全局赋值将保留该值。\skip@#1\skip@\skip@

答案2

现代系统上的保存堆栈比以前大得多,但该\global#1=#1习语仍然会用尽它,如果使用得足够频繁,将导致致命错误

\documentclass{article}

\newlength\zzz
\newlength\Lzzz

\newcount\ccc

\begin{document}

{

\loop
\Lzzz=5pt \global\zzz=\Lzzz
\Lzzz=6pt \global\zzz=\Lzzz
\ifnum\ccc<20000
\advance\ccc 1
\repeat

}
\end{document}

运行无错误,日志显示

 23i,1n,17p,109b,36s stack positions out of 5000i,500n,10000p,200000b,80000s

因此最多使用了 36 个堆栈位置(这是在 latex 核心代码中,而不是文档中可见的代码)

但是如果您尝试使用单一长度的版本并对同一寄存器进行本地和全局设置:

\documentclass{article}

\newlength\zzz

\newcount\ccc

\begin{document}

{

\loop
\zzz=5pt \global\zzz=\zzz
\zzz=6pt \global\zzz=\zzz
\ifnum\ccc<20000
\advance\ccc 1
\repeat

}
\end{document}

然后你就得不到任何输出:

! TeX capacity exceeded, sorry [save size=80000].
<to be read again> 
                   \global 
l.16 \repeat

!  ==> Fatal error occurred, no output PDF file produced!

并且所有 80000 个保存堆栈位置均已用于保存寄存器的值。

这就是为什么寄存器总是在本地使用和全局使用之间分开的原因,例如,具有奇数的临时寄存器 1,3,5,7,9 总是全局使用,而具有偶数的临时寄存器 0,2,4,6,8 总是在本地使用。

相关内容