【问题标题】:Changing the return order of captures in an LPeg pattern?以 LPeg 模式更改捕获的返回顺序?
【发布时间】:2015-12-29 05:33:31
【问题描述】:

(我使用的是 Lua 5.2 和 LPeg 0.12)

假设我有一个模式P 会产生一些不确定数量的捕获(如果有的话),我想编写一个模式Q 来捕获P 以及P 之后的位置--但是以便在P 的捕获之前返回该位置。本质上,如果lpeg.match(P * lpeg.Cp(), str, i) 产生v1, v2, ..., j,那么我希望lpeg.match(Q, str, i) 产生j, v1, v2, ...

这是否可以在每次匹配P 时不必创建新表来实现?

主要是为了简化一些产生迭代器的函数。 Lua的stateless iterator functions只获取一个控制变量,需要是迭代器函数返回的第一个值。

在一个允许人们命名可变参数函数的 last 参数的世界中,我可以这样写:

function pos_then_captures(pattern)
    local function roll(..., pos)
        return pos, (...)
    end
    return (pattern * lpeg.Cp()) / roll
end

唉。简单的解决方案是明智地使用lpeg.Ct()

function pos_then_captures(pattern)
    -- exchange the order of two values and unpack the first parameter
    local function exch(a, b)
        return b, unpack(a)
    end
    return (lpeg.Ct(pattern) * lpeg.Cp()) / exch
end

或者让lpeg.match 的调用者跳起打包/删除/插入/解包的舞蹈。尽管后者听起来很恶心,但我可能会这样做,因为lpeg.Ct() 可能会对pos_then_captures 的病态但“正确”的论点产生一些意想不到的后果。

每次成功匹配pattern 时,其中任何一个都会创建一个新表,诚然这在我的应用程序中并不重要,但是有没有办法在没有任何打包解包魔法的情况下做到这一点?

我不太熟悉 Lua 的内部结构,但感觉我真正想做的就是从 Lua 的堆栈中弹出一些东西并将其放回其他地方,这看起来不像是一个操作直接或有效地支持,但也许 LPeg 在这种特定情况下可以做一些事情。

【问题讨论】:

  • 您可以在 vararg 函数中使用 select 来获取从 vararg 参数中的任意点开始的元素 select(-1, ...) 是最后一个元素。所以return select(-1, ...), ... 应该将最后一个元素添加到返回的列表中,尽管它不会从列表的末尾删除该元素,但是如果您的循环在到达其计数器时停止,这对这种用法无关紧要。
  • select 非常有趣。不敢相信我在手册中错过了!谢谢:)

标签: lua lpeg


【解决方案1】:

比赛时间捕获和升值可以完成工作。此函数使用Cmt 确保在将pos 粘贴到patternpattern / prepend 捕获之前设置pos

Cmt = lpeg.Cmt
Cp  = lpeg.Cp

function prepend_final_pos(pattern)
    -- Upvalues are dynamic, so this variable belongs to a
    -- new environment for each call to prepend_final_pos.
    local pos

    -- lpeg.Cmt(patt, func) passes the entire text being
    -- searched to `function` as the first parameter, then
    -- any captures. Ignore the first parameter.
    local function setpos(_, x)
      pos = x

      -- If we return nothing, Cmt will fail every time
      return true
    end

    -- Keep the varargs safe!
    local function prepend(...)
      return pos, ...
    end

    -- The `/ 0` in `Cmt(etc etc) / 0` is to get rid of that
    -- captured `true` that we picked up from setpos.
    return (pattern / prepend) * (Cmt(Cp(), setpos) / 0)
end

示例会话:

> bar = lpeg.C "bar"
> Pbar = prepend_final_pos(bar)
> print(lpeg.match(Pbar, "foobarzok", 4))
7       bar
> foo = lpeg.C "foo" / "zokzokzok"
> Pfoobar = prepend_final_pos(foo * bar)
> print(lpeg.match(Pfoobar, "foobarzok"))
7       zokzokzok       bar

正如预期的那样,实际捕获对新模式返回的位置没有影响;只有与原始模式匹配的文本长度。

【讨论】:

    【解决方案2】:

    您可以使用原始解决方案来完成此操作,而无需像这样的表格捕获或匹配时间捕获

    function pos_then_captures(pattern)
        local function exch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, ...)
            if a1 == nil then return end
            if a2 == nil then return a1 end
            if a3 == nil then return a2, a1 end
            if a4 == nil then return a3, a1, a2 end
            if a5 == nil then return a4, a1, a2, a3 end
            if a6 == nil then return a5, a1, a2, a3, a4 end
            if a7 == nil then return a6, a1, a2, a3, a4, a5 end
            if a8 == nil then return a7, a1, a2, a3, a4, a5, a6 end
            if a9 == nil then return a8, a1, a2, a3, a4, a5, a6, a7 end
            if a10 == nil then return a9, a1, a2, a3, a4, a5, a6, a7, a8 end
            local t = { a10, ... }
            return t[#t], a1, a2, a3, a4, a5, a6, a7, a8, a9, unpack(t, 1, #t-1)
        end
        return (pattern * lpeg.Cp()) / exch
    end
    

    以下示例使用返回每个匹配的 'a',并在其前面加上匹配的结尾

    local p = lpeg.P{ (pos_then_captures(lpeg.C'a') + 1) * lpeg.V(1) + -1 }
    print(p:match('abababcd'))
    
    -- output: 2       a       4       a       6       a
    

    【讨论】:

    • 这只是将表的创建推迟到local a = { ... } 行,这正是我想要避免的。
    • 看看select函数源码。它可以从一开始就修剪...,即return select(2, ...) 修剪第一个参数并从第二个参数开始返回所有内容,但它不能修剪最终参数(我不知道)。所以我只是展开 exch 最多 10 个参数,并使用表格和使用 unpack 及其可选的第二个和第三个参数来实现其余案例。
    猜你喜欢
    • 2018-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-06
    • 2014-07-05
    • 2014-11-26
    相关资源
    最近更新 更多