lua 过滤器迭代 table 行时出现问题
Problems with lua filter to iterate over table rows
我正在尝试为 pandoc 编写一个简单的 lua 过滤器,以便对 ReST Table.
中的元素进行一些宏扩展
filter.lua
function tablelength(T)
local count = 0
for _ in pairs(T) do count = count + 1 end
return count
end
function Table(table)
elems=pandoc.Table(table)["rows"]
print(tablelength(table))
for v in pairs(elems) do
print(v) -- Prints nothings
end
return table
end
test.rst
======= =========
A B
======= =========
{{x}} {{y}}
======= =========
现在,如果我 运行 pandoc.exe -s --lua-filter filter.lua test.rst -t rst
程序说 elems 中有 5 个元素,但是 for 循环被跳过了,我真的不知道我做错了什么这里。
我是 Lua 的新手,对 pandoc 也知之甚少。如何遍历 elems 中的元素?
Pandoc lua-filters 提供方便的 walk_block 帮助程序,它递归地遍历文档树并将函数应用于与键匹配的元素。
在下面的示例中,我们给 walk_block
一个 lua table(其他语言中的映射或字典)只有一个键(键 Str
), table 的值是要应用的函数。该函数检查大括号,剥离它们并添加 foo
.
function Table(table)
return pandoc.walk_block(table, {
Str = function(el)
if el.text:sub(1,2) == '{{' then
txt = 'foo' .. el.text:sub(3, -3)
else
txt = el.text
end
return pandoc.Str(txt)
end
})
end
您的代码中有几个地方存在误解。首先,您需要记住 lua 中的所有内容都是 table(作为关联数组或字典实现),而数组只是 table 的特例,其中键是整数。为避免混淆,对于此答案的其余部分,当我指的是 pandoc 文档元素时,我将使用 Table,而当我指的是 lua 数据结构时,我将使用 table。
你的 tablelength 函数只是计算 pandoc table 中代表 Table 的元素数量。如果您查看 https://www.pandoc.org/lua-filters.html#type-ref-Block,您会看到 Table 有 5 个属性——标题、对齐、宽度、headers 和行。因此,此函数的 return 值为 5。如果您在 tablelength 内打印出循环中的值,那么您将确认这一点。如果要计算行数,则需要将行数组传递给函数,而不是 table.
第二个问题是您正在创建一个新的 table 而不是使用 pandoc 传入的那个。而不是使用 elems=pandoc.Table(table)["rows"]
只需使用 elems=table["rows"]
或 elems=table.rows
是等效的。函数pandoc.Table()
用于创建一个新元素。
此外,要遍历数组形式的 table 中的元素,您可以使用 ipairs 函数 - 它会 return 此处描述的数字索引值 What is the difference of pairs() vs. ipairs() in Lua?.
行 table 正如所料,是一个行数组,其中每一行又是一个元素数组。因此,要访问 table 的元素,您需要有两个循环。
最后是 pandoc object 模型的问题。因为 table 可以包含其他内容(图像、链接、粗体文本等),所以最终的单元格值实际上是一个块列表。现在根据你想用 table 做什么,你可以用不同的方式处理这个问题。您可以使用 mb21 引用的 walk_block 函数,但只循环访问单个单元格中的块。如果您的 Table 只包含(未格式化的)文本,那么您可以使用 stringify 函数来简化事情,它将块列表折叠成一个字符串。
将所有这些放在一起可以得到您的代码的以下修改版本。
local stringify=pandoc.utils.stringify
-- This function is no longer needed
function tablelength(T)
local count = 0
for e in pairs(T) do
count = count + 1
print(e) -- this shows the key not the value
end
return count
end
function Table(table)
rows=table["rows"]
print("TableLength="..#rows)
for rownum,row in ipairs(rows) do
for colnum, elem in ipairs(row) do
print(stringify(elem)) -- Prints cell text
end
end
return table
end
关于你的后续问题,如果你想修改东西,那么你只需要替换单元格值,同时尊重 pandoc 的 object 模型。你可以用pandoc模块中的构造函数构造pandoc使用的各种类型(比如前面提到的pandoc.Table)。最简单的 table 单元格将是一个包含单个 Plain 块的数组,该块又包含一个 Str 元素(块通常包含一个内联元素列表)。
以下代码显示了如何使用现有内容或 row/column 号码修改 table。请注意,我将 Table 函数的参数从 table
更改为 tableElem
,因为 table
是 lua 中使用的常见类型并且覆盖它会难以跟踪错误。
local stringify=pandoc.utils.stringify
function makeCell(s)
return {pandoc.Plain({pandoc.Str(s)})}
end
function Table(tableElem)
rows=tableElem["rows"]
for rownum,row in ipairs(rows) do
for colnum, elem in ipairs(row) do
local elemText=stringify(elem)
if elemText=="{{x}}" then
row[colnum]=makeCell(elemText:gsub("x","newVal"))
end
if rownum==1 and colnum==2 then
row[colnum]=makeCell("Single cell")
end
end
end
local newRow={ makeCell("New A"), makeCell("New B")}
table.insert(rows,newRow)
return tableElem
end
我正在尝试为 pandoc 编写一个简单的 lua 过滤器,以便对 ReST Table.
中的元素进行一些宏扩展filter.lua
function tablelength(T)
local count = 0
for _ in pairs(T) do count = count + 1 end
return count
end
function Table(table)
elems=pandoc.Table(table)["rows"]
print(tablelength(table))
for v in pairs(elems) do
print(v) -- Prints nothings
end
return table
end
test.rst
======= =========
A B
======= =========
{{x}} {{y}}
======= =========
现在,如果我 运行 pandoc.exe -s --lua-filter filter.lua test.rst -t rst
程序说 elems 中有 5 个元素,但是 for 循环被跳过了,我真的不知道我做错了什么这里。
我是 Lua 的新手,对 pandoc 也知之甚少。如何遍历 elems 中的元素?
Pandoc lua-filters 提供方便的 walk_block 帮助程序,它递归地遍历文档树并将函数应用于与键匹配的元素。
在下面的示例中,我们给 walk_block
一个 lua table(其他语言中的映射或字典)只有一个键(键 Str
), table 的值是要应用的函数。该函数检查大括号,剥离它们并添加 foo
.
function Table(table)
return pandoc.walk_block(table, {
Str = function(el)
if el.text:sub(1,2) == '{{' then
txt = 'foo' .. el.text:sub(3, -3)
else
txt = el.text
end
return pandoc.Str(txt)
end
})
end
您的代码中有几个地方存在误解。首先,您需要记住 lua 中的所有内容都是 table(作为关联数组或字典实现),而数组只是 table 的特例,其中键是整数。为避免混淆,对于此答案的其余部分,当我指的是 pandoc 文档元素时,我将使用 Table,而当我指的是 lua 数据结构时,我将使用 table。
你的 tablelength 函数只是计算 pandoc table 中代表 Table 的元素数量。如果您查看 https://www.pandoc.org/lua-filters.html#type-ref-Block,您会看到 Table 有 5 个属性——标题、对齐、宽度、headers 和行。因此,此函数的 return 值为 5。如果您在 tablelength 内打印出循环中的值,那么您将确认这一点。如果要计算行数,则需要将行数组传递给函数,而不是 table.
第二个问题是您正在创建一个新的 table 而不是使用 pandoc 传入的那个。而不是使用 elems=pandoc.Table(table)["rows"]
只需使用 elems=table["rows"]
或 elems=table.rows
是等效的。函数pandoc.Table()
用于创建一个新元素。
此外,要遍历数组形式的 table 中的元素,您可以使用 ipairs 函数 - 它会 return 此处描述的数字索引值 What is the difference of pairs() vs. ipairs() in Lua?.
行 table 正如所料,是一个行数组,其中每一行又是一个元素数组。因此,要访问 table 的元素,您需要有两个循环。
最后是 pandoc object 模型的问题。因为 table 可以包含其他内容(图像、链接、粗体文本等),所以最终的单元格值实际上是一个块列表。现在根据你想用 table 做什么,你可以用不同的方式处理这个问题。您可以使用 mb21 引用的 walk_block 函数,但只循环访问单个单元格中的块。如果您的 Table 只包含(未格式化的)文本,那么您可以使用 stringify 函数来简化事情,它将块列表折叠成一个字符串。
将所有这些放在一起可以得到您的代码的以下修改版本。
local stringify=pandoc.utils.stringify
-- This function is no longer needed
function tablelength(T)
local count = 0
for e in pairs(T) do
count = count + 1
print(e) -- this shows the key not the value
end
return count
end
function Table(table)
rows=table["rows"]
print("TableLength="..#rows)
for rownum,row in ipairs(rows) do
for colnum, elem in ipairs(row) do
print(stringify(elem)) -- Prints cell text
end
end
return table
end
关于你的后续问题,如果你想修改东西,那么你只需要替换单元格值,同时尊重 pandoc 的 object 模型。你可以用pandoc模块中的构造函数构造pandoc使用的各种类型(比如前面提到的pandoc.Table)。最简单的 table 单元格将是一个包含单个 Plain 块的数组,该块又包含一个 Str 元素(块通常包含一个内联元素列表)。
以下代码显示了如何使用现有内容或 row/column 号码修改 table。请注意,我将 Table 函数的参数从 table
更改为 tableElem
,因为 table
是 lua 中使用的常见类型并且覆盖它会难以跟踪错误。
local stringify=pandoc.utils.stringify
function makeCell(s)
return {pandoc.Plain({pandoc.Str(s)})}
end
function Table(tableElem)
rows=tableElem["rows"]
for rownum,row in ipairs(rows) do
for colnum, elem in ipairs(row) do
local elemText=stringify(elem)
if elemText=="{{x}}" then
row[colnum]=makeCell(elemText:gsub("x","newVal"))
end
if rownum==1 and colnum==2 then
row[colnum]=makeCell("Single cell")
end
end
end
local newRow={ makeCell("New A"), makeCell("New B")}
table.insert(rows,newRow)
return tableElem
end