I'm trying, as an exercise, to make a set implementation in Lua. In particular, I want to use the simplified implementation of Pil2 11.5 and grow it to enable the ability to insert values, delete values, etc.
Now the obvious way to do this (and the way it works):
Set = {} function Set.new(l) local s = {} for _, v in ipairs(l) do s[v] = true end return s end function Set.insert(s, v) s[v] = true end ts = Set.new {1,2,3,4,5} Set.insert(ts, 5) Set.insert(ts, 6) for k in pairs(ts) do print(k) end
As expected, I get printed numbers from 1 to 6. But these calls to Set.insert(s, value) really pretty ugly. I would rather call something like ts:insert(value) .
My first attempt at such a solution looked like this:
Set = {} function Set.new(l) local s = { insert = function(t, v) t[v] = true end } for _, v in ipairs(l) do s[v] = true end return s end ts = Set.new {1,2,3,4,5} ts:insert(5) ts:insert(6) for k in pairs(ts) do print(k) end
This works basically fine until you see what comes of it:
1 2 3 4 5 6 insert
It is very obvious that the insert function, which is a member of the table, is displayed. Not only is it uglier than the original problem of Set.insert(s, v) , it is also prone to serious problems (for example, what happens if "insert" is a valid key that someone is trying to enter?). It's time to hit the books again. What happens if I try this instead ?:
Set = {} function Set.new(l) local s = {} setmetatable(s, {__call = Set.call}) for _, v in ipairs(l) do s[v] = true end return s end function Set.call(f) return Set[f] end function Set.insert(t, v) t[v] = true end ts = Set.new {1,2,3,4,5} ts:insert(5) ts:insert(6) for k in pairs(ts) do print(k) end
Now I am reading this code:
- When I call
ts:insert(5) , the fact that insert does not exist for the call means that the metatet ts will look for "__call" . - Metatable
"__call" ts returns Set.call . - Now
Set.call is called with the name insert , which forces it to return the Set.insert function. Set.insert(ts, 5) .
What really happens:
lua: xasm.lua:26: attempt to call method 'insert' (a nil value) stack traceback: xasm.lua:26: in main chunk [C]: ?
And at that moment I'm at a standstill. I have no idea where to go from here. I cracked for an hour with varying degrees of increasingly desperate variations of this code, but the end result is that I canβt do anything. What is undoubtedly obvious in this matter?