对于索引为 10 的框,Savebox 和 usebox 出现意外行为

对于索引为 10 的框,Savebox 和 usebox 出现意外行为

我发现,在保存并稍后使用盒子时,索引为 10 的盒子的行为与其他所有盒子的行为不同。

下面是一个展示不同行为的简单演示。在序言中我定义了以下命令:

\usepackage{forloop}
\newcounter{BoxCount}
\newcommand{\numberedBox}[1]{%
  \stepcounter{BoxCount}%
  \savebox{\theBoxCount}{\vbox{\noindent\theBoxCount\\~ #1}}%
}%
\newcommand{\writeBoxes}{
  \stepcounter{BoxCount}%
  \newcounter{I}%
  \setcounter{I}{1}%
  \forloop{I}{1}{\value{I} < \value{BoxCount}}{%
  \usebox{\theI}\par
}%

基本上\numbredBox将给定的文本存储在索引框中,同时将框编号添加到文本中。\writeBoxes按存储顺序打印所有存储的框。

在文档正文中我有以下代码:

\numberedBox{word}
\numberedBox{word}
\numberedBox{word}
\numberedBox{word}
\numberedBox{word}
\numberedBox{word}
\numberedBox{word}
\numberedBox{word}
\numberedBox{word}
\numberedBox{word}
\numberedBox{word}
\numberedBox{word}
\numberedBox{word}
\writeBoxes

这是本文档的打印输出: 在此处输入图片描述 所有框都按预期打印,除了第 10 个框,因为它以某种方式干扰了第 11 个框。我玩过类似的代码,发现第 10 个保存的框几乎总是表现得很奇怪。在某些情况下,我甚至会遇到! Incompatible list can't be unboxed错误,但仅限于第 10 个保存的框(所有框的内容相同)。

为什么会发生这种情况?我该如何避免?


编辑

这是 Hebert 要求的这个示例的完整源代码。只需使用 pdfLaTeX 进行编译,您就会得到上面显示的输出。

\documentclass[a5paper]{book}
\usepackage{forloop}
\newcounter{BoxCount}
\newcommand{\numberedBox}[1]{%
  \stepcounter{BoxCount}%
  \savebox{\theBoxCount}{\vbox{\noindent\theBoxCount\\~ #1}}%
}%
\newcommand{\writeBoxes}{
  \stepcounter{BoxCount}%
  \newcounter{I}%
  \setcounter{I}{1}%
  \forloop{I}{1}{\value{I} < \value{BoxCount}}{%
    \usebox{\theI}\par
  }%
}
\begin{document}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \writeBoxes
\end{document}

答案1

使用 LaTeX 的盒子定义:

\documentclass[a5paper]{book}
\usepackage{forloop}
\newcounter{BoxCount}
\setcounter{BoxCount}{0}
\newcounter{I}
\newcommand\numberedBox[1]{%
  \stepcounter{BoxCount}%
  \expandafter\newsavebox\expandafter{\csname BOX\alph{BoxCount}\endcsname}
  \expandafter\sbox\expandafter{\csname BOX\alph{BoxCount}\endcsname}{\vbox{\noindent\theBoxCount\\~ #1}}%
}%
\newcommand{\writeBoxes}{
  \stepcounter{BoxCount}%
  \setcounter{I}{1}%
  \forloop{I}{1}{\value{I} < \value{BoxCount}}{%
    \usebox{\csname BOX\alph{I}\endcsname}\par
  }%
}
\begin{document}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \writeBoxes
\end{document}

在此处输入图片描述

答案2

您不应该使用除 0 到 9 之外的其他编号的盒式寄存器。

尤其是框寄存器 10 对应于\voidb@xLaTeX 内核认为始终为空的框。如果你用某些东西填充它,许多宏将无法正常工作。其中包括

\leavevmode
\settowidth \settoheight \settowidth

为什么会出现错误?因为\usebox执行\leavevmode反过来又执行\unhbox\voidb@x;您已将\box10(即\voidb@x)设置为\vbox,因此 执行的命令\leavevmode会尝试\unhbox\vbox这是不允许的。

这是一个更安全(但并非万无一失)的方法。如果只是为了实验,那么风险就没那么大;另一方面,我永远不会用它来制作真正的文件。在这种情况下,我会分配我的盒子寄存器。

\documentclass[a5paper]{book}
\usepackage{forloop,etex}
\newcounter{BoxCount}
\setcounter{BoxCount}{\count14}
\ifnum\value{BoxCount}<256 
  \setcounter{BoxCount}{355}
\else
  \addtocounter{BoxCount}{100}
\fi
\mathchardef\FirstBoxCount=\value{BoxCount}
\newcommand{\numberedBox}[1]{%
  \stepcounter{BoxCount}%
  \savebox{\theBoxCount}{\vbox{\noindent\theBoxCount\\~ #1}}%
}
\newcounter{I}
\newcommand{\writeBoxes}{%
  \stepcounter{BoxCount}%
  \setcounter{I}{\FirstBoxCount}%
  \forloop{I}{1}{\value{I} < \value{BoxCount}}{%
    \usebox{\theI}\par
  }%
}
\begin{document}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \writeBoxes
\end{document}

如果扩展寄存器集已被使用,则使用扩展寄存器集并从最后分配的寄存器开始前进 100 个寄存器,这应该可以工作而无需触及任何分配的盒子寄存器。


LaTeX 内核分配的盒子比寄存器 10 多:

\voidb@x=\box10
\strutbox=\box11
\@tempboxa=\box12
\rootbox=\box13
\@labels=\box14
\@curline=\box15
\@curfield=\box16
\@tabfbox=\box17
\@arstrutbox=\box18
\@picbox=\box19
\@linechar=\box20
\@dashbox=\box21
\@begindvibox=\box22
\@outputbox=\box23
\@leftcolumn=\box24
\@holdpg=\box25

使用这些寄存器并遵循可能由软件包分配的寄存器几乎肯定会导致错误。如果你这样做,那你就是在搬起石头砸自己的脚。


这是一个完全安全的版本。

\documentclass[a5paper]{book}
\usepackage{forloop,etex}
\newcounter{BoxCount}

\makeatletter
\newcommand{\myBox}[1]{\@nameuse{myBox@\number\value{#1}}}
\newcommand{\checkBox}[1]{%
  \@ifundefined{myBox@\number\value{#1}}
    {\expandafter\newbox\csname myBox@\number\value{#1}\endcsname}
    {}%
}
\makeatother

\newcommand{\numberedBox}[1]{%
  \stepcounter{BoxCount}%
  \checkBox{BoxCount}%
  \savebox{\myBox{BoxCount}}{\vbox{\noindent\theBoxCount\\~ #1}}%
}
\newcounter{I}
\newcommand{\writeBoxes}{%
  \stepcounter{BoxCount}%
  \setcounter{I}{1}%
  \forloop{I}{1}{\value{I} < \value{BoxCount}}{%
    \usebox{\myBox{I}}\par
  }%
}
\begin{document}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \numberedBox{word}
  \writeBoxes
\end{document}

当您设置与计数器值关联的框时,首先检查其分配:

\checkBox{BoxCount}

如果你还没有这样做,它将分配一个新的盒子寄存器;如果盒子寄存器已经分配,​​则什么也不会发生。然后你使用

\myBox{<counter>}

当您需要保存或使用与计数器的值相对应的盒子寄存器时。

相关内容