3

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

Rider
  • 91
  • 4
  • I assume you meant `ipairs(arr)`. Double check ´GetTableValue´, does it return nil if the element does not exist? – Luke100000 Jan 13 '23 at 11:09
  • @Luke100000 I updated the problem to check the index before doing the `GetTableValue` function to make sure it returns `nil` if it goes out of range, but it still doesn't work – Rider Jan 13 '23 at 11:16
  • Can you define "doesnt work"? What values do you receive when printing the index and value in the loop? Will it spam nils? I can run your example (with only the tbl/arr fix applied) on all common lua versions. – Luke100000 Jan 13 '23 at 11:36
  • @Luke100000 When using `ipairs(arr)` to access values, the number of loops exceeds the length of arr – Rider Jan 13 '23 at 11:44
  • Yea but how do you notice. What are the out of bounds results of the iterator. – Luke100000 Jan 13 '23 at 11:47
  • @Luke100000 I've updated a use case that can be run directly – Rider Jan 13 '23 at 11:55
  • Thanks! I have bad news. It still works. See this online proof for the output I see: http://tpcg.io/_UL6LVZ I receive 5 times a 1. What Lua version do you use, and in what environment? – Luke100000 Jan 13 '23 at 12:04
  • @Luke100000 The version of lua is 5.4, I updated the image again – Rider Jan 13 '23 at 12:11
  • Ohh, I'm sorry, I assumed the online Lua would also be a 5.4. Yes, you example doesnt work on 5.4 because `__ipairs` has been removed. You need to use `pairs` and `__pairs` respectively. It gets stuck in a look because you also implemented __index, which returns a constant. I will validate my claims and post a proper answer soon. – Luke100000 Jan 13 '23 at 12:16
  • 1
    @Luke100000 You are correct, `__ipairs` was deprecated in 5.3 and removed in 5.4. `ipairs` iterates using normal access (`table[n]`) until first `nil` is encountered. Since `__index` always returns `1`, it's never done. The solution is `pairs` _or_ fixing `__index`. – Aki Jan 13 '23 at 12:21

2 Answers2

3

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
Luke100000
  • 1,395
  • 2
  • 6
  • 18
0

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.

Luatic
  • 8,513
  • 2
  • 13
  • 34