Lua 中的自定义 foreach 实现

Custom foreach implementation in Lua

提问人:szulak 提问时间:9/21/2023 更新时间:9/22/2023 访问量:62

问:

我正在尝试实现一个自定义迭代器,它的工作方式类似于 Lua,但以更“语法友好”的方式。我遇到了以下构造(不确定它是纯粹在 Lua 中完成的,还是在 Lua 源代码中的某种 C“hack”中完成的。table.foreach

-- creature is a collection of Creature objects
-- "ps" is used as a filter variable

foreach creature cre "ps" do
    if cre.name ~= $name then
    -- code...
    end
end

我尝试使用 ,依此类推 - 但无法获得与上述代码示例类似的结果。你能给我一个提示,如何实现吗?table.foreachipairs

lua lua-table

评论

1赞 Luatic 9/21/2023
这是 Lua 中的语法错误;它不能在普通的 Lua 中实现。你可以在分词器中添加一个关键字,在语法器中添加一个产品,等等,只是为了获得一些语法糖,这将难倒每个支持Lua的工具。真的值得吗?为什么标准的 Lua for 循环不够好?我发现它更具可读性,而且由于它只是普通的 Lua,因此它可以很好地与您选择使用的任何工具配合使用。foreachfor cre in creature:filter"ps" do ... end
0赞 szulak 9/21/2023
@Oka 我会说简短 - 因此这个问题,如果使用 iterators/next 实现这个 foreach 关键字是可行的。

答:

1赞 Oka 9/22/2023 #1

关键字需要 preproccesor直接修改语言的语法。

通过编写迭代器,已经可以很容易地实现这种语法的近似,而无需离开语言的限制 - 一个函数,当提供给泛型时,它会持续提供循环变量的值,直到它返回的第一个值为

(更具体地说,您通常会编写一个函数来创建(返回)迭代器。pairsipairs 就是这方面的例子。)

Next 是一个函数,当提供和键时,返回下一个及其关联值。

您可以使用元表和元方法扩展它,以缩短构造迭代器所需的语法(Lua 中正确构造的类/对象/原型(即 OOP)已经严重依赖元表)。__call

所有这些都意味着语法

for value in collection 'filter' do
    print(value)
end

是非常可以实现的。

完整的示例,带有一个非常基本的迭代器:

-- This is a live, proxied view into an existing table
local function View(t)
    local methods = {}

    local function match(value, filter)
        return type(value) == 'string' and value:match(filter)
    end

    function methods:filter(filter)
        local key, value

        return function (_)
            repeat
                key, value = next(t, key)
            until value == nil or match(value, filter)

            return value, key
        end
    end

    return setmetatable({}, {
        __index = methods,
        __metatable = false,
        __call = methods.filter
    })
end

local foo = View { 'c', 'a', 'b', 'a', 'c', 'd' }

-- :filter via __call
for value in foo '[ab]' do
    print(value)
end

print('-------')

-- explicitly
for value in foo:filter '[cd]' do
    print(value)
end

print('-------')

-- additional index variable
-- this is the inverse of the more typical `pairs` construct
for value, key in foo '[ad]' do
    print(key)
end
a
b
a
-------
c
c
d
-------
2
4
6

评论

0赞 ESkri 9/22/2023
实现只读是一种快速而肮脏的方法吗?__newindex = false
0赞 Oka 9/22/2023
呃,有点 - 如果您尝试在该表:)中注册一个新索引,运行时将爆炸。 绕过它。示例的那部分并不重要,但我的意思可能是无法通过代理/视图 () 更改/引用。我将更新措辞,因为它不完全是只读的。rawset__metatable = falsemethodsgetametatable