这是与此相关的后续问题:单个字母后不能有空格
在纯文本上测试了建议的解决方案后,该finalizer
解决方案有效。然而,在包含分段命令(\title
,甚至其他命令)的文档中使用它后,ConTeXt 进程崩溃了。
梅威瑟:
\startluacode
function userdata.prevent_single_letter(head)
local cur = head
while cur do
if cur.id == node.id("glyph") then
if cur.prev.id == node.id("glue") and cur.next.id == node.id("glue") then
local p = node.new("penalty")
p.penalty = 10000
-- This is for debugging only, but then you have to
-- remove the last node.insert_after line:
--local w = node.new("whatsit","pdf_literal")
--w.data = "q 1 0 1 RG 1 0 1 rg 0 0 m 0 5 l 2 5 l 2 0 l b Q"
--node.insert_after(head,cur,w)
--node.insert_after(head,w,p)
node.insert_after(head,cur,p)
end
end
cur = cur.next
end
return head, true
end
\stopluacode
\startluacode
nodes.tasks.appendaction("processors", "before", "userdata.prevent_single_letter")
\stopluacode
\starttext
\title[title:poznamky]{Notes z Con\TeX{}t}
Filling text filling text filling text filling text filling text filling text filling text fil V text
\stoptext
更令人不安的是,除了问题是由 引起的之外,我无法获得任何有意义的错误消息\title
。我从控制台获得的唯一可读错误消息是:
hpack filter: error: [\directlua]:6: attempt to index a nil value (field 'prev')
我不知道是什么hpack_filter
。为了下次能够更好地调试我遇到的问题,我应该使用哪个 ConTeXt 参考?
答案1
问题来自于 ConTeXt在和 上processors
运行的事实。在 中不会出现问题,因为第一个节点始终是。然而,在 中,第一个节点很可能是 ,这是一个问题,因为它前面没有其他节点,即字段是。现在代码只是查询字段的属性,但由于字段根本不存在,因此会失败。我们可以通过利用 and 逻辑(与)运算符的属性来解决这个问题,该属性称为pre_linebreak_filter
hpack_filter
pre_linebreak_filter
local_par
hpack_filter
glyph
prev
nil
id
prev
prev
and
or
短路评估。
有关 ConTeXt 中这些回调的更多信息,请参阅手册MkIV 混合动力技术,特别是第 9 章“回调”中的表格(来源)。
关于 Lua 中的短路求值
Lua 中的常见做法是使用类似这样的语句来保护这种访问
if cur.prev and cur.prev.id == ... then
当左侧操作数的计算结果为 时false
,右侧操作数甚至没有评估复合表达式的结果为false
。这是因为根据布尔逻辑 ,false and <anything>
始终为false
,而不管 的值如何<anything>
。
在 Lua 中,这种习语的延伸甚至更远,因为该语言为所有值分配了“真值”。值nil
和false
始终被视为“假值”,而其他所有值都被视为“真值”(特别是整数值0
和1
都计算为“真值”,即使您可能希望0
它像 C 类语言一样为“假值”)。最重要的是,逻辑语句始终返回最后一个被评估的操作数。这允许构造如下
userdata = userdata or {}
您在 ConTeXt 核心 Lua 代码中经常看到这种代码。它的作用是评估userdata
,如果这是nil
(这被认为是“false-y”),它评估{}
(这被认为是“true-y”),并且从表达式返回空表然后将其分配给userdata
。这是一种快速编写受保护变量初始化的好方法,当变量已经初始化时,它不会执行任何操作。
平均能量损失
if
在本例中,我们实际上可以使用 的短路求值属性将两个嵌套语句融合在一起and
。这会使代码更加紧凑,并且您可能会获得无限的性能改进。对性能更相关的可能是缓存glyph
和glue
节点的 ID,因为这些 ID 永远不会改变。
\startluacode
local glyph_id = node.id("glyph")
local glue_id = node.id("glue")
function userdata.prevent_single_letter(head)
local cur = head
while cur do
if cur.id == glyph_id and
cur.prev and
cur.next and
cur.prev.id == glue_id and
cur.next.id == glue_id
then
local p = node.new("penalty")
p.penalty = 10000
node.insert_after(head,cur,p)
end
cur = cur.next
end
return head, true
end
\stopluacode
\startluacode
nodes.tasks.appendaction("processors", "before", "userdata.prevent_single_letter")
\stopluacode
\starttext
\title[title:poznamky]{Notes z Con\TeX{}t}
Filling text filling text filling text filling text filling text filling text filling text fil V text
\stoptext