【问题标题】:Problems with lua filter to iterate over table rowslua 过滤器迭代表行的问题
【发布时间】:2019-07-08 05:37:12
【问题描述】:

我正在尝试为 pandoc 编写一个简单的 lua 过滤器,以便对 ReST 表中的元素进行一些宏扩展。

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 中的元素?

【问题讨论】:

  • print(type(elems)) 打印什么?
  • @EgorSkriptunoff:它打印table

标签: lua python-sphinx pandoc restructuredtext


【解决方案1】:

Pandoc lua-filters 提供了方便的 walk_block 帮助器,它递归地遍历文档树并将函数应用于匹配键的元素。

在下面的例子中,我们给walk_block一个只有一个键(键Str)的lua表(什么是map或dict),表的值就是要应用的函数。该函数检查大括号,去掉它们并添加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

【讨论】:

  • 非常感谢您提供的简洁解决方案!是否也可以仅将元素添加到表格的第一行?
  • 我快速浏览了一下,但没有找到一种简单的方法将其添加到我的代码中......也许可以在 pandoc-discuss 上询问......?
  • 我也非常努力地修改了您的代码,或者将其应用于 table.rows[0],但这不起作用。我会再次阅读 pandoc 文档或其他论坛。非常感谢您的巧妙解决方案。
【解决方案2】:

您的代码中有几个方面存在误解。首先需要记住,lua 中的所有内容都是一个表(实现为关联数组或字典),而数组只是键为整数的表的一种特殊情况。为避免混淆,对于这个答案的其余部分,当我指代 pandoc 文档元素时,我将使用 Table,当我指代 lua 数据结构时,我将使用 table。

您的 tablelength 函数只是计算 pandoc 表中表示表的元素数。如果你查看https://www.pandoc.org/lua-filters.html#type-ref-Block,你会看到一个表格有 5 个属性——标题、对齐、宽度、标题和行。因此,该函数的返回值为 5。如果您在 tablelength 内的循环中打印出值,那么您将确认这一点。如果要计算行数,则需要将行数组传递给函数,而不是表。

第二个问题是您正在创建一个新表,而不是使用 pandoc 传入的表。而不是使用elems=pandoc.Table(table)["rows"],只需使用elems=table["rows"]elems=table.rows,这是等效的。函数pandoc.Table() 用于创建新元素。

此外,要遍历表中数组形式的元素,您可以使用 ipairs 函数 - 它将返回数字索引值,如此处所述What is the difference of pairs() vs. ipairs() in Lua?

正如预期的那样,rows 表是一个行数组,其中每一行又是一个元素数组。因此,要访问表格的元素,您需要有两个循环。

最后是 pandoc 对象模型的问题。因为表格可以包含其他内容(图像、链接、粗体文本等),所以最终的单元格值实际上是块列表。现在,根据您要对表格执行的操作,您可以采用不同的方式处理此问题。您可以使用 mb21 引用的 walk_block 函数,但只循环单个单元格中的块。如果您的表格只包含(未格式化的)文本,那么您可以使用 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 的对象模型。您可以使用模块 pandoc 中的构造函数来构造 pandoc 使用的各种类型(例如前面提到的 pandoc.Table)。最简单的表格单元格将是一个带有单个 Plain 块的数组,该块又包含一个 Str 元素(块通常包含一个 Inline 元素列表)。

以下代码显示了如何使用现有内容或行/列号修改表格。请注意,我将 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

【讨论】:

猜你喜欢
  • 2015-07-27
  • 1970-01-01
  • 2012-09-15
  • 1970-01-01
  • 1970-01-01
  • 2020-12-23
  • 2012-07-18
  • 2013-09-02
相关资源
最近更新 更多