What general tips do you have for golfing in Lua? I'm looking for ideas that can be applied to code golf problems in general that are at least somewhat specific to Lua (e.g. "remove comments" is not an answer). Please post one tip per answer.
-
7\$\begingroup\$ Tips questions should be community wiki. But to whoever submitted this for close as "primarily opinion-based", the Tips for Golfing in Language questions are our accepted exception to that rule. The open-ended nature of these questions is why they are community wiki-ed. \$\endgroup\$– Jonathan Van MatreCommented Mar 17, 2014 at 16:18
11 Answers
In addition to the ones already posted, here are some tricks I've gathered over time, in no specific order...
For the function calls that only have one parameter delimited by a symbol (
"
for strings,{
for tables), the parameter doesn't need to be wrapped around parentheses.
For example, instead of doingprint("hello")
, you can simply do :print"hello"
Remove as much whitespace as possible - this is especially easy to do after a symbol that closes strings (or before one opening), function calls, tables...
Instead ofprint(42) a=1
you can doprint(42)a=1
. Other example:print(a and-1 or-2)
.Use the ternary operator when you can! Instead of
if a>0 then print("hello") else print("goodbye") end
, preferprint(a>0 and "hello" or "goodbye")
. More info here.
(This can actually get even better :print(a>0 and"hello"or"goodbye")
)Use the colon-call syntactic sugar when you can : instead of
string.rep(str,12)
, dostr:rep(12)
. That also works on non-variables this way (and only this way) :("a"):rep(5)
Instead of doing
tonumber(str)
just dostr+0
For functions with no parameters, instead of defining them the usual way (
function tick() blabla() end
), you can do :ticks=loadstring"blabla()"
, which saves 1 or more bytes, depending on the content. Also, if you define multiple functions, localizeloadstring
to a 1-char variable before and you'll save a lot of bytes ;). Credits to Jim Bauwens for this trick.Lua considers empty string (and
0
too unlike other languages) as true in conditional tests, so, for example, instead of doingwhile 1 do ... end
, save 1 byte by writingwhile''do ... end
({""})[i]or"s"
is 1 char shorter thani==1 and""or"s"
and can be written without a whitespace at the beggining, which may save one more byte.
-
\$\begingroup\$ (Added the loadstring trick) \$\endgroup\$– AdriwebCommented Aug 24, 2014 at 10:00
-
3\$\begingroup\$ 0 being a truthy value is just silly \$\endgroup\$ Commented Feb 2, 2016 at 14:21
-
2\$\begingroup\$ another
str+0
equivalent is~~str
, can be usefull for it's precedence \$\endgroup\$ Commented Jun 15, 2017 at 14:05 -
\$\begingroup\$ @FelipeNardiBatista however that's only supported on Lua 5.3+ \$\endgroup\$– AdriwebCommented Jun 15, 2017 at 14:52
Shorten your infinite loop
When you have to use an infinite loop, you may think of using a while
, but using a label instead is shorter by 2 byte:
while''do end
::a::goto a
Use the less space as possible
There's a simple thing you could (ab)use to remove even more spaces from your code. Lua's specs are clear about the name you give to variables: They have to start with a letter. It imply that, sometimes, you can skip on spaces between numbers and functions/variables
x=0>1 and 0or 1print(x)
The possibility of removing the space depends on the letter following the number, here's the letter that won't allow you to do this:
a,b,c,d,e,f -- They would be interpreted as hexadecimal
x -- only fail when after a 0, other number are fine
-- (0x indicates the following is an hexadecimal number)
By using this, and paying attention to how you call your variables, you can make most of your source codes space-free.
Picking up an example already here, and using this advice, here's one more byte you could shaved off :).
print(a and-1 or-2)
print(a and-1or-2)
Use the right input method
If we look at the boilerplate and cost for each major type of input, here's what we have:
function f(x)x end
io.read()
arg[1]
Each one of this method allow us to take 1 input, with the function being the one with the heaviest cost (but allows us to take a table as input)
We can now see that using command-line argument is the way-to-go if you want to golf, but be aware: it can be even shorter
arg[1]
...
The ...
are a bit special in lua, it's a variable containing the unpacked content of arg
, or the unpacked parameters in case of a variadic function.
When you will have to get more than one input, and use each of them, it can be good to do save them in a variable. Here's some ways to save 2 inputs in variables
a=arg[1]b=arg[2] -- highly un-efficient, costs 8 bytes by variable
a,b=unpack(arg) -- costs 15, but at least doesn't depends on the number of argument
a,b=... -- only costs 7
and here are the shortest call you could have done without the variables:
... -- using a allow a gain of 1-2 bytes at each use
arg[2] -- using b allow a gain of 4-5 bytes at each use
From the point where you have 3 argument, or when you use 2 arguments, with one used twice, you're already gaining bytes due to a,b=...
! :)
Almost never use if!
There's near no cases where using a if/elseif/if statement will cost less than a ternary. the boilerplate for such a statement is really heavy:
-- exemple with dumb values
if 1>0then v=1 else v=0 end
v=1>0 and 1or 0
With a simple example, you already save 12 bytes, when you have to do some elseifs, it become more and more important, so be aware of that!
Also, ternaries in lua are special, there's some condition in how they work, for those interested, I'll explain it below:
Ternaries in lua are of the form <condition> and <case true: have to be a true value> or <case false: can be anything>
First of all, let's see the truth table of the or
. A or
can be considered as a function: it always return a value, here's the value it returns:
x | y ||x or y
------||-------
0 | 0 || y
0 | 1 || y
1 | 0 || x
1 | 1 || x
That's what allow us to construct our ternary.
The and
is what allow us to evaluate the condition, it will always return y
if x and y
evaluates to true.
The problem with it is that it will fail if we want a nil
or false
to be return when the condition is false
. For instance, the following will always return 5, despite the condition being true.
v = true and false or 5
Here's a step by step evaluation of a ternary to explain how it works (it will be useful for when you have to nest them :))
-- let's use our dumb ternary
= true and false or 5
-- and statement will be evaluated first, leading to
= false or 5
-- and we saw how the or works
= 5
-
2
-
\$\begingroup\$ Note that the "Use the less space as possible" trick only works on Lua 5.2 and later. \$\endgroup\$– AdriwebCommented May 14, 2019 at 5:05
-
\$\begingroup\$ @Adriweb On Lua 5.2 and Lua 5.3, actually. They fixed parser accepting madness in 5.4. \$\endgroup\$ Commented Aug 21, 2020 at 10:24
-
\$\begingroup\$ @valsaysReinstateMonica some parts of it actually. For instance, this still works:
print(a and-1 or-2)
\$\endgroup\$– AdriwebCommented Aug 24, 2020 at 19:03
I have already thought of one.
I don't know if it works in some other languages, but Lua is the only one I know what allows you to store functions in variables. So if e.g. string.sub
is used multiple times in your program, use e.g. s=string.sub
.
-
7\$\begingroup\$ It also works in many other languages such as Python and Ruby. \$\endgroup\$– user344Commented Apr 21, 2014 at 19:16
-
7\$\begingroup\$ Javascript and Haskell can have function values too. \$\endgroup\$ Commented Sep 17, 2014 at 23:04
-
2\$\begingroup\$ This is equivalent to
s=("").sub
ors=a.sub
for any variablea
containing string value. \$\endgroup\$ Commented Oct 12, 2015 at 19:36 -
\$\begingroup\$ This is called first class functions \$\endgroup\$– rydwolfCommented Oct 28, 2019 at 12:28
It's a pretty verbose language for golfing... but some general tips that come to mind are:
- Try to avoid conditionals, since
if
...then
...else
...end
is a major waste. - Instead, try to focus on language constructs that are shorter, e.g.
for i=1,5 do
. - The
#
operator is pretty great for golfing (and in general).
I have compiled several tips as well. I'm sure some of mine will overlap with ones already stated, but I'll include them anyhow in the vein of creating a more complete answer.
Assign repeated functions to variables
Lua allows you to assign functions to variables. Even one character variables. This means if you repeat the function string.sub(x, y)
more than twice, you will get a benefit from assigning it to a variable.
Without assigning to a variable (69 characters):
print(string.sub(x, y))
print(string.sub(x, y))
print(string.sub(x, y))
Assigning to a variable (51 characters):
s=string.sub
print(s(x,y))
print(s(x,y))
print(s(x,y))
There's cases where you can take this yet a step further. Lua allows an OOP to string manipulation, like so: str:sub(x, y)
or str.sub(x, y)
This opens up new options for our code.
You can assign a variable to the function by it's reference as shown (46 characters.)
s=z.sub
print(s(x, y))
print(s(x, y))
print(s(x, y))
Use the most efficient way to parse strings
You may find yourself using a for
loop and string.sub
to iterate character by character in Lua. Sometimes this may work best, depending on your needs, but other times, string.gmatch will work in fewer characters. Here's an example of both:
s=io.read()
for a = 1, s:len() do
print(s:sub(a, a))
end
for i in io.read():gmatch('.') do
print(i)
end
And when golfed, the difference is more notable:
s=io.read()for a=1,s:len()do print(s:sub(a, a))end
for i in io.read():gmatch'.'do print(i)end
Restructure Assignations To Optimize Whitespace
In Lua, you do not have to put a space character between a closed parentheses or a end quotation mark and the next character. So far, I've found two cases where restructuring with this in mind will cut characters.
Assigning variables:
x,y=io.read(),0 print(x) vs. y,x=0,io.read()print(x)
If Statements:
if x:sub(1,1)==1 then vs if 1==x:sub(1,1)then
Return the fewest characters possible
If you must return a true or false value, then it seems you must necessarily use at least 5 characters for the return value. In reality the following works just as well:
return true
return false
vs
return 1>0
return 0>1
-
\$\begingroup\$ Great tips, I've taken the liberty to suggest an edit to your post. Only
nil
andfalse
evaluates to false in lua, everything else is true, so your tips about replacingx==0
,x==""
andx==''
byx
is false. I'm currently changing it tonil
:). \$\endgroup\$– KatenkyoCommented Feb 2, 2016 at 9:31 -
\$\begingroup\$ String paring example can be golfed to
(...):gsub('.',print)
. Replaceprint
withload'your function code'
in real golfing. \$\endgroup\$ Commented Aug 24, 2020 at 21:24
Variadic Functions
The main variadic function that will trouble you is print()
. For instance, when you're using it along String.gsub()
it will print the string you modified AND the number of times gsub
triggered.
To supress that second output, encapsulate your gsub
in parens to force it to return only one value
print(("").gsub("aa",".","!")) -- outputs "!! 2\n"
print((("").gsub("aa",".","!"))) -- outputs "!!\n"
-
\$\begingroup\$ you can move the first argument into the lhs of the . and replace with a : , i.e.:
print((("aa"):gsub(".","!"))
\$\endgroup\$ Commented Apr 16 at 9:12
The expression a==1 and 1or 0
can be shortened to #{[a]=0}
.
This works because the array length operator #
is actually measuring the number of consecutive integer array indices starting at 1. (Arrays in Lua are 1-based, but they allow assignment at other indices, even those that are negative or floating point.) If there no element at index 1, it will always report a length of zero. And {[a]=0}
is an anonymous array with an element of value 0
at index a
.
How many bytes this will save depends on the circumstance. For example, if you're already using n%i<1
as a shorthand for n%i==0
to test divisibility, then changing n%i<1 and z or 0
to z*#{[n%i+1]=0}
saves 2 or 3 bytes, depending on whether it's followed by a symbol or a keyword/identifier.
These are Lua only (I think) optimizations:
Strings are automatically converted into numbers when doing arithmetic operations on them. Just watch out for conditional statements, they do not automatically convert. This is great for taking user input as numbers while saving space.
Switching the contents of two variables does not require a temporary variable. a,b=b,a
will swap the values of a and b.
Also, to extend upon what was said above, any alphanumeric character can touch a non alphanumeric character. So a,b=io.read():match"(.+)/(.+)"u,v=a,b
is a perfect, working script, even with the lack of whitespace.
A bunch of tips in no particular order:
string
is pretty long name. Efficiently,('').char
is same asstring.char
. Even better results can be achieved if you use it together with a semicolon on variables:a=...; print(a:sub(1, 5))
, but somestring
functions don't take strings as input.- Lua has automatic conversions between strings and numbers for most cases, so
tonumber
and+0
often only waste bytes. - Always use
load'your function code here'
instead offunction()your function code here end
. Access function arguments using...
inside. - Some string functions in Lua can be used in unintended ways! For example,
a:gsub('.',load'my function')
seems to be the shortest way to iterate over chars in a string - While the string engine is powerful, beware of its power when using user input as patterns! Because of that, you might have to use
a:find('.',1,1)
(to test for this issue, try including%
at various places in your input and check out results). Countless ideas broke because of Lua trying to parse input as pattern. nil
is three bytes,_
is one (it is just random name which most likely doesn't exist). Also, any digit will work as truthy value.- Know your logic behind
x and i or o
. It isn't just a ternary operator - it is a complete logical expression. In fact, it means the following: "ifx
is truthy, tryi
. If either x or i is falsy, return o". So ifi
is not truthy, result iso
. Also, bothand
oror
parts can be omitted (x and i
,x or o
). - Use integer division by one instead of
math.floor
:5.3//1==5.0
. Please note that the resulting number always follows the type of input one (integer/float).
-
1\$\begingroup\$ "Also, any digit will work as truthy value." Just wanted to elaborate that this includes 0, which might not be very intuitive to some coders from a C/C++ background. \$\endgroup\$– ouflakCommented Oct 30, 2019 at 8:10
Combine Local Variable Assignments
Instead of:
local a=42
local b=17
local c=99
Use parallel assignment:
local a,b,c=42,17,19
6 bytes saved for each variable!
Declare Local Variables via Unused Function Parameters
Instead of:
function foo(a,b) local c,d ... end
function bar(a,b) local c,d=42,17 ... end
Use
function foo(a,b,c,d) ... end
function bar(a,b,c,d) c,d=42,17 ... end
6 bytes saved (minus 1-2 bytes for each variable that might be duplicated).
-
7\$\begingroup\$ Downvoted because there's absolutely no case where using
local
is justified when golfing, because you just have to use a different name. We could use ALL the names up to 7 charachters AND tables with string indexes up to 7 charachters combinations before we're hitting something that could benefit from using locals \$\endgroup\$– KatenkyoCommented Oct 25, 2017 at 23:49
Know how to output
There's two main method of outputting in lua
io.write() -- outputs without trailing newline
print() -- outputs with trailing new line and shorter by 3 bytes
When you have to concatenate multiple times, you could shorten that by using io.write()
assigned to a one letter variable instead of the standard concatenation operator ..
i(a) -- i was defined as io.write
s=s..a
You gain 2 bytes at each call while paying some upfront
i=io.write -- longer by 6 bytes
s=""
You're even on the third concatenation, and start gaining byte on the fourth.
-
4\$\begingroup\$ It is
print
and notprintf
! \$\endgroup\$ Commented Oct 28, 2019 at 11:41 -
\$\begingroup\$ @val Wow, I don't even know how I could do that mistake. Thanks for pointing it out, I'll edit \$\endgroup\$– KatenkyoCommented Oct 29, 2019 at 10:54