(most important stuff first, trailing off into less relevant stuff towards the end. I tried to keep it short!)
also called dictionaries/records/hashmaps/objects/etc by other languages
local tab_a = {hp=3,x=14,y=17} --a table with keys 'hp', 'x', and 'y'
print(tab_a.hp) --3
print(tab_a['hp']) --3
local key = 'hp' print(tab_a[key]) --3
tab_a.hp = nil --delete hp from the table
print(tab_a.bamboo) --nil, the key does not exist. but this is not an error
surprise, these are also tables, just with integer keys. but it's useful to call them "arrays"
local tab_b = {10,20,30} --keys 1,2,3 (note: lua arrays are 1-based; it's generally best to just go along with it)
print(tab_b[1]) --10
print(tab_b[2]) --20
print(tab_b[3]) --30
print(tab_b[0]) --nil
print(tab_b.elephant) --nil
append to the end with table.insert(tab_b,40)
or tab_b[#tab_b+1]=40
. I might type add(tab_b,40)
sometimes b/c I'm used to pico8. (I have a helper for that)
for ii=1,#tab_b do
print(tab_b[ii])
end
for ii,entry in ipairs(tab_b) do
print(entry)
end
#
returns an array's length; it only works on array-like tables (keys 1..n). if you try it on an array with "holes" you'll get very confusing results
pairs is like ipairs. pairs works on all tables but has no guaranteed iteration order. ipairs only works on arrays and goes up 1..#array
you can write custom iterators to use instead of pairs/ipairs
assert(myvar == 2,'oh no, myvar was '..tostring(myvar)..' instead of 2') -- very useful to check your assumptions
assert(myvar == 2,qq('got',myvar,'want',2)) -- qq() is my string-building helper
assert(myvar,'myvar was nil/false')
print(3) -- '3', hooray!
print({x=3,y=4}) -- 'table: 0x55c54b8dd110' -- not very helpful!!!!
pq({x=3,y=4}) -- '{x=3, y=4}' yay! pq is my own custom thing; I consider it essential. its in printh.lua along with other helpers
pq(tab_a,tab_b,nil,'whaddup',tab_c) -- can print multiple; handles nil fine.
every variable is nil until it isn't
assert(my_unset_var==nil) -- it's just nil, this line doesn't cause an error
local my_unset_var=3
assert(my_unset_var~=nil)
my_unset_var=nil --now its nil again
nil is very cool, basically every variable is a Maybe/Optional type (e.g. from haskell, rust, others)
local result = maybe_do_something()
if result then
result.act()
end
result
here is either nil or some sort of object, and you can check if it exists with if
.
if/and/or all check the "truthiness" of a variable: nil and false are falsey, literally everything else is truthy (including 0, "", {})
a = cond and b or c
-- this is like if cond then a=b else a=c end
. but be very careful -- if b is nil or false, a will always equal c. (and and or don't return true or false, they return the first/last argument they took that caused them to resolve. I didn't explain that well but it lets you do this ternary stuff)
function callme(x,y, border) -- the extra space is my personal convention
border = border or 8
end
if border is not passed, e.g. callme(2,3)
then border will be nil, until the first line sets it to 8
all variables names are references that point to a value. assigning a variable assigns the value on the righthand side into the name on the lefthand side. this can cause subtleties you might not expect:
a=1
b=a
a=1000
assert(b==1)
a={x=0}
b=a -- b and a now refer to the same table. the "value" of a is... sorta like a pointer to the table, not the table itself
print(a) --table: 0x55c54b8dd110
print(b) --table: 0x55c54b8dd110
a.x=1000
assert(b.x==1000)
function hello(x,y)
return x+y,x*y
end
local c,d = hello(20,30)
assert(c==50)
assert(d==600)
cute, useful, but pretty cursed if you try to get too fancy:
function nums()
return 1,2
end
function feedme(a,b,c)
print(a)
print(b)
print(c)
end
feedme(1,2,3) --1,2,3
feedme(4,nums()) --4,1,2 --yo cool
feedme(nums(),3) --1,3,nil !?! (multivals only work as the last arg)
lots of info in the lua manual -- this is 5.1, which is what luajit uses, which is what love2d uses. its a bit old and I wish we used lua 5.4 but we do not.
table:foo(x,y)
is syntax sugar for table.foo(table,x,y)
.
function thingy:bar(x,y)
is syntax sugar for thingy.bar = function(self,table,x,y)
. so you'll see self
a lot, it's not particularly special to the lua interpreter, but it's what that first argument gets named so its convenient to use it. and text editors will prolly highlight it special. you can do weird stuff like thingy.bar(other_thing,x,y)
(note: .
, not :
) but idk why you would want to
very common hard-to-track-down bug: saying thingy.foo()
when you meant to say thingy:foo()
. I dunno any good way of catching that. some sort of linter program?
no static typing, sorry. this causes bugs sometimes. I test the stuff I'm working on often and it works well enough
you can change the type of a variable but please do not:
local a="hello"
a=42
a={x=3}
assert(type(3)=="number")
basic type checking. use very sparingly, a bit of a code-smell to me
assert(2.0==2)
all numbers are floats (doubles? not sure). bitwise operations (& | ~ <<
etc) only work on numbers that happen to be integers -- error otherwise, annoying. also bitwise ops don't work in html exports, so I'm avoiding them for now
if foo and foo.time==4 then
elseif bar~=2 then --not equals
elseif not qux then
-- elseif not bar==4 then -- don't do this!!! it checks (not bar)==4, oh no
else
end
while thing do
end
function foo(a,b)
end
there's no switch
statement
there's no continue
statement inside for
loops. (there is a break
tho). you can use ::name:: ... goto name
except probably don't b/c that breaks in html exports
function stuff(x,y,...)
local as_table={...}
return as_table[3]
end
print(stuff(1,2,10,20,30,40,50)) --30
the last arg can be ...
, for extra "varargs", which are a multival. you can do {...}
to turn it into a table. its niche but hard to google so I'm putting it here
I wish lua had x += 1
but it does not. you gotta x = x+1
instead
math.random is kinda wack (sometimes returns floats, sometimes returns ints), I have helpers that I use instead
local myvar = require 'src/hello'
is a function that runs the file src/hello.lua (path is relative to main.lua) and stores the result (the top-level return
, or nil) in myvar. anything declared globally in hello.lua will be put in the global namespace, files don't have separate namespaces.
syntax sugar note: the parens in the function call foo('hello')
are unecessary. this only applies when the function takes a single arg and that arg is a literal string or table. e.g. foo'hello'
or foo{x=3,y=4}
. it's odd. but anyway that's how require 'foo'
works.