User:LennardHofmann/Lua
This is a list of things about writing Lua modules that I had to figure out the hard way.
Call functions that expect a frame object
[edit]To call a function that expects a frame object, you need to give it a table containing an args
table like so: WikidataIB.getValue{ args = {'P31', qid='Q42'} }
. Prefer functions that take arguments directly, e.g. WikidataIB._getValue
, over those that expect a frame object.
Get user's language on multilingual wikis
[edit]Scribunto does not provide a Lua interface to get the user's language code ("en"), so we need to call a parser function:
frame:callParserFunction( "int", "lang" )
This only works on wikis where subpages of MediaWiki:Lang are set up. Wikimedia Commons is such a wiki.
Performance
[edit]Don't be that guy who stops caring about performance because "premature optimization is the root of all evil". There are several easy ways to optimize Lua code without compromising readability.[1][2]
- Always use local variables and functions.
- Do not build long strings using repeated concatenations. Add the string pieces to a table and use
table.concat()
at the end.[3] - Use
t[#t+1] = v
to add a value to a table instead oftable.insert(t, v)
to avoid function overhead. - Do not create the same closure or table in every loop iteration. Define it outside the loop.
- Lua rehashes a table if its size exceeds a power of two after adding an element.[4] So don't do this: Do this instead:
local args = {} args[1] = 'P31' args.qid = 'Q42' _getValue( args )
If you are creating lots of empty tables that each need room for n elements, initialize it with n_getValue{ 'P31', qid = 'Q42' }
nil
values:local args = { nil, nil, nil }
.
Optimizations that are not worth it
[edit]The following optimizations are generally not worth going for because they add a bit of noise to the code:
Slow | Fast | |
---|---|---|
Adding to table inside loop | for i = 1, 99999 do
t[#t+1] = i
end
|
local len = #t + 1
for i = 1, 99999 do
t[len] = i
len = len + 1
end
|
Iterating over a sequence table | for i, v in ipairs(t) do
···
end
|
for i = 1, #t do
local v = t[i]
···
end
|
Calling a library function repeatedly | for i = 1, 99999 do
math.sin(i)
end
|
local sin = math.sin
for i = 1, 99999 do
sin(i)
end
|
Benchmarking
[edit]You can do benchmarks from the Lua console with
local s = os.clock()
-- your code here
print('took ' .. os.clock() - s .. ' seconds')
Handling nil values
[edit]Be careful with variables that can be nil
: Indexing a nil table or trying to iterate over it with ipairs()
will result in an error:
Bad | Good |
---|---|
v = claim.mainsnak.datavalue.value
-- error if claim or datavalue are nil
|
v = claim and claim.mainsnak.datavalue and claim.mainsnak.datavalue.value
if v then
-- do something with v
end
|
t = getTableOrNil()
for i,v in ipairs(t) do
|
t = getTableOrNil() or {}
for i,v in ipairs(t) do
|
When working with Wikidata items, make sure that your code does not produce errors if you give it an empty item. Another common source of nil errors are somevalue and novalue claims, where mainsnak.datavalue == nil
.
Useful links
[edit]- Lua reference manual
- Comparable Lua functions to wikitext
- Documentation, JSON spec, and source code for the
mw.wikibase
library - Source code for several libraries, including
mw.text
References
[edit]- ↑ StackOverflow answer by dualed. Retrieved June 3, 2022.
- ↑ Lua Performance on the Spring Wiki. Retrieved June 3, 2022.
- ↑ Programming in Lua, chapter 11.6. lua.org.
- ↑ Lua Performance Tips (PDF) by Roberto Ierusalimschy