This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Kingdaro's profile picture

Some lua tricks you may not have known...

Started by Kingdaro, 20 August 2012 - 08:51 AM
Kingdaro #1
Posted 20 August 2012 - 10:51 AM
EDIT: Hey, it's me from the future. Lots of stuff has changed, one of those things being that I'm not all that interested in CC or Lua that much anymore. But it seems like some people are still stumbling upon this (badly written) post! Since there's still some interest in it, thanks to SquidDev, you can view the full formatted post here. The rest of this post remains, as always, for reference.



I've rewritten the Lua Tricks tutorial and hosted it on my github page. You can access it here: http://kingdaro.gith...lua-tricks.html

Old content for reference:

Spoiler
Lua Tricks
Note that this is not a lua tutorial and is intended for intermediate to advanced lua users.

I've been looking at some peoples' programs around here and I notice that a lot of them aren't using some neat tricks that may or may not make coding easier/neater.

Strings
The first thing is that a lot of people use double quotes instead of single quotes, but I think that's just a preference in most cases. In case you still aren't aware:

string1 = ''
string2 = ""

Both of these string variables are valid. I should probably mention that you should be careful with using single quotes, because there are common words with apostrophes such as "don't" and "you're". To prevent errors, you should put backslashes before single quotes inside single quotes:

string1 = 'don\'t'

An alternate solution is to use double quotes in this case.

There's also this neat trick that I didn't learn until a little further down the line in my use of lua - multiline strings. Defined by [[ ]], much like ' ' and " ".

The following example is valid:

print [[This is
a multiline
string.]]

Be wary that using multiline strings with tabs before text will actually output the tabs as well.

print [[This string
	  will take the tabbing
	  with it.]]

Multiline strings are useful in printing menus for your console. Instead of doing this:

print '------------------------------'
print 'Screen Title'
print '------------------------------'
print 'Option'
print 'Option'
print 'Option'

you can do:

print [[
------------------------------
Screen Title
------------------------------
Option
Option
Option
]]

A similar helpful trick to multiline strings can be done with comments, starting with –[[ and ending with ]]:

--[[
This is a multiline comment.
It does nothing :P/>/>/>/>
]]

I learned this very recently from an amazing youtuber named NitroFingers. You can use the # operator on strings to get the length of a string, much like string.len().

myString = 'waffles'

