在查看其他人的包时,我发现这个\discretionary
命令被使用得相当频繁。我想挂接到其中一个参数中使用的代码中,但发现我的代码执行得比我预期的要多得多。我猜想它的\discretionary
行为有点像\mathchoice
:每个参数都被处理成一个框,其中一个框被排版到文档中,其他的被丢弃。
是吗?
(我想做的是当使用某个特定参数时,计数器会移动。由于这是一个分配,所以每次\discretionary
使用时,计数器似乎都会移动。在类似情况下,我使用了一个技巧,使用文件aux
来查看实际处理了哪个框。我想知道我是否可以在这里避免这种情况。)
答案1
(对您最终想要完成的目标没有帮助,只是按照要求逐字回答问题。)
回想一下,这\discretionary
应该被用作
\discretionary{⟨pre-break text⟩}{⟨post-break text⟩}{⟨no-break text⟩}
其中 是⟨no-break text⟩
默认打印的内容,但如果需要换行,则当前行将以 结束⟨pre-break text⟩
,下一行将以 开始⟨post-break text⟩
。这在 TeXbook 第 95 页(第 14 章:TeX 如何将段落分成行)中有解释。
其内部工作原理是 TeX 会将“圆盘”节点插入到当前列表中。例如,如果你输入
在这里,您可以找到一个 st\discretionary{ra-}{ng}{rang}e 词。
那么 TeX 的内部列表将如下所示(点击图片以获得适当的分辨率):
(还要注意字距和“fi”连字符——上面的代码是使用 LuaTeX 的pre_linebreak_filter
as这里)。更准确地说,disc
节点指向的当然是列表本身:
一般而言,这三个列表将仅包含char_node
、、、、和项目。kern_node
hlist_node
vlist_node
rule_node
ligature_node
当 TeX 执行\discretionary
命令时,会构建这些列表;稍后在断行期间,TeX 将首先在其第一次(link
无连字符)传递中使用节点的 (无断行文本)disc
,然后(如果在 中未找到任何令人满意的内容)在第二次断行传递中\pretolerance
使用节点的pre
和链接。post
disc
进一步深入到更低层次的细节:
当 TeX 遇到 时\discretionary
,如果它处于垂直模式,那么(就像遇到字母 或\hskip
等时一样)它首先后退并开始一个新段落(从而切换到水平模式)。(参见 TeXbook 第 283 页“第 24 章:垂直模式总结”,或该计划的第 1090 条)因此我们可以假设当 TeX 遇到 时,它处于水平模式或数学模式\discretionary
。
因此,当它遇到时\discretionary
,它会执行以下操作:
- 将新
disc
节点附加到当前列表。 - 推送到保存堆栈,并开始新的保存级别,跟踪当前级别是否为可自由选择的类型。(而不是 hbox、align 等——有 16 种组;该计划的第 269 条。
- 扫描左边的括号。
- 进入受限水平模式(空间系数为 1000,默认值)。
- 然后,直到遇到右括号,继续照常处理(就像在受限水平模式下一样)。
- 当遇到右括号时,
- 修剪目前已建立的列表,使其仅包含 char、kern、hlist、vlist、rule 和 ligature 节点。(否则说“不适当的自由列表”。)
- 如果正在扫描休息前的文本,则让
disc
节点的pre_break
字段指向它,然后再次开始扫描(从上面的步骤 3 开始)。 - 如果正在扫描休息后的文本,则使
disc
节点的post_break
字段指向它,然后再次开始扫描(从上面的步骤 3 开始)。 - 如果正在扫描不间断文本,则使
disc
节点的link
字段指向它。(此处对于数学模式,该字段应为空。)
这一切都在该计划的第 1114 至 1121 条,或者 The TeXbook 的第 287 页(第 25 章:水平模式摘要):
\discretionary⟨general text⟩⟨general text⟩⟨general text⟩
这三个通用文本以受限水平模式处理。它们应该只包含固定宽度的内容;因此在这种情况下它们实际上并不是很通用。更准确地说,每个自由选择通用文本形成的水平列表必须仅由字符、连字、字距、方框和规则组成;不应有粘连或惩罚项等。此命令将自由选择项附加到当前列表;有关自由选择项的含义,请参阅第 14 章。空间因子不变。
答案2
如果您使用 luatex,您可以在回调中遍历节点post_linebreak_filter
,但看看经典的 TeX......
你提到\mathchoice
,但情况更\mathchoice
简单,语法\mathchoice
是
\mathchoice{a}{b}{c}{d}
并且 a、b、c、d 中的每一个都设置在一个框中,但只有其中一个框被复制到最终页面,因此通过在每个框中放置非立即写入,您可以从输出中看到使用了哪个框。
\mathchoice{\write20{A}a}{\write20{B}b}{\write20{C}c}{\write20{D}d}
只会写出 ABCD 中的一个来显示使用了哪个盒子。
情况discretionary
表面上类似
\discretionary{pre-break text}{post-break text}{no-break text}
但参数通常不被框起来。能使用一个盒子,但通常它们只是字符,并且它们仅限于字符,盒子和字距,你不能有写节点。
因此您可以立即打印而不生成节点:
aaa \discretionary{\message{1}a}{\message{2}b}{\message{3}c} xxx
aaa \discretionary{\immediate\write20{a}a}{\immediate\write20{b}a}{\immediate\write20{c}a} xxx
但是这两种形式都不会给你任何信息,因为所有三个参数都会被评估,所以你分别得到1 2 3
和a b c
。
你可以尝试非立即行动\write
,但你会得到
! Improper discretionary list.
l.9 aaa \discretionary{\write20{a}a}
所以你可以把写入节点放在一个盒子里
aaa \discretionary{\hbox{\write20{A}a}}{\hbox{\write20{B}b}}{\hbox{\write20{C}c}} xxx
没有错误,只是输出C
没有换行的情况,但这意味着水平列表\hbox{c}
不包含c
任何会干扰连字或字母间字距的字符。