关于 Lua 中的短路求值

关于 Lua 中的短路求值

这是与此相关的后续问题:单个字母后不能有空格

在纯文本上测试了建议的解决方案后,该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_filterhpack_filterpre_linebreak_filterlocal_parhpack_filterglyphprevnilidprevprevandor短路评估

有关 ConTeXt 中这些回调的更多信息,请参阅手册MkIV 混合动力技术,特别是第 9 章“回调”中的表格(来源)。

关于 Lua 中的短路求值

Lua 中的常见做法是使用类似这样的语句来保护这种访问

if cur.prev and cur.prev.id == ... then

当左侧操作数的计算结果为 时false,右侧操作数甚至没有评估复合表达式的结果为false。这是因为根据布尔逻辑 ,false and <anything>始终为false,而不管 的值如何<anything>

在 Lua 中,这种习语的延伸甚至更远,因为该语言为所有值分配了“真值”。值nilfalse始终被视为“假值”,而其他所有值都被视为“真值”(特别是整数值01都计算为“真值”,即使您可能希望0它像 C 类语言一样为“假值”)。最重要的是,逻辑语句始终返回最后一个被评估的操作数。这允许构造如下

userdata = userdata or {}

您在 ConTeXt 核心 Lua 代码中经常看到这种代码。它的作用是评估userdata,如果这是nil(这被认为是“false-y”),它评估{}(这被认为是“true-y”),并且从表达式返回空表然后将其分配给userdata。这是一种快速编写受保护变量初始化的好方法,当变量已经初始化时,它不会执行任何操作。

平均能量损失

if在本例中,我们实际上可以使用 的短路求值属性将两个嵌套语句融合在一起and。这会使代码更加紧凑,并且您可能会获得无限的性能改进。对性能更相关的可能是缓存glyphglue节点的 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

相关内容