print(string.len(myString))
--> 7
print(#myString)
--> 7

I haven't tested this with all string methods, but in most cases, you'll usually be able to perform the method on the actual string instead of calling the method with the string as an argument.

local s = 'waffles'
s:sub(1,3)
--> waf
s:match 'f.-s'
--> fles

for i,v in s:gmatch '.' do
  print(v)
end
--> w
--> a
--> f
--> f
--> l
--> e
--> s

Functions
Function calls are also flexible in other ways. In the case that a function only takes one argument and it is a string, you don't have to supply parentheses.

print('a string')
print 'a string'
Both of these examples will print a string.

Unless you're getting deep into some OOP stuff, this won't be very helpful to you in a lot of cases. If a function takes a table argument, you can drop the parentheses, like strings.

myFunction {
a = 1;
b = 2;
}

There's also an alternate way of defining functions.

myFunct = function()
-- stuff
end

For me, this is useful for defining functions without having to go outside your table:

myTable = {
funct1 = function()
  print 'stuff'
end;

funct2 = function()
  print 'more stuff'
end;
}

myTable.funct1()
myTable.funct2()

Functions are able to be defined with a variable number of arguments:

function stuff(...)

end

The three dots represent the table of arguments given and can me accessed in the function as "arg".

function stuff(...)
for i,v in pairs(arg) do
  write(v)
end
end

stuff(1,2,3,'a','b','c')
--> 123abc6

Woah wait, where'd that 6 come from? That's just the number of arguments given. It's indexed in the "arg" table as n.

print(arg.n)
--> 6

However you'll still get the number of arguments given if you use the pound sign operator, as it only counts values given as indexes.

print(#arg)
--> 6

At the end of the day, it's all a matter of preference. To make things less complicated, you can simply store your arguments in a local table, as shown by some computercraft base programs.

tArgs = { ... }

If you don't want to check for individual arguments in a table, you can catch variable arguments using variables instead.

-- the old way:
local args = {...}
local length, height = args[1], args[2]

-- the new way
local length, height = ...
Thinking about it is kind of screwy, but makes sense at the same time.

In lua, you have the ability to directly call anonymous functions without having actually defined them yet. It will sound less confusing when I use an example. Say I wanted to have a table full of all of the available colors in numerical order.

local colorTable = (function()
  local t = {}
  for _, color in pairs(colors) do
	if type(color) == 'number' then
	  table.insert(t, color)
	end
  end
  table.sort(t)
  return t
end)()

print(colorTable[1])
--> 8192

Functions can be used as "iterators", which is a fancy term for a function that keeps returning, and at some point, returns nil, such as when a table is empty, when there are no more lines in a file, or when a number has reached a certain limit. Iterators are used in "for" statements, like with pairs or ipairs.

-- this function returns random numbers from 1 to 10, but doesn't return anything if the number is 10.
local function randomNumbers()
  local n = math.random(1,10)
  if n == 10 then
	return nil
  end
  return n
end

-- this is how we'd do it the old way.
while true do
  local n = randomNumbers()
  if n then
	print(n)
  else
	break
  end
end

-- and this is using it as an iterator
for n in randomNumbers do
  print(n)
end

A more applicable example:

local file = fs.open('my-data', 'r')
local lines = {}

-- the old way:
while true do
  local line = file.readLine()
  if line then
	table.insert(lines, line)
  else
	file.close()
	break
  end
end

-- the new way:
for line in file.readLine do
  table.insert(lines, line)
end

I feel like you may be wondering something, though. In the examples above, our iterators don't have parentheses. However pairs() and ipairs() do. This is because the functions pairs and ipairs aren't iterators, but they return iterators. They return the next() function, which returns the next key/value pair in a table after the one given, like how file.readLine returns the next line in a file. The next() function, however, does not automatically know which index you're currently on. It must be stored and given to it he next time you want a key/value pair out of it.

local data = {
  name = 'Kingdaro';
  title = 'The Doctor';
  posts = 'A lot';
}
local key, value

-- we do not have a current "position" in the table, so we just get the first index that lua can see.
key, value = next(data)
print(key, value) --> posts, A lot
-- note that lua will not always find the indices in order.

-- so we take the current key we have, and use it to find the next index and key.
key, value = next(data, key)
print(key, value) --> name, Kingdaro

-- and again
key, value = next(data, key)
print(key, value) --> title, The Doctor

-- at this point, we run out of key/value pairs, so we just get nil after we try to get another.
key, value = next(data, key)
print(key, value) --> nil

And what we did above is what the function that pairs() returns does. Remember that it's not pairs() itself, because if it were, we couldn't pass a table to iterate through using next().

Tables
Like strings, the # operator can also be used to find the length of tables.

myTable = {'hello', 'world!', 3, 4, 5}
print(#myTable) --> 5

This is tricky though; the length of a table is counted up until the last non-nil index. Setting an index in the middle of a table to nil will still have its elements counted up until the last value that isn't nil. If the last value becomes nil, the counter decreases until it's found a non-nil value. For example, the length of {1, nil, 3} is three, and the length of {1, 2, nil} is two. Indices that are strings do not count, so the length of {5, 7, 3, hello = 'world'} would be three.

Moving on, when I see people do this:

something = {}
something['key'] = 'value'
something['sub'] = {}
something['sub']['key'] = 'value'
I die a little on the inside.

It's not very clean, and it's much more typing than doing this:

something = {
  key = 'value',
  sub = {
	key = 'value'
  }
}
Defining tables and subtables this way allows you to more accurately see the hierarchy of the table, and as stated before, is much cleaner.

Note that there's still an alternative even when you need to define new indices after making the table. You can use a dot syntax instead of the string around brackets.

local stuff = {}

function defineStuff()
  stuff.x = 5
  stuff.y = 5
  stuff.fun = {
	foo = 'bar';
  }
end

defineStuff()
print(stuff.fun.foo) --> bar

However, brackets are required when using string variables, and the dot syntax is only used to get and set raw indices.

local stuff = {}
local key = 'hello'

stuff[key] = 'world'

-- this is wrong; we actually get the key named "key" instead of the value of the variable key
print(stuff.key) --> nil

-- this is right; the value of the variable key is "hello", and up there, we set the key named "hello", the value of the key variable.
print(stuff.hello) --> world

Also, when defining indices in tables, you can actually use semicolons instead of commas. For me, this is easier because I can rearrange elements of the table without having to redo my commas.

myTable = {
  index1 = "foobar";
  index2 = 1337;
}

In case you didn't know this either, you can also use strings to access indexes not defined by strings. This would be helpful for storing functions for possible user input in tables.

local answers = {
  yes = 'You\'re cool!';
  no = 'Fine, be that way.';
}

print 'Do you like pineapples?'
input = read() -- this is accepted as a string.
if answers[input] ~= nil then
print(answers[input])
else
print 'I don't understand you, sorry.'
end

Tables and multiple arguments/variables get along really well. Remember you can do this:

args = {...}

In this situation, … could represent "a, b", or "foo, bar, baz", or just "hello". Therefore, you can do this:

local function add(n1, n2)
  print(n1 + n2)
end

local myTable = {5, 15}
add(unpack(myTable))

And you can do this:

local function numbers()
  return math.random(1,10), math.random(11, 20)
end

local myTable = {numbers())
print(myTable[1]) --> a random number from 1 to 10
print(myTable[2]) --> a random number from 11 to 20

In a situation applicable to ComputerCraft, you can even do this:

while true do
  local events = {os.pullEvent()}
  if events[1] ~= 'mouse_click' then
	doEvents(unpack(events))
  end
end

This catches the events, and only sends them to the doEvents function if the event isn't mouse_click.

Numbers
There isn't much to learn in this category, however I think you'll eventually find it useful that you're actually able to concatenate numbers like strings.

print(3 .. 3)
--> 33
There need to be spaces in between the dots and the numbers, otherwise lua will confuse your dots as decimals and throw an error.

Some of you coming from a different programming background may have already known this, but the function math.fmod() can be replaced with the operator %. If you don't know what this does, it returns the remainder of the division of two numbers. e.g. 7 % 3 would equal 1.

You could use this to tell if a number is even - if the remainder of the number and 2 is 0.

while true do
  textutils.slowPrint 'Type in a number.'
  write '> '
  local input = read()
  local num = tonumber(input)
  if num then --checking if the input was successfully converted into a number
	if num % 2 == 0 then
	  textutils.slowPrint 'Your number is even.'
	else
	  textutils.slowPrint 'Your number is odd.'
	end
  elseif input == 'exit' then --give the ability to leave the program.
	textutils.slowPrint 'Thank you for using my useless program! :P/>/>/>/>/>/>/>/>/>/>'
	sleep(1)
	break
  else
	textutils.slowPrint "That's not a valid number!"
  end

  sleep(1)
end

Booleans
In the bools field, there's actually a cute little statement that is way easier than if … elseif … end:

var = condition and set_if_true or set_if_false
Five unnecessary lines condensed into one. Here's an example:


var = 5 > 3 and 'yes!' or 'aw'
In this case, var would be 'yes!' because 5 > 3. If on some distant alien planet in a parallel universe 5 were less than three, var would be 'aw'.

If you wanted to have a bit of fun, you can make yourself a clamp function by nesting these.

local function clamp(low,n,high)
return n < low and low or (n > high and high or n)
end

There's also a simple way of reversing a boolean:

bool = not bool

In a more applicable example, you could make a sort of toggling light switch program with one line (assuming your output is in the back and hooked up to your lights)

rs.setOutput('back', not rs.getOutput('back'))

Oh, and remember that % example? This:

	if num % 2 == 0 then
	  textutils.slowPrint 'Your number is even.'
	else
	  textutils.slowPrint 'Your number is odd.'
	end

Could be this:

	textutils.slowPrint('Your number is ' .. (num % 2 == 0 and 'even.' or 'odd.'))
Brackets needed because it isn't just one string anymore XD

Metatables
Christ, where do I begin.


local myTable = {
  value = 5;
  anotherValue = 10;
}

local mt = {
  __call = function(tab, index, value)
	if not index then return end
	if value then
	  tab[index] = value
	end
	return tab[index]
  end;
}

setmetatable(myTable, mt)

What I just did here, is that I just made a callable table. Basically, I've made it so that this won't error:

myTable()

Furthermore, in my specific example, I can get and set the keys and values of myTable through function calls.

print(myTable('value')) --> 5
myTable('value', 10)
print(myTable('value')) --> 10

This is but one of the very many powerful functions of metatables. A metatable is like a child or sibling to a normal table. When you do something with a metatable, such as call it like a function, lua looks for a certain value in the table's metatable so it knows what to do in that situation. These values are called "metamethods". A very simple and commonly used metamethod is __index.

The __index metamethod is triggered when you try to access a value in a table that doesn't exist. If you define the __index metamethod as a table, Lua will try to look for a key in that table that doesn't exist in the original table. With this system, it's easy to make a lookup table:

local lookupTable = {
  someValue = 5;
}

local myTable = {
  -- no values here!
}

print(myTable.someValue) --> nil
setmetatable(myTable, { __index = lookupTable })
print(myTable.someValue) --> 5

There's also __add, __sub, __mul, __div, and __tostring among others. You can find more about metatables on lua-users. http://lua-users.org...methodsTutorial



You could've lived your life without knowing any of this, really. However I thought it'd be helpful to some of you people, a couple of interesting tidbits to know. Pretty sure I'm missing some stuff but I'm not necessarily a book of infinite useless lua knowledge :D/>/>/>
Edited on 25 February 2019 - 07:28 AM
FuzzyPurp #2
Posted 20 August 2012 - 11:07 AM
Nice Thread!
Cranium #3
Posted 20 August 2012 - 02:55 PM
I learned a few things here! +1 for you!
Kingdaro #4
Posted 20 August 2012 - 03:48 PM
Thanks guys. :D/>/>
Graypup #5
Posted 24 August 2012 - 01:26 AM
Nice, now we just have to convince the noobs to read programming in lua :D/>/>.
Kingdaro #6
Posted 24 August 2012 - 05:46 AM
Reformatted the post and reorganized/added some more information.
nitrogenfingers #7
Posted 24 August 2012 - 09:18 AM
Nice topic, picked up a few tips I wasn't familiar with. Thanks for posting!

One small thing though, you mention you can use strings to access non-string indexed fields in tables. That's actually another little eccentricity in lua as the statements

table example = {
  ["a"] = 1;
  ["b"] = 2;
}
and
table example = {
  a = 1;
  b = 2;
}

Are actually equivalent, which also means that


print example.a
and
print example["a"]

Are also equivalent. I learnt that a few weeks ago, rather nice bit of syntactic sugar.
TntTerminal #8
Posted 26 August 2012 - 07:35 PM
Thank you this helped me a lot! Especially using a ' instead of a " because when i type a " nothing appears, but when i type anything else it appears but what i typed dont. For example: if i type: "hello" It appears "ello" so i need to type "h" two times, i dont know why.
Noodle #9
Posted 27 August 2012 - 04:09 AM
Nice work!
ETHANATOR360 #10
Posted 31 August 2012 - 08:07 PM
very good +1
kaj #11
Posted 01 September 2012 - 09:54 AM
Thanks for this.
Favorited / +1
I'm finding this really helpful.
Jajnick #12
Posted 03 September 2012 - 11:02 AM
Everybody who have checked lua-users wiki knows that…
Kingdaro #13
Posted 03 September 2012 - 07:49 PM
Everybody who have checked lua-users wiki knows that…
The people here tend to have not even have heard of lua before discovering ComputerCraft.
Jajnick #14
Posted 03 September 2012 - 08:16 PM
Everybody who have checked lua-users wiki knows that…
The people here tend to have not even have heard of lua before discovering ComputerCraft.
That's interesting! I was exactly in the same situation, and the first thing I've done was checking Google, keyword: lua tutorial. First result? Lua-users.
Have those people not heard of Google? I don't think so.

There is no excuse for human thoughtlessness. Understand it.
Cloudy #15
Posted 03 September 2012 - 08:29 PM
He's just sharing his knowledge. Sure it can be found elsewhere, but I don't see the harm in it being here.
Kingdaro #16
Posted 04 September 2012 - 12:55 AM
That's interesting! I was exactly in the same situation, and the first thing I've done was checking Google, keyword: lua tutorial. First result? Lua-users.
Have those people not heard of Google? I don't think so.

There is no excuse for human thoughtlessness. Understand it.
I'm basing my knowledge of others' knowledge through looking at the programs people have displayed, and as far as I've seen the examples I've given have been scarcely used.

He's just sharing his knowledge. Sure it can be found elsewhere, but I don't see the harm in it being here.
Exactly this.
NIN3 #17
Posted 04 September 2012 - 02:45 AM
You sir, deserve a one up. Thank you for making my scripts just a wee bit neater!
FuzzyPurp #18
Posted 06 September 2012 - 09:02 AM
You sir, deserve a one up. Thank you for making my scripts just a wee bit neater!

Then click the + button at the bottom right of his initial Post. 9 people have so far it seems.
Kingdaro #19
Posted 08 September 2012 - 05:00 PM
Still adding on as I learn more wonderful things about lua's flexibility.

Thanks for the rep, guys. :D/>/>
sjele #20
Posted 08 September 2012 - 08:53 PM
This is nice :D/>/>

"Scrolling up to hit +1"
hego555 #21
Posted 11 September 2012 - 07:34 AM
Are tables the equivalent of arrays?
GopherAtl #22
Posted 11 September 2012 - 08:09 AM
technically they are hashmaps, but they can act like arrays, like so…

--initialize a table like an array; lua will assign numeric keys starting with 1
local myArray={"cherries", "apples", "potatoes", "grapes", }

--use the # prefix operator to get the size of the array
print("myArray has "..#myArray.." items.")
--[[outputs :
myArray has 4 items
--]]

--you can use this to iterate over the array
function printArray(array)
  for i=1,#myArray do
	print(i..": "..myArray[i])
  end
end

printArray(myArray)
--[[outputs
1: cherries
2: apples
3: grapes
--]]

--they're still really hashmaps, so you can add arbitrary members too...
myArray.foo="bar"

--...but those non-numbered members are ignored when treating it like an array
print("myArray STILL has "..#myArray.." items.")
--outputs: "myArray STILL has 4 items")

--the table api, native to lua, provides some methods to help as well. You can remove items from the array..
table.remove(myArray,3)

printArray(myArray)
--[[ outputs
1: cherries
2: apples
3: grapes
--]]

--...or insert items

table.insert(myArray,2,"bananas")

printArray(myArray)
--[[ outputs
1: cherries
2: bananas
3: apples
4: grapes
--]]

--you can even sort them!
table.sort(myArray)
printArray(myArray)
--[[outputs:
1: apples
2: bananas
3: cherries
4: grapes
--]]

--though note, for this version of sort to work without errors, your array must contain all strings or all numbers.
table.insert(myArray,4,9001)
--calling table.sort(myArray) now would throw an error "attempt to compare string with number"

--however, mixed arrays, or even arrays of tables, can be sorted with a custom compare function!
function compareMixed(a,:D/>/>
  --convert both to strings first, then compare
  return tostring(a)<tostring(:)/>/>
end


--pass the function (WITHOUT ()s, just the name, like a variable!) to sort as 2nd parameter
table.sort(myArray,compareMixed)

printArray(myArray)
--[[ outputs:
1: 9001
2: apples
3: bananas
4: cherries
5: grapes
--]]

hope this helps :D/>/>
ElectricOverride #23
Posted 11 September 2012 - 04:50 PM
Lua Tricks
Note that this is not a lua tutorial and is intended for intermediate to advanced lua users.

I've been looking at some peoples' programs around here and I notice that a lot of them aren't using some neat tricks that may or may not make coding easier/neater.

Strings
The first thing is that a lot of people use double quotes instead of single quotes, but I think that's just a preference in most cases. In case you still aren't aware:

string1 = ''
string2 = ""

Both of these string variables are valid. I should probably mention that you should be careful with using single quotes, because there are common words with apostrophes such as "don't" and "you're". To prevent errors, you should put backslashes before single quotes inside single quotes:

string1 = 'don't'

An alternate solution is to use double quotes in this case.

There's also this neat trick that I didn't learn until a little further down the line in my use of lua - multiline strings. Defined by [[ ]], much like ' ' and " ".

The following example is valid:

print [[This is
a multiline
string.]]

Be wary that using multiline strings with tabs before text will actually output the tabs as well.

print [[This string
	  will take the tabbing
	  with it.]]

Multiline strings are useful in printing menus for your console. Instead of doing this:

print '------------------------------'
print 'Screen Title'
print '------------------------------'
print 'Option'
print 'Option'
print 'Option'

you can do:

print [[
------------------------------
Screen Title
------------------------------
Option
Option
Option
]]

A similar helpful trick to multiline strings can be done with comments, starting with –[[ and ending with ]]:

--[[
This is a multiline comment.
It does nothing :P/>/>
]]

I learned this very recently from an amazing youtuber named NitroFingers. You can use the # operator on strings to get the length of a string, much like string.len().

myString = 'waffles'

print(string.len(myString))
--> 7
print(#myString)
--> 7

I haven't tested this with all string methods, but in most cases, you'll usually be able to perform the method on the actual string instead of calling the method with the string as an argument.

local s = 'waffles'
s:sub(1,3)
--> waf
s:match 'f.-s'
--> fles

for i,v in s:gmatch '.' do
  print(v)
end
--> w
--> a
--> f
--> f
--> l
--> e
--> s

Functions
Function calls are also flexible in other ways. In the case that a function only takes one argument and it is a string, you don't have to supply parentheses.

print('a string')
print 'a string'
Both of these examples will print a string.

Unless you're getting deep into some OOP stuff, this won't be very helpful to you in a lot of cases. If a function takes a table argument, you can drop the parentheses, like strings.

myFunction {
a = 1;
b = 2;
}

There's also an alternate way of defining functions.

myFunct = function()
-- stuff
end

For me, this is useful for defining functions without having to go outside your table:

myTable = {
funct1 = function()
  print 'stuff'
end;

funct2 = function()
  print 'more stuff'
end;
}

myTable.funct1()
myTable.funct2()

Functions are able to be defined with a variable number of arguments:

function stuff(...)

end

The three dots represent the table of arguments given and can me accessed in the function as "arg".

function stuff(...)
for i,v in pairs(arg) do
  write(v)
end
end

stuff(1,2,3,'a','b','c')
--> 123abc6

Woah wait, where'd that 6 come from? That's just the number of arguments given. It's indexed in the "arg" table as n.

print(arg.n)
--> 6

However you'll still get the number of arguments given if you use the pound sign operator, as it only counts values given as indexes.

print(#arg)
--> 6

At the end of the day, it's all a matter of preference. To make things less complicated, you can simply store your arguments in a local table, as shown by some computercraft base programs.

tArgs = { ... }

Tables
When defining indexes in tables, you can actually use semicolons instead of commas. For me, this is easier because I can rearrange elements of the table without having to redo my commas.

myTable = {
index1 = "foobar";
index2 = 1337;
}

In case you didn't know this either, you can also use strings to access indexes not defined by strings. This would be helpful for storing functions for possible user input in tables.

local answers = {
yes = 'You're cool!';
no = 'Fine, be that way.';
}

print 'Do you like pineapples?'
input = read() -- this is accepted as a string.
if answers[input] ~= nil then
print(answers[input])
else
print 'I don't understand you, sorry.'
end

Numbers
There isn't much to learn in this category, however I think you'll eventually find it useful that you're actually able to concatenate numbers like strings.

print(3 .. 3)
--> 33
There need to be spaces in between the dots and the numbers, otherwise lua will confuse your dots as decimals and throw an error.

Some of you coming from a different programming background may have already known this, but the function math.fmod() can be replaced with the operator %. If you don't know what this does, it returns the remainder of the division of two numbers. e.g. 7 % 3 would equal 1.

You could use this to tell if a number is even - if the remainder of the number and 2 is 0.

while true do
  textutils.slowPrint 'Type in a number.'
  write '> '
  local input = read()
  local num = tonumber(input)
  if num then --checking if the input was successfully converted into a number
	if num % 2 == 0 then
	  textutils.slowPrint 'Your number is even.'
	else
	  textutils.slowPrint 'Your number is odd.'
	end
  elseif input == 'exit' then --give the ability to leave the program.
	textutils.slowPrint 'Thank you for using my useless program! :D/>/>'
	sleep(1)
	break
  else
	textutils.slowPrint "That's not a valid number!"
  end

  sleep(1)
end

Booleans
In the bools field, there's actually a cute little statement that is way easier than if … elseif … end:

var = condition and set_if_true or set_if_false
Five unnecessary lines condensed into one. Here's an example:


var = 5 > 3 and 'yes!' or 'aw'
In this case, var would be 'yes!' because 5 > 3. If on some distant alien planet in a parallel universe 5 were less than three, var would be 'aw'.

If you wanted to have a bit of fun, you can make yourself a clamp function by nesting these.

local function clamp(low,n,high)
return n < low and low or (n > high and high or n)
end

There's also a simple way of reversing a boolean:

bool = not bool

In a more applicable example, you could make a sort of toggling light switch program with one line (assuming your output is in the back and hooked up to your lights)

rs.setOutput('back', not rs.getOutput('back'))

Oh, and remember that % example? This:

	if num % 2 == 0 then
	  textutils.slowPrint 'Your number is even.'
	else
	  textutils.slowPrint 'Your number is odd.'
	end

Could be this:

	textutils.slowPrint('Your number is ' .. (num % 2 == 0 and 'even.' or 'odd.'))
Brackets needed because it isn't just one string anymore XD



You could've lived your life without knowing any of this, really. However I thought it'd be helpful to some of you people, a couple of interesting tidbits to know. Pretty sure I'm missing some stuff but I'm not necessarily a book of infinite useless lua knowledge :)/>/>
Thanks for the tips. :)/>/>
hego555 #24
Posted 11 September 2012 - 11:30 PM
Dont quote the original post… its just a waste of space!
Kingdaro #25
Posted 12 September 2012 - 12:45 AM
I understand you're probably new here, but please, please don't ever quote entire first posts.
sjele #26
Posted 13 September 2012 - 04:12 PM
Or use spoilers as that takes it down to only 2 lines, not so much spam
lfairy #27
Posted 16 September 2012 - 06:42 AM
Kingdaro, how about including the difference between pairs and ipairs?

The pairs function lets you loop over all the things in a table. For example:


local meanings = {
    python = 'a British comedy show',
    lua = 'the moon',
    java = 'a type of coffee',
    haskell = 'a famous computer scientist',
    c = 'the third letter of the alphabet',
    }

for key, value in pairs(meanings) do
    print('The language ' .. key .. ' is named after ' .. value)
end

But if you're going over a sequence (i.e. where the keys are numbers), pairs won't guarantee you'll get the values in order. Also, as you've shown with your vararg example, it often ends up returning extra values such as the length of the sequence.

To solve that issue, Lua has an alternative function called ipairs:


local languages = { 'python', 'lua', 'java', 'haskell', 'c' }
for index, value in ipairs(languages) do
    print('At index ' .. index .. ' we have ' .. value)
end
Kingdaro #28
Posted 18 September 2012 - 12:54 AM
It's a good suggestion, though the subject of this topic is more oriented towards syntax abuse than anything.

I might include it in the future.
Cloudy #29
Posted 18 September 2012 - 07:53 AM
It is worth pointing out that ipairs is deprecated and removed as of 5.2 - it is quicker to do this:

for i = 1, #tableTest do
  print("k: ", i, " v: ", tableTest[i])
end
Kingdaro #30
Posted 19 September 2012 - 04:22 AM
Both pairs and ipairs are in 5.2, and I find using both of them easier than a numeric for statement. That way you don't have to index the table on every iteration and you have the value readily available.

for i=1, #aTable do
  print(i, aTable[i]) --longer
end

for i,v in pairs(aTable) do
  print(i, v) --shorter
end

Even though in this example it's just one statement, coding would be faster and easier in the longrun using "v" instead of "aTable".
Kingdaro #31
Posted 24 October 2012 - 04:26 AM
Learned something pretty neat a while ago. Added it under "Functions" (before Tables).
KaoS #32
Posted 24 October 2012 - 07:39 AM
hey man, why not include metatables in that, they are the most amazing tool I have yet seen in CC, another thing is that you can do the same with tables

({1,2,3,4,5})[2]==2

this seems pretty useless but it can be used to get a specific parameter from a function. for example if you only want the message from rednet but not the id we typically use


local _,message=rednet.receive()

but we could also use


local message=({rednet.receive()})[2]
faubiguy #33
Posted 24 October 2012 - 09:02 AM
hey man, why not include metatables in that, they are the most amazing tool I have yet seen in CC, another thing is that you can do the same with tables

({1,2,3,4,5})[2]==2

this seems pretty useless but it can be used to get a specific parameter from a function. for example if you only want the message from rednet but not the id we typically use


local _,message=rednet.receive()

but we could also use


local message=({rednet.receive()})[2]

I usually use select to do that
local message = select(2, rednet.receive())
KaoS #34
Posted 24 October 2012 - 09:37 AM
that gives the second returned item and all items after it rather than just 1 but it makes little/no difference really. to be honest I did not know that function existed, good call there. I guess my example is pretty useless ^_^/>/> anyways it is another thing to know about lua, always good to gather info
Kingdaro #35
Posted 24 October 2012 - 11:48 AM
hey man, why not include metatables in that, they are the most amazing tool I have yet seen in CC, another thing is that you can do the same with tables

({1,2,3,4,5})[2]==2

this seems pretty useless but it can be used to get a specific parameter from a function. for example if you only want the message from rednet but not the id we typically use


local _,message=rednet.receive()

but we could also use


local message=({rednet.receive()})[2]
I forgot completely about metatables, haha.

I could tell people about the option to throw your function calls into a table and get specific arguments out of them, too. I myself don't prefer to do this but some would definitely benefit from knowing it.
KaoS #36
Posted 24 October 2012 - 12:02 PM
awesome stuff, in case it helps here is a link to the most helpful tutorial I have ever seen on metatables
bbqroast #37
Posted 28 October 2012 - 02:03 AM
I think there is a good reason for using double quotes as opposed to single quotes. In other languages like Java double quotes signify a string and single quotes signify a char.
So
"Hello World!" Is fine in most languages but 'Hello World!' can cause issues in some.
Kingdaro #38
Posted 28 October 2012 - 06:30 AM
I think there is a good reason for using double quotes as opposed to single quotes. In other languages like Java double quotes signify a string and single quotes signify a char.
So
"Hello World!" Is fine in most languages but 'Hello World!' can cause issues in some.
But that's Java, it isn't Lua.

The only difference between double and single quotes is that double quotes can have single quotes inside, and vice versa.
 "It's useful if you want to have a statement with an apostrophe,"
'or if you want to "emphasize" something within single quotes.'
bbqroast #39
Posted 30 October 2012 - 08:59 PM
I think there is a good reason for using double quotes as opposed to single quotes. In other languages like Java double quotes signify a string and single quotes signify a char.
So
"Hello World!" Is fine in most languages but 'Hello World!' can cause issues in some.
But that's Java, it isn't Lua.

The only difference between double and single quotes is that double quotes can have single quotes inside, and vice versa.
 "It's useful if you want to have a statement with an apostrophe,"
'or if you want to "emphasize" something within single quotes.'
I believe some other languages do it as well. For that reason, it's still good practice to do it. It just makes it that much easier to change language, or try something new.

Also if you're doing a tutorial you need to remember that the reader may wish to move onto languages (LUA is a nice starting language) and it's bad to teach them thigns that will come back to bite them later.
ChunLing #40
Posted 31 October 2012 - 08:35 AM
Either is fine. I use double quotes unless I have a specific reason not to use them, in which case I'll usually go straight to the double brackets rather than muck about with single quotes. But as long as you're consistent about how you use them, string delineation is string delineation. It makes it a little easier to read your code if you write it the way you're used to reading it. Otherwise…not a big deal.
Viproz #41
Posted 25 November 2012 - 11:06 PM
Thank you for this thread !
Kingdaro #42
Posted 27 March 2013 - 07:41 AM
Updated.

Added a section on metatables, trapping arguments in tables, and iterators.
theoriginalbit #43
Posted 27 March 2013 - 09:46 AM
rename the 'Boolean' section to 'The Ternary Operator'
Bubba #44
Posted 27 March 2013 - 09:53 AM
rename the 'Boolean' section to 'The Ternary Operator'

It doesn't only have to do with ternary operations though. Although specifying that the shortened if statement is a ternary operator would probably be good.
superaxander #45
Posted 28 March 2013 - 03:00 AM
Another trick if you want double brackets in double brackets you can do.

[=[
This is [[cool]]
]=]
You can add up the ='s like this

[==[
[=[hey]=][hey]
]==]
Etc.

Cheers Alexander

EDIT: This could also fix a bug in lightshot with some brackets in the edit program not showing
theoriginalbit #46
Posted 28 March 2013 - 03:02 AM
Another trick if you want double brackets in double brackets you can do.

[=[
This is [[cool]]
]=]
You can add up the ='s like this

[==[
[=[hey]=][hey]
]==]
Etc.

Cheers Alexander

EDIT: This could also fix a bug in lightshot with some brackets in the edit program not showing
Hmmm seems someone has been reading this chapter of the PIL… :P/>

PIL extract
Spoiler
Literal strings can also be defined using a long format enclosed by long brackets. We define an opening long bracket of level n as an opening square bracket followed by n equal signs followed by another opening square bracket. So, an opening long bracket of level 0 is written as [[, an opening long bracket of level 1 is written as [=[, and so on. A closing long bracket is defined similarly; for instance, a closing long bracket of level 4 is written as ]====]. A long string starts with an opening long bracket of any level and ends at the first closing long bracket of the same level. Literals in this bracketed form can run for several lines, do not interpret any escape sequences, and ignore long brackets of any other level. They can contain anything except a closing bracket of the proper level.

For convenience, when the opening long bracket is immediately followed by a newline, the newline is not included in the string. As an example, in a system using ASCII (in which 'a' is coded as 97, newline is coded as 10, and '1' is coded as 49), the five literal strings below denote the same string:


a = 'alo\n123"'
a = "alo\n123\""
a = '\97lo\10\04923"'
a = [[alo
123"]]
a = [==[
alo
123"]==]
superaxander #47
Posted 28 March 2013 - 03:04 AM
Another trick if you want double brackets in double brackets you can do.

[=[
This is [[cool]]
]=]
You can add up the ='s like this

[==[
[=[hey]=][hey]
]==]
Etc.

Cheers Alexander

EDIT: This could also fix a bug in lightshot with some brackets in the edit program not showing
Hmmm seems someone has been reading this chapter of the PIL… :P/>/>

PIL extract
Spoiler
Literal strings can also be defined using a long format enclosed by long brackets. We define an opening long bracket of level n as an opening square bracket followed by n equal signs followed by another opening square bracket. So, an opening long bracket of level 0 is written as [[, an opening long bracket of level 1 is written as [=[, and so on. A closing long bracket is defined similarly; for instance, a closing long bracket of level 4 is written as ]====]. A long string starts with an opening long bracket of any level and ends at the first closing long bracket of the same level. Literals in this bracketed form can run for several lines, do not interpret any escape sequences, and ignore long brackets of any other level. They can contain anything except a closing bracket of the proper level.

For convenience, when the opening long bracket is immediately followed by a newline, the newline is not included in the string. As an example, in a system using ASCII (in which 'a' is coded as 97, newline is coded as 10, and '1' is coded as 49), the five literal strings below denote the same string:


a = 'alo\n123"'
a = "alo\n123\""
a = '\97lo\10\04923"'
a = [[alo
123"]]
a = [==[
alo
123"]==]
Nope I found it on lua users under hidden features.
KaoS #48
Posted 28 March 2013 - 03:05 AM
nice, I didn't know that. that way I can get sublime text to highlight correctly and not get confused when I use a table indexing to index a table (like mytable[mytable[max]])
theoriginalbit #49
Posted 28 March 2013 - 03:07 AM
mine doesn't get confused with that… o.O
KaoS #50
Posted 28 March 2013 - 03:09 AM
it ends the string highlight or something. I can't quite remember the issue I was having but I remember it being highly irritating

EDIT: I think I'll move this to PM… no thread hijacking
theoriginalbit #51
Posted 28 March 2013 - 03:12 AM
got the latest version of Grav's pack?
Kilobyte #52
Posted 08 April 2013 - 12:29 PM
Very good one. One note though: don't use slowPrint() itjust slows down the code. also by you using it here you encurage the noobs to use it. And thats not what is good. basicly slowPrint (and slowWrite) are functions which should just be removed.
Bubba #53
Posted 08 April 2013 - 12:40 PM
Very good one. One note though: don't use slowPrint() itjust slows down the code. also by you using it here you encurage the noobs to use it. And thats not what is good. basicly slowPrint (and slowWrite) are functions which should just be removed.

slowPrint() has a purpose and I would not advocate removing it. Occasionally people do have a valid reason to use it and taking it out would be pointless. Of course, noobs have a tendency of using it far too much which can be annoying, but if they want to make their code slow then that is up to them.

Valid use of it:

local menu = [[- File Edit Update Config About]]
term.setBackgroundColor(colors.white)
term.clear()
term.setBackgroundColor(colors.red)
term.setCursorPos(1,1)
term.clearLine()
local display = false

while true do
  term.setBackgroundColor(colors.red)
  term.setCursorPos(1,1)
  term.clearLine()
  if display then
    term.setBackgroundColor(colors.blue)
  end
  textutils.slowPrint(display and menu or "+")
  local e = {os.pullEvent()}
  if e[1] == "mouse_click" and e[3] == 1 and e[4] == 1 then
    display = not display
  elseif e[1] == "key" and e[2] == keys.enter then
    return
  end
end

Admittedly, the above code is not a very good example of slowPrint. But it does show that it has a purpose making interesting text effects.
Pharap #54
Posted 04 May 2013 - 01:23 PM
The len operator ( this thing # ) can be applied to tables to count all their numerical indexes, not just the arg table.
You should also be able to override it with metatables in most implementations of lua, though I'm not certain if the CC implementation supports this.
theoriginalbit #55
Posted 04 May 2013 - 01:53 PM
You should also be able to override it with metatables in most implementations of lua, though I'm not certain if the CC implementation supports this.
I don't think it does…. everytime I've overrode the __len metamethod it doesn't actually change anything.
Kingdaro #56
Posted 04 May 2013 - 02:01 PM
The len operator ( this thing # ) can be applied to tables to count all their numerical indexes, not just the arg table.
I thought that was pretty obvious, but alright.
theoriginalbit #57
Posted 04 May 2013 - 02:08 PM
I thought that was pretty obvious, but alright.
I think the key thing stated there is "all their numerical indexes" … so a table like so

local t = { 1, "hello", k = "world" }
would return 2 with both # and table.getn
Kingdaro #58
Posted 04 May 2013 - 02:16 PM
I alerady knew that; I don't get what you guys are saying.
theoriginalbit #59
Posted 04 May 2013 - 02:18 PM
I alerady knew that; I don't get what you guys are saying.
I didn't realise that this was just for you to learn new stuff.
Kingdaro #60
Posted 04 May 2013 - 02:33 PM
I was confused because I thought you both were talking to me, haha.
Pharap #61
Posted 04 May 2013 - 04:37 PM
The len operator ( this thing # ) can be applied to tables to count all their numerical indexes, not just the arg table.
I thought that was pretty obvious, but alright.
Well, if you have to explicitly mention it for string it would be unfair to not explicitly mention its functioning on tables since it is actually classified as an operator.

I thought that was pretty obvious, but alright.
I think the key thing stated there is "all their numerical indexes" … so a table like so

local t = { 1, "hello", k = "world" }
would return 2 with both # and table.getn

It's table.maxn, but you're correct in the fact that they are pretty much the same functions, one is just easier to use.
theoriginalbit #62
Posted 04 May 2013 - 11:33 PM
It's table.maxn
Actually its both… I aways forget about maxn so never say about it…
Pharap #63
Posted 05 May 2013 - 07:53 AM
It's table.maxn
Actually its both… I aways forget about maxn so never say about it…
Weird, seems kind of redundant.
I'm guessing getn is CC-specific since the official lua docs have it listed as maxn, no mention of getn.
theoriginalbit #64
Posted 05 May 2013 - 07:58 AM
Weird, seems kind of redundant.
I'm guessing getn is CC-specific since the official lua docs have it listed as maxn, no mention of getn.
Yeh I guess so… talking about redundancies I think having
os.getComputerLabel()
and
os.computerLabel()
and the same with id.
both fairly redundant, but they are still there, so meh.
Pharap #65
Posted 05 May 2013 - 10:27 AM
Weird, seems kind of redundant.
I'm guessing getn is CC-specific since the official lua docs have it listed as maxn, no mention of getn.
Yeh I guess so… talking about redundancies I think having
os.getComputerLabel()
and
os.computerLabel()
and the same with id.
both fairly redundant, but they are still there, so meh.
Both of those are CC-specific functions, but yeah, there are a lot of redundancies lying around here and there.
Majd Alfhaily #66
Posted 20 May 2013 - 11:10 AM
Really nice information, knew some of them but most of them I didn't so it's really cool. Thanks!
theoriginalbit #67
Posted 24 May 2013 - 09:38 PM
It's table.maxn, but you're correct in the fact that they are pretty much the same functions, one is just easier to use.
I actually just noticed in the Lua 5.2 docs that table.maxn is deprecated.
Upgrade_Windows #68
Posted 26 May 2013 - 01:30 PM
I know about most of these but for the ones I didn't know. wow. Thanks for the Lua tricks. :)/>
Robotonic #69
Posted 26 May 2013 - 05:29 PM
These were actually very useful tips, thanks, this makes it a whole lot easier.
JustPingo #70
Posted 26 May 2013 - 05:42 PM
I have already learnt all of this, but it's useful for people who don't know. Nice topic man.

I have already learnt all of this, but it's useful for people who don't know. Nice topic man.
MaHuJa #71
Posted 10 June 2013 - 11:08 AM
Also, when defining indices in tables, you can actually use semicolons instead of commas. For me, this is easier because I can rearrange elements of the table without having to redo my commas.
myTable = {
index1 = "foobar";
index2 = 1337;
}
Actually, t = { a, b, } is also valid, even without anything after the last comma. The explicit reason was to make it easier to make code that generates (lua coded) tables. Neither option applies to most languages.

var = condition and set_if_true or set_if_false
If the valid values for set_if_true include nil or false, it will not work as intended.


textutils.slowPrint('Your number is ' .. (num % 2 == 0 and 'even.' or 'odd.'))
It might surprise you to learn that this method is, while more compact in source code, worse performing than the previous version. (Performance being in server lag terms.)
The reason: string concatenations - or alterations in general - are expensive.

On that note, here's an important piece you've missed.
If you'd use
str = "H" .. "e" .. "l" .. "l" .. "o".. " ".. "W".. "o".. "r".. "l".. "d".. "!"
typically in the form of a loop that's accumulating output in a string, like this
text = ""
for i = 1,10 do 
  text = text .. ' ' .. i 
end
you should instead be doing
table.concat { 'H','e','l','l','o',' ','W','o','r','l','d', }
or
t = {}
for i = 1,10 do
  table.insert(t,i)
end
text = table.concat(t,' ') -- letting concat do the separator
if you don't want to kill the server.

If table.insert is verbose, t[#t+1]= should also be viable, I think.
(The above can also be done by metatable to make it transparent, fwiw.)
theoriginalbit #72
Posted 10 June 2013 - 06:13 PM
On that note, here's an important piece you've missed.
If you'd use
str = "H" .. "e" .. "l" .. "l" .. "o".. " ".. "W".. "o".. "r".. "l".. "d".. "!"
typically in the form of a loop that's accumulating output in a string, like this
text = ""
for i = 1,10 do
  text = text .. ' ' .. i
end
you should instead be doing
table.concat { 'H','e','l','l','o',' ','W','o','r','l','d', }
How exactly do you think table.concat works? you're comment above this one is saying about being more efficient and having good performance, yet here you insist on adding another function to the stack, incurring the overhead of the call, to just do the exact same thing as the loop… using table.concat does not add anything better than the loop…

If table.insert is verbose, t[#t+1]= should also be viable, I think.
Also with this once, doing the following

local t = {}
for i = 1, 10 do
  t[#t + 1] = i
end
is far more efficient and has less overhead than

local t = {}
for i = 1, 10 do
  table.insert(t, i)
end
Lyqyd #73
Posted 10 June 2013 - 08:19 PM
table.concat is likely to be implemented java-side, though. There will certainly be some speed gains there.
Kingdaro #74
Posted 11 June 2013 - 02:09 AM
Also, when defining indices in tables, you can actually use semicolons instead of commas. For me, this is easier because I can rearrange elements of the table without having to redo my commas.
myTable = {
index1 = "foobar";
index2 = 1337;
}
Actually, t = { a, b, } is also valid, even without anything after the last comma. The explicit reason was to make it easier to make code that generates (lua coded) tables. Neither option applies to most languages.
Good point. I'll leave the bit in, though, because semicolons do look better (in my opinion) than unorderly trailing commas.

var = condition and set_if_true or set_if_false
If the valid values for set_if_true include nil or false, it will not work as intended.
Also a good point. I might just redo that section altogether, because the "and" and "or" keywords expand to many more possibilities than just the ternary statement.

textutils.slowPrint('Your number is ' .. (num % 2 == 0 and 'even.' or 'odd.'))
It might surprise you to learn that this method is, while more compact in source code, worse performing than the previous version. (Performance being in server lag terms.)
The reason: string concatenations - or alterations in general - are expensive.
The performance difference is not noticeable or relevant to this topic.

On that note, here's an important piece you've missed.

If you'd use
str = "H" .. "e" .. "l" .. "l" .. "o".. " ".. "W".. "o".. "r".. "l".. "d".. "!"
typically in the form of a loop that's accumulating output in a string, like this
text = ""
for i = 1,10 do 
  text = text .. ' ' .. i 
end
you should instead be doing
table.concat { 'H','e','l','l','o',' ','W','o','r','l','d', }
or
t = {}
for i = 1,10 do
  table.insert(t,i)
end
text = table.concat(t,' ') -- letting concat do the separator
if you don't want to kill the server.

If table.insert is verbose, t[#t+1]= should also be viable, I think.
(The above can also be done by metatable to make it transparent, fwiw.)
Reiterating on my previously stated point, my focus in this topic is on the use and abuse of syntactic sugar, and not speed and performance. Even then, your example is only ten operations, and any significantly greater number is going to take a long time regardless of whichever method you're using.

Concatenation methods would not "kill the server".
MaHuJa #75
Posted 17 June 2013 - 08:13 PM
I hear you about the syntactic sugar - but the thread title was "lua tricks". And as "tricks" go, this one's big. Thus I believe bringing this up was appropriate. Of course, it's still up to you if you'll exclude it from the scope of the opening post, but at least I want to get the facts underlying that decision correct.

(FTR: I agree that the single concatenation as I first pointed out is irrelevant. Perhaps outside redpower computers, if one stripped down lua enough to run on it.)

The native lua implementation will allocate a new string each time you do a concatenation. Especially if you're concatenating lots of strings, or with the first ones being long, that means it'll be traversing over quite large amounts of memory, doing a full copy of all the accumulated content for each additional concatenation. This is explicitly not the case when using table.concat. Java has the same thing in the form of String vs StringBuilder, so I believe the LuaJ implementation merely "forwards" it, getting the same effect. (It would take a lot of effort to get it better, to handle a case which the language spec say's "don't do" about.)

any significantly greater number is going to take a long time regardless of whichever method you're using
The big clue is the difference between what numbers it takes to take a particular performance hit.

(Computer science incoming)
If we bump the number up to 1000 (1 character each string), then table.concat will be a total of 1000 chars allocated once, and written. (Excluding the "originals", that are created in any case.)
String concatenation becomes around 500000 allocated over several parts (so + allocation overhead) and written.
That's 499k more bytes moved around for the same end result, plus the 998 extra allocations (with their overhead) that needs to be done. It's damning even before cache misses become a topic.

Scale it up to 10000 and it's become a "significantly higher number" in your statement - but only for the string concatenation. So the correct version would be "any significantly greater number is going to take a long time depending on which method you're using"
Disclaimers/Known errors:
- Math done while tired. I think I got it at least approximate.
- Real operation would probably not be appending a single byte at a time, and possibly not all of them at once.
(though string concat would be worse off if there wasn't a corresponding reduction in operations count.)
- This doesn't account for the filling of the table which gets passed to table.concat, but that won't change it significantly.
- No accounting for separators in the string concatenation example.

(Did I miss anything? Please let me know.)


I have a habit of thinking in scale. Given a large server with enough users running multiple computers doing this, this difference has the potential to bring tick times up to unplayable levels ("killing the server") quickly. Assuming the CC memory limit guard picks up the unreferenced strings, or the JRE in use does emergency GC, it won't actually crash from an out of memory error any faster.


t[#t + 1] = i is far more efficient and has less overhead than table.insert(t, i)
You're probably right; the docs say table.insert position parameter defaults to #t+1.
Though I doubt the "far" part. Of course, if you have measured to find that, then I'll bow to that.
# will look up the metatable __len, and if it exists, call it. If there is no metatable or no __len, it may still be a function call or two itself. Coupled with whatever else you're doing in that loop, it's probably not going to be measurable, let alone significant. So in the end, that should be up to what the programmer finds the most readable. With a few lines of code already run, t:insert(i) or t:ins(i) are also options.



It is possible, btw, to make a "stringbuilder" type that will basically do the table.concat implicitly even if you're doing .. style concatenation. And otherwise transparently act like a string. You'd just have to invoke it explicitly when you begin. Lua is very flexible.



Thanks for making me think about what I've said.
Tripy998 #76
Posted 21 June 2013 - 01:58 PM
This is so helpfull!
Tripy998 #77
Posted 21 June 2013 - 01:59 PM
These were actually very useful tips, thanks, this makes it a whole lot easier.
Off-Topic: I like your picture!
On-Topic: I agree
FNCPro #78
Posted 23 June 2013 - 03:07 PM
These lua tricks are very helpful!
RoD #79
Posted 25 June 2013 - 01:22 PM
Aweomse this is really usefull :D/> thanks
Larandar #80
Posted 27 June 2013 - 01:10 AM
Nice topic,
Thanks
lordwh #81
Posted 29 June 2013 - 05:27 AM
I am only a beginner and stopped at the middle. It got too intense. And also, NitrogenFingers is my fav Programmer/Youtuber too! He got me back into computer craft, because I liked the RPG engine he made.
Encreedem #82
Posted 02 July 2013 - 05:49 AM
I didn't know that these boolean functions exist in LUA too. Thanks, it will definitely help me with my code!
Cutecurtain #83
Posted 19 July 2013 - 08:35 AM
I learned quite a few more tricks here! ;)/> Thanks! B)/>
CyborgAlex #84
Posted 30 July 2013 - 11:37 AM
Some of then i knew,but some realy helped me.Thanks
TimTheRedstoner #85
Posted 31 July 2013 - 10:03 PM
very nice will give a +1
NEOparmen #86
Posted 09 August 2013 - 05:40 AM
Very good!
Lux #87
Posted 27 August 2013 - 11:36 PM
Oh god! Thanks for post it! I'm learning LUA and this is very useful! Thanks a lot!
kreezxil #88
Posted 29 August 2013 - 07:58 PM
I'm bookmarking this thread as I will be coming back to it often to optimize and enhance my code as the case may be. Btw, +1'd you! :)/>
jay5476 #89
Posted 03 September 2013 - 04:28 AM
you could add that
[=[ can have --[[comments]] in it ]=]
I just found this out :)/>
ChemE #90
Posted 20 November 2013 - 10:47 AM
Great collection of tips! I always enjoy these when learning a new programming language. Much appreciated!
_gjkf_ #91
Posted 24 November 2013 - 11:14 AM
great job!!!
thanks a lot!!!
Kingdaro #92
Posted 24 November 2013 - 02:34 PM
Thanks for the comments, everyone! I've gotten around to rewriting the entire post (since there were some misinformations, bad wordings some important left-out information here and there), and you can access it here: http://kingdaro.gith...lua-tricks.html

Also, it'd be great if someone could inform me on how to have Lua syntax highlighting on here.
Edited on 24 November 2013 - 01:34 PM
awsmazinggenius #93
Posted 04 December 2013 - 10:42 PM
I couldn't have ever known we can print "Hi" like in old versions (2.7) of Python! As well, I thought you needed to use ]]– to end a multi-line comment. Thanks and +1 to you!

I think you use ```lua to get a Lua syntax highlight. Confirming now, will edit when confirmed.

Edit: Yep, you use ```lua , like this:

```lua
print("Lua syntax highlighting!!!")
-- Better lua code then that
```
Some GitHub Tricks You Probably Knew :)/>/&amp;gt;/&amp;amp;gt;

EDIT: The editor derped up my post :)/>
Edited on 04 December 2013 - 09:48 PM
distantcam #94
Posted 04 December 2013 - 11:23 PM
–snip
indicing tables using strings is actually a little slower than using the dot syntax…
-snip

Does anyone know why this is?
Edited on 04 December 2013 - 10:25 PM
theoriginalbit #95
Posted 05 December 2013 - 01:46 AM
Does anyone know why this is?
I believe its to do with the way LuaJ implements it.

EDIT: Sorry I should expand on that a bit.
LuaJ maintains 3 immutable arrays for a single table.
  1. an array to store indexed values
  2. an array to store hash keys
  3. an array to store hash values
This means that when getting an element from a table the simple array lookup is quicker than the hash lookup (especially since they don't actually use a HashMap, and use two separate arrays).

It is also slightly slower to add a key/value pair as opposed to an indexed value due to the fact that there are two arrays that need to be modified; hashKey and hashValue arrays. Otherwise it is still fairly expensive to add elements to a table due to the fact that they must create a new array each time, since arrays in Java are immutable. I do think that they really should implement it as mutable data structures, for example an ArrayList and HashMap.

If you're interested in seeing most of the relevant implementation

public LuaValue get(int paramInt)
  {
	LuaValue localLuaValue = rawget(paramInt);
	return (localLuaValue.isnil()) &amp;&amp; (this.m_metatable != null) ? gettable(this, valueOf(paramInt)) : localLuaValue;
  }

  public LuaValue get(LuaValue paramLuaValue)
  {
	LuaValue localLuaValue = rawget(paramLuaValue);
	return (localLuaValue.isnil()) &amp;&amp; (this.m_metatable != null) ? gettable(this, paramLuaValue) : localLuaValue;
  }

  public LuaValue rawget(int paramInt)
  {
	if ((paramInt > 0) &amp;&amp; (paramInt <= this.array.length))
	  return this.array[(paramInt - 1)] != null ? this.array[(paramInt - 1)] : NIL;
	return hashget(LuaInteger.valueOf(paramInt));
  }

  public LuaValue rawget(LuaValue paramLuaValue)
  {
	if (paramLuaValue.isinttype())
	{
	  int i = paramLuaValue.toint();
	  if ((i > 0) &amp;&amp; (i <= this.array.length))
		return this.array[(i - 1)] != null ? this.array[(i - 1)] : NIL;
	}
	return hashget(paramLuaValue);
  }

  protected LuaValue hashget(LuaValue paramLuaValue)
  {
	if (this.hashEntries > 0)
	{
	  LuaValue localLuaValue = this.hashValues[hashFindSlot(paramLuaValue)];
	  return localLuaValue != null ? localLuaValue : NIL;
	}
	return NIL;
  }

  public void set(int paramInt, LuaValue paramLuaValue)
  {
	if ((this.m_metatable == null) || (!rawget(paramInt).isnil()) || (!settable(this, LuaInteger.valueOf(paramInt), paramLuaValue)))
	  rawset(paramInt, paramLuaValue);
  }

  public void set(LuaValue paramLuaValue1, LuaValue paramLuaValue2)
  {
	paramLuaValue1.checkvalidkey();
	if ((this.m_metatable == null) || (!rawget(paramLuaValue1).isnil()) || (!settable(this, paramLuaValue1, paramLuaValue2)))
	  rawset(paramLuaValue1, paramLuaValue2);
  }

  public void rawset(int paramInt, LuaValue paramLuaValue)
  {
	if (!arrayset(paramInt, paramLuaValue))
	  hashset(LuaInteger.valueOf(paramInt), paramLuaValue);
  }

  public void rawset(LuaValue paramLuaValue1, LuaValue paramLuaValue2)
  {
	if ((!paramLuaValue1.isinttype()) || (!arrayset(paramLuaValue1.toint(), paramLuaValue2)))
	  hashset(paramLuaValue1, paramLuaValue2);
  }

  private boolean arrayset(int paramInt, LuaValue paramLuaValue)
  {
	if ((paramInt > 0) &amp;&amp; (paramInt <= this.array.length))
	{
	  this.array[(paramInt - 1)] = (paramLuaValue.isnil() ? null : paramLuaValue);
	  return true;
	}
	if ((paramInt == this.array.length + 1) &amp;&amp; (!paramLuaValue.isnil()))
	{
	  expandarray();
	  this.array[(paramInt - 1)] = paramLuaValue;
	  return true;
	}
	return false;
  }

  private void expandarray()
  {
	int i = this.array.length;
	int j = Math.max(2, i * 2);
	this.array = resize(this.array, j);
	for (int k = i; k < j; k++)
	{
	  LuaInteger localLuaInteger = LuaInteger.valueOf(k + 1);
	  LuaValue localLuaValue = hashget(localLuaInteger);
	  if (!localLuaValue.isnil())
	  {
		hashset(localLuaInteger, NIL);
		this.array[k] = localLuaValue;
	  }
	}
  }

  public LuaValue remove(int paramInt)
  {
	if (paramInt == 0)
	  paramInt = length();
	LuaValue localLuaValue1 = rawget(paramInt);
	LuaValue localLuaValue2 = localLuaValue1;
	while (!localLuaValue2.isnil())
	{
	  localLuaValue2 = rawget(paramInt + 1);
	  rawset(paramInt++, localLuaValue2);
	}
	return localLuaValue1.isnil() ? NONE : localLuaValue1;
  }

  public void insert(int paramInt, LuaValue paramLuaValue)
  {
	if (paramInt == 0)
	  paramInt = length() + 1;
	while (!paramLuaValue.isnil())
	{
	  LuaValue localLuaValue = rawget(paramInt);
	  rawset(paramInt++, paramLuaValue);
	  paramLuaValue = localLuaValue;
	}
  }
Edited on 05 December 2013 - 01:00 AM
distantcam #96
Posted 05 December 2013 - 03:32 AM
While that was interesting (I didn't know about how Lua tables worked under the covers) it didn't really answer my question.

Why is
table["foo"]
slower than
table.foo
?
Bomb Bloke #97
Posted 05 December 2013 - 04:51 AM
An educated guess based on the extract TheOriginalBIT posted is that when using the dot, "hashget()" is called - whereas when using the brackets, rawget() is called.

The latter function checks to see if what you put in the square brackets was a number or not, then if not, it just calls the former function and returns whatever that returns. Obviously it's faster to skip that extra check and call by rigging things so the former function is called directly.

It also appears that adding elements to a table does not require a new array each time - rather, if the current array is full then a new array double the size of the last is built, meaning after each resize it takes longer and longer until the next one is needed.
digpoe #98
Posted 17 May 2015 - 05:37 PM
While that was interesting (I didn't know about how Lua tables worked under the covers) it didn't really answer my question.

Why is
table["foo"]
slower than
table.foo
?

This is also actually a thing in the Lua VM. It's because there's an entire opcode dedicated to accessing tables using dots.
An excerpt from http://lua-users.org/wiki/OptimisationCodingTips;
Spoiler Table Access

Question: It's not the performance of creating the tables that I'm worried about, but rather all the accesses to the table contents.
(lhf) Tables are the central data structure in Lua. You shouldn't have to worry about table performance. A lot of effort is spent trying to make tables fast. For instance, there is a special opcode for a.x. See the difference between a.x and a[x] … but, like you said, the difference here is essentially an extra GETGLOBAL.


		 			
a,c = {},"x"
        CREATETABLE	0
        PUSHSTRING 	2	; "x"
        SETGLOBAL  	1	; c
        SETGLOBAL  	0	; a
b=a.x
        GETGLOBAL  	0	; a
        GETDOTTED  	2	; x
        SETGLOBAL  	3	; b
b=a["x"]
        GETGLOBAL  	0	; a
        GETDOTTED  	2	; x
        SETGLOBAL  	3	; b
b=a[c]
        GETGLOBAL  	0	; a
        GETGLOBAL  	1	; c
        GETTABLE   	
        SETGLOBAL  	3	; b
        END
Edited on 17 May 2015 - 03:39 PM
Yevano #99
Posted 18 May 2015 - 06:53 PM
This is also actually a thing in the Lua VM. It's because there's an entire opcode dedicated to accessing tables using dots.
An excerpt from http://lua-users.org...ationCodingTips;
Spoiler Table Access

Question: It's not the performance of creating the tables that I'm worried about, but rather all the accesses to the table contents.
(lhf) Tables are the central data structure in Lua. You shouldn't have to worry about table performance. A lot of effort is spent trying to make tables fast. For instance, there is a special opcode for a.x. See the difference between a.x and a[x] … but, like you said, the difference here is essentially an extra GETGLOBAL.

		 			
a,c = {},"x"
		CREATETABLE	0
		PUSHSTRING 	2	; "x"
		SETGLOBAL	  1	; c
		SETGLOBAL	  0	; a
b=a.x
		GETGLOBAL	  0	; a
		GETDOTTED	  2	; x
		SETGLOBAL	  3	; b
b=a["x"]
		GETGLOBAL	  0	; a
		GETDOTTED	  2	; x
		SETGLOBAL	  3	; b
b=a[c]
		GETGLOBAL	  0	; a
		GETGLOBAL	  1	; c
		GETTABLE   	
		SETGLOBAL	  3	; b
		END

*ahem, necro*

GET/SETDOTTED aren't present in Lua 5.1. They were in Lua 4. If there really is a difference, it probably would be from the Lua compiler emitting code which loads the string into a register before doing GET/SETTABLE rather than just referencing a constant index in the GET/SETTABLE instruction.


a.b = 3
settable <register of var a> <const index to string "b"> <const index to number 3>

a["b"] = 3
loadk <some register r> <const index to string "b">
settable <register of var a> <r> <const index to number 3>

The register argument probably triggers the check for correct type like BombBloke was saying. The constant argument doesn't require this since it is guaranteed to be whatever its type is in the constant pool. Even if that's not the case, loading the constant into a register takes an extra instruction anyways.
Thefdjurt #100
Posted 26 May 2015 - 08:22 PM
Cool stuffs (even though I knew it all ;)/>), but the link is dead.
GitHub's current page system requires you to create a new project then create a branch named "gh-pages"– I recommend setting this as the default branch– , and finally then add an "index.html" file.

The squid knows all….
Edited on 27 May 2015 - 04:11 AM
SquidDev #101
Posted 26 May 2015 - 09:37 PM
Cool stuffs (even though I knew it all ;)/>), but this link
is dead.
GitHub's current page system requires you to create a new project then create a branch named "gh-pages"– I recommend setting this as the default branch– , and finally then add an "index.html" file.

No, the creator deleted their site. You can browse the old code here though (or through the wayback machine)
Edited on 26 May 2015 - 07:38 PM
Waitdev_ #102
Posted 12 July 2015 - 03:57 PM
i thought i might point out with fs api,
try doing this

f = fs.open("file","w")
f.writeLine [[
--comment :P/>/>
function()
random lines n' stuff
lots of lines, so much lols!
]]
f.close()
and that writes to a file
Edited on 16 July 2015 - 02:56 AM
MKlegoman357 #103
Posted 12 July 2015 - 09:29 PM
i thought i might point out with fs api,
try doing this

f = f.open("file","w")
f.writeLine [[
--comment :P/>/>
function()
random lines n' stuff
lots of lines, so much lols!
]]
f.close()
and that writes to a file

I don't really see what you're trying to point out here. Could you elaborate more?
flaghacker #104
Posted 13 July 2015 - 10:54 PM
i thought i might point out with fs api,
try doing this

f = f.open("file","w")
f.writeLine [[
--comment :P/>/>
function()
random lines n' stuff
lots of lines, so much lols!
]]
f.close()
and that writes to a file

should be fs.open
Waitdev_ #105
Posted 16 July 2015 - 04:55 AM
what i'm saying is, when you pointed out the multi-line print,

print [[
this prints multi line stuff
this is a second line
]]
i noticed it also works with fs.writeLine()

fs.writeLine [[
this is line 1
this is line 2
]]
MKlegoman357 #106
Posted 16 July 2015 - 07:29 AM
That's because it's simply a Lua's multiline string. It will work with anything that requires strings, because a multiline string is exactly the same as a non-multiline string. The only difference is that you can embed special characters into a multiline string without escaping them, such as newline characters.
AoD #107
Posted 27 July 2015 - 09:05 PM
OMG thats a few tricks there lol bookmarked as this must come is handy at some point thanks for posting such an array of alternate coding techniques :)/>

Thanks OP