29
\$\begingroup\$

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.

\$\endgroup\$
1
  • 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\$ Commented Mar 17, 2014 at 16:18

11 Answers 11

14
\$\begingroup\$

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 doing print("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 of print(42) a=1 you can do print(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, prefer print(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), do str:rep(12). That also works on non-variables this way (and only this way) : ("a"):rep(5)

  • Instead of doing tonumber(str) just do str+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, localize loadstring 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 doing while 1 do ... end, save 1 byte by writing while''do ... end

  • ({""})[i]or"s" is 1 char shorter than i==1 and""or"s" and can be written without a whitespace at the beggining, which may save one more byte.

\$\endgroup\$
4
  • \$\begingroup\$ (Added the loadstring trick) \$\endgroup\$
    – Adriweb
    Commented 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\$
    – Adriweb
    Commented Jun 15, 2017 at 14:52
8
\$\begingroup\$

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
\$\endgroup\$
4
  • 2
    \$\begingroup\$ One tip per answer, please. \$\endgroup\$
    – ATaco
    Commented Nov 7, 2016 at 2:26
  • \$\begingroup\$ Note that the "Use the less space as possible" trick only works on Lua 5.2 and later. \$\endgroup\$
    – Adriweb
    Commented 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\$
    – Adriweb
    Commented Aug 24, 2020 at 19:03
5
\$\begingroup\$

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.subis used multiple times in your program, use e.g. s=string.sub.

\$\endgroup\$
4
  • 7
    \$\begingroup\$ It also works in many other languages such as Python and Ruby. \$\endgroup\$
    – user344
    Commented 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 or s=a.sub for any variable a containing string value. \$\endgroup\$ Commented Oct 12, 2015 at 19:36
  • \$\begingroup\$ This is called first class functions \$\endgroup\$
    – rydwolf
    Commented Oct 28, 2019 at 12:28
5
\$\begingroup\$

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).
\$\endgroup\$
5
\$\begingroup\$

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
\$\endgroup\$
2
  • \$\begingroup\$ Great tips, I've taken the liberty to suggest an edit to your post. Only nil and falseevaluates to false in lua, everything else is true, so your tips about replacing x==0,x=="" and x=='' by x is false. I'm currently changing it to nil :). \$\endgroup\$
    – Katenkyo
    Commented Feb 2, 2016 at 9:31
  • \$\begingroup\$ String paring example can be golfed to (...):gsub('.',print). Replace print with load'your function code' in real golfing. \$\endgroup\$ Commented Aug 24, 2020 at 21:24
5
\$\begingroup\$

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"
\$\endgroup\$
1
  • \$\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
5
\$\begingroup\$

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.

\$\endgroup\$
3
\$\begingroup\$

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.

\$\endgroup\$
3
\$\begingroup\$

A bunch of tips in no particular order:

  • string is pretty long name. Efficiently, ('').char is same as string.char. Even better results can be achieved if you use it together with a semicolon on variables: a=...; print(a:sub(1, 5)), but some string 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 of function()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: "if x is truthy, try i. If either x or i is falsy, return o". So if i is not truthy, result is o. Also, both and or or 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).
\$\endgroup\$
1
  • 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\$
    – ouflak
    Commented Oct 30, 2019 at 8:10
2
\$\begingroup\$

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).

\$\endgroup\$
1
  • 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\$
    – Katenkyo
    Commented Oct 25, 2017 at 23:49
2
\$\begingroup\$

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.

\$\endgroup\$
2
  • 4
    \$\begingroup\$ It is print and not printf! \$\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\$
    – Katenkyo
    Commented Oct 29, 2019 at 10:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.