So, I'm trying to learn metamethods. I'm trying to make a script that can add arrays of numbers together, but will error if there's non-number elements in either table. Here's what I have:
t1 = {1, 2, 3} t2 = {4, 5, 6,"swag noob"} mt = { __add = function(t1, t2) for _, v in pairs(t1) do --Check table 1 for non-number values if not type(v) == "number" then error("Numbers. You need to use numbers!") return end end for _, v in pairs(t2) do --Same for table 2 if not type(v) == "number" then error("Numbers. You need to use numbers!") return end end local sumtable = {} --Table of sums local function add(iterations) for i = 1, iterations do table.insert(sumtable, t1[i] + t2[i]) --Add elements, put sums in sum table end end if #t1 > #t2 then add(#t2) --The next few lines are to make sure that we're not adding elements that don't exist. elseif #t1 < #t2 then add(#t1) else add(#t1) end return sumtable end } setmetatable(t1, mt) print(table.concat(t1 + t2, ", "))
Although "swag noob" is not a number value, the output won't error. It will add 1 to 4, 2 to 5, and 3 to 6. That's not what I want. When I take 6 out of the table and leave "swag noob", I get...
Workspace.Script:24: attempt to perform arithmetic on field '?' (a string value)
The errors are supposed to be caught in the beginning of the metamethod, but they aren't.
This is an order-of-operations mistake.
If you check the Lua operator precedence article, you'll see that not
is higher precedence than ==
. What does that mean?
That means that not a == b
is interpretted as (not a) == b
-- which is NOT a ~= b
.
You should use ~=
when you want to see if things are different!
I would suggest a few code-cleanup-things. Repeating code is bad -- you should have a function that checks for non-number values. You can also take advantage of assert
to eliminate the if
entirely:
function onlyContainsNumber(tab) for _, el in pairs(tab) do assert(type(el) == "number", "table must contain only numbers") end end ..... onlyContainsNumbers(t1) onlyContainsNumbers(t2)
I would argue that you usually don't want to add tables of different lengths -- because you'll lose some information, that is probably a mistake when it happens.
Even so, you can simplify your usage of add
using math.min
, which gives you the smaller of a few numbers (so you can get the shorter of the two lengths)
local sum = {} for i = 1, math.min(#t1, #t2) do sum[i] = t1[i] + t2[i] -- Usually `table.insert` describes that you are appending -- so it is what you want. -- In this case, using `[i] = ` better illustrates the relationship -- between sum[i] and t1[i] and t2[i]. -- (It's also much faster) end return sum
You probably want to set the metatable of sum
to be mt
, too!