Lua the __ipairs function implemented does not end the loop

270 views Asked by At

I made an empty table, and using ipairs to access it will call other functions, but I don't know how to end the loop, when the loop proceeds to the last element, it still continues

local arr = {}
arr = setmetatable(arr, {
    __len = function(tbl)
        return 5
    end,
    __index = function(tbl, index)
        return 1
    end,
    __ipairs = function(tbl)
        local function iter(array, index)
            index = index + 1
            local value = nil
            if index <= #array then
                value = 1
            end
            if nil ~= value then return index, value end
        end

        return iter, tbl, 0
    end
})
for _, v in ipairs(arr) do
    print(_, v)
end

enter image description here

2

There are 2 answers

0
Luke100000 On BEST ANSWER

In Lua 5.3, the __ipairs metamethod has been deprecated, see https://www.lua.org/manual/5.3/manual.html#8.2

In Lua 5.4 it has been removed. That means, it will be ignored, and is only relying on __index, which returns a constant 1 and thus remains stuck.

A solution would be to use pairs:

local arr = {}
arr = setmetatable(arr, {
    __len = function(tbl)
        return 5
    end,
    __index = function(tbl, index)
        return 1
    end,
    __pairs = function(tbl)
        local function iter(array, index)
            index = index + 1
            local value = nil
            if index <= #array then
                value = 1
            end
            if nil ~= value then return index, value end
        end

        return iter, tbl, 0
    end
})
for _, v in pairs(arr) do
    print(_, v)
end
0
Luatic On

I think the cleaner solution is to fix __index rather than switching to pairs; this allows you to trivially implement __pairs in terms of ipairs:

arr = setmetatable(arr, {
    __len = function() return 5 end,
    __index = function(self, index)
        return (index >= 1 and index <= #self and index % 1 == 0 and 1) or nil
    end,
    __pairs = function(self) return ipairs(self) end
})

You will then be able to use arr with pairs and ipairs and indexing as if it were a table of five ones:

$ lua
Lua 5.4.4  Copyright (C) 1994-2022 Lua.org, PUC-Rio
> arr = {}
> arr = setmetatable(arr, {
    __len = function() return 5 end,
    __index = function(self, index)
        return (index >= 1 and index <= #self and index % 1 == 0 and 1) or nil
    end,
    __pairs = function(self) return ipairs(self) end
})
> arr[42]
nil
> arr[1]
1
> for _, v in ipairs(arr) do print(v) end
1
1
1
1
1
> for _, v in pairs(arr) do print(v) end
1
1
1
1
1

That said, such a dummy table is usually dirty. The fact that you were able to swap ipairs with pairs suggests to me that you are able to refactor the code that works with this dummy table. In that case I'd suggest generalizing it to work with an iterator rather than expecting a table; you can then trivially write a range iterator that loops over five ones.