意外的换行符(catcode 魔法)

意外的换行符(catcode 魔法)

我正在玩 Tex catcodes 和 hboxes。以下代码应产生三行输出:

A...1...
B...2...
C...3...

A然而,由于某种原因,它在和之间出现了换行符...1...,但我不知道为什么。

以下是代码:

\def\changeCatcodes{
    \catcode`A=13
    \catcode`B=13
    \catcode`C=13
}

\begingroup
\changeCatcodes
\gdefA{\argtohbox{\string A}}
\gdefB{\argtohbox{\string B}}
\gdefC{\argtohbox{\string C}}
\endgroup

\gdef\argtohbox#1{\hbox to 20pt{#1}}

\gdef\newstring{\penalty-10000}

\catcode`\^^M=13 % one for reading macro definition
\def\specialenv{%
    \begingroup%
    \parindent0pt%
    \catcode`\^^M=13 % ... and one for actually changing it when it is executed
    \changeCatcodes%
    \let^^M\newstring%
}
\catcode`\^^M=5 % return catcode back

\def\endspecialenv{\endgroup}

\specialenv
A...1...
B...2...
C...3...
\endspecialenv
\end

答案1

更简单的版本是

在此处输入图片描述


\hbox{A}1
\hbox{B}2
\hbox{C}3

\bye

\leavevmode因为在之前没有,所以\hbox第一个 hbox 保持在垂直模式,但随后该段落开始(在您的情况下是...),因此在 hmode 中找到以下 hbox。

答案2

当 TeX 处于垂直模式时,它会在它们之间堆叠盒子(可能添加行间粘连)并添加随之而来的其他垂直材料。

如果您\hbox{...}在 TeX 处于垂直模式时进行贡献,它只会遵循其性质并将其与已经贡献的材料一起堆叠。

某些 token 会导致垂直模式被搁置,水平模式开始。水平模式的目的是准备一个或多个框,以便在返回垂直模式时进行贡献(这由以下情况触发:\par,可能会隐式添加)。这些 token 中有不是 \hbox,如上所述。但是,如果\hbox在水平模式下发现,则在此模式下会贡献。

一个字符(在您的情况下是第一个句点)导致水平模式启动。它将在之后结束,\endspecialenv或者更准确地说,通过\end隐式提供的结束\par

您将获得相同的输出

\parindent=0pt

\hbox to 20pt{A}...1...\break
\hbox to 20pt{B}...2...\break
\hbox to 20pt{C}...3...\break
\end

在此处输入图片描述

以及几条Underfull \hbox消息:

Underfull \hbox (badness 10000) detected at line 3
\tenrm A

Underfull \hbox (badness 10000) detected at line 4
\tenrm B

Underfull \hbox (badness 10000) detected at line 5
\tenrm C

Underfull \hbox (badness 10000) in paragraph at lines 3--6
[]\tenrm ...1...

Underfull \hbox (badness 10000) in paragraph at lines 3--6
[]\tenrm ...2...

Underfull \hbox (badness 10000) in paragraph at lines 3--6
[]\tenrm ...3...

Underfull \hbox (badness 10000) in paragraph at lines 3--6

固定代码:

\def\changeCatcodes{
    \catcode`A=13
    \catcode`B=13
    \catcode`C=13
}

\begingroup
\changeCatcodes
\gdef A{\argtohbox{\string A}}
\gdef B{\argtohbox{\string B}}
\gdef C{\argtohbox{\string C}}
\endgroup

\def\argtohbox#1{\hbox to 20pt{#1\hss}}

\def\newstring{\hfil\penalty-10000 }

\catcode`\^^M=13 % one for reading macro definition
\def\specialenv{%
    \begingroup%
    \parindent0pt%
    \catcode`\^^M=13 % ... and one for actually changing it when it is executed
    \changeCatcodes%
    \let^^M\newstring%
    \leavevmode%
}
\catcode`\^^M=5 % return catcode back

\def\endspecialenv{\unpenalty\endgroup}

\specialenv
A...1...
B...2...
C...3...
\endspecialenv
\end
  1. \hss提供可拉伸的胶水,避免盒子不够满
  2. \hfil避免\penalty-10000线路未满
  3. \leavevmode启动水平模式
  4. \unpenalty删除最后一行末尾的惩罚
  5. 注意后面的空格-10000可以避免以下标记可能不合时宜地扩展

您可能会喜欢研究这种定义活跃角色的不同方法:

\def\activedef#1{%
  \begingroup\lccode`~=`#1\lowercase{\endgroup\def~}%
}
\activedef A{\argtohbox{A}}
\activedef B{\argtohbox{B}}
\activedef C{\argtohbox{C}}

这些定义没有必要,\global也没有必要开个小组来发布\changeCatcodes。也没有\string

相关内容