Why bicker about it? Just benchmark it.
The code I used
function testA(Text)
if Text == "abc" then
-- code
elseif Text == "123" then
-- code
elseif Text == "def" then
-- code
elseif Text == "456" then
--code
elseif Text == "ghi" then
--code
elseif Text == "789" then
--code
end
end
local TextTable = {
["abc"] = function()
--code
end,
["123"] = function()
--code
end,
["def"] = function()
--code
end,
["456"] = function()
--code
end,
["ghi"] = function()
--code
end,
["789"] = function()
--code
end,
}
function testB(Text)
return TextTable[Text]()
end
function testC(Text)
local TextTable = {
["abc"] = function()
--code
end,
["123"] = function()
--code
end,
["def"] = function()
--code
end,
["456"] = function()
--code
end,
["ghi"] = function()
--code
end,
["789"] = function()
--code
end,
}
return TextTable[Text]()
end
local cases = {"abc", "123", "def", "456", "ghi", "789"}
local start
start = os.clock()
for i = 1,10000000 do
testA(cases[i%6 + 1])
end
print("Test A", os.clock() - start)
start = os.clock()
for i = 1,10000000 do
testB(cases[i%6 + 1])
end
print("Test B", os.clock() - start)
start = os.clock()
for i = 1,10000000 do
testC(cases[i%6 + 1])
end
print("Test C", os.clock() - start)
Test A is with the if-else chain.
Test B is with the table if it is created only once.
Test C is with the table if it is recreated every time (which is the way @nooneisback thinks it has to be done)
Each test function is called 10 million times.
There might be some influence from the way the string is chosen.
Results:
Studio (Luau)
Test A 0.82050225022249
Test B 1.5509403150645
Test C 8.4669149238034
Luvit (LuaJIT on 5.1)
Test A 0.231
Test B 0.454
Test C 11.828
Lua 5.3
Test A 2.4510000000009
Test B 1.9739999999874
Test C 10.441999999981
With 6 items, the if-else chain runs faster, is easier to understand and takes less memory.
Let’s try it again with 500 items.
The rough outline of the code I used (not the whole thing, which is 1024 lines)
function testA(Text)
if Text == 001 then
elseif Text == 002 then
-- ...
elseif Text == 499 then
elseif Text == 500 then
end
end
local TextTable = {
[001] = function() end,
[002] = function() end,
-- ...
[499] = function() end,
[500] = function() end,
}
function testB(Text)
return TextTable[Text]()
end
local start
start = os.clock()
for i = 1,10000000 do
testA(i%500 + 1)
end
print("Test A", os.clock() - start)
start = os.clock()
for i = 1,10000000 do
testB(i%500 + 1)
end
print("Test B", os.clock() - start)
Results:
Studio
Test A 19.097824928002
Test B 1.1308720872039
Luvit
Test A 2.005
Test B 53.229
EXCUSE ME?
I really don’t know why it does that.
It took twice as long when I rewrote the test to use string keys.
It took 14.011 seconds with a table.find-like thing (for i = 1, #asdf
) thing in Luvit, 71 seconds with the same in Studio and 27.827956805297 with table.find in Studio (return d2[table.find(d, Text)]()
).
Lua 5.3
Test A 62.782000000007
Test B 1.9919999999693
So why use tables instead of if-else chains?
The difference between the two is that if-else chains have to check each candidate individually. This means that if you have a case that the if-else chain doesn’t handle, then it must be compared to all of them to find that it doesn’t match.
On the other hand, tables are hash maps. This means that the key is hashed and the result is looked up much like in an array. Even in a table with a trillion keys, only a few comparisons need to be done; all the other keys are ignored.
In more formal terms, the if-else chain is a linear-time algorithm (O(n)
).
As the amount of items to compare to increases, so does running time. It’s fast with a few items, but slow with many items.
The table approach is a constant-time algorithm (O(1)
).
The running time stays constant regardless of the amount of items.
The reason why the table approach is slower is because it needs to hash the string to find the result, so there’s some overhead to it that the if-else chain doesn’t. Oh yeah, the function call, too.
So for performance, use an if-else chain if you have a few choices and a table/map of functions if you have a very large amount of cases.
Another difference is that tables (hashmaps…) are limited to exact matches only.
If you want to do one thing if Text is “asd”, another if it’s “ghi” and a third thing if it starts with “jkl”, you can’t do that with tables only; you have to check that with an if string.match(Text, "^jkl")
.
The most compelling reason to use tables of functions instead of if/else chains is modifying your code dynamically.
You can add and remove from the table of functions. This allows module systems and similar. Most popular admin command systems past Person299’s probably do this.
TL;DR YandereDev’s code is bad, but don’t write your code differently just to blindly avoid his mistakes. You should be able to explain why you’re taking one approach over another.