Pandoc Lua Filters: `pairs` 不能显示表中所有键的问题

自 Pandoc 2.0 版本以来,就有了编写 Lua Filter 的能力。然而,在 Pandoc 2.0 中,我发现使用 Lua 的 pairs 函数遍历元素表时不能显示表中的所有键。

以下是一个简单的实例,用来说明这个问题。在 filter.lua 文件中,我编写了如下代码:

function Para(elem)
  io.stderr:write("A: " .. type(elem) .. "\n")
  for k, v in pairs(elem) do
    io.stderr:write("B: " .. k .. "\n")
  end
  io.stderr:write("C: " .. elem["t"] .. "\n")
  io.stderr:write("D: " .. tostring(elem["c"]) .. "\n")
  -- 返回未修改的元素
  return nil
end

然后,从命令行中运行以下命令:

echo "Hello." | pandoc -f markdown -t native --lua-filter filter.lua

这将产生以下输出:

A: table
B: c
C: Para
D: table: 0x53adb40
[Para [Str "Hello."]]

我可以将 -t native 改成 -t json,这样最后一行将变成:

{"blocks":[{"t":"Para","c":[{"t":"Str","c":"Hello."}]}],"pandoc-api-version":[1,17,2],"meta":{}}

因此,从 (B) 的输出看来,celem 中的唯一键,但从 (C) 的输出中,可以清楚地看到 t 也是一个键,因为可以访问它来获取 Para。究竟发生了什么,为什么使用 pairs 函数进行遍历时 t 键会被隐藏起来呢?

点赞
用户2425163
用户2425163

t 值被隐藏在元素的元表中:Pandoc 不是为每个元素分配一个 t 值,而是为每个元素设置一个元表。这样做的原因是在可用性和性能之间要找到平衡。

将数值表索引传递回 Haskell 比访问基于字符串索引的值要快得多。然而,用户应该能够以直观的面向对象的方式使用元素,通过可读的字符串属性访问元素组件。这就是为什么我们为每个元素分配元表的原因。元表包含有关元素类型的信息(例如,Plain vs Para 等)并定义访问器(例如,content 是在 Plain 和 Para 元素中索引为 0 的别名)。

因此,可以通过调用 elem.t 来获取元素 elem 的类型,但元素本身没有该键,元表有。这就是为什么在使用 pairs 迭代元素时,t 不会出现的原因。可以使用 getmetatable 函数接收元表。

您可能会欣赏以下方法来获取访问器的名称(未经记录并可能更改)。

for k, _ in pairs(getmetatable(elem).getters) do
    print k
end

或者,在 Lua 过滤器文档的 "Module pandoc" 部分中列出了每个元素类型的访问器。

2017-10-31 11:16:08