Programming beginner here learning Lua. I always see this function in example code mostly in a for loop that goes through an array. I dont actually understand what it does and why I should use it. It seems I make similar for loops a lot that do almost the same thing but i never use pairs() or ipairs()
From the Lua 5.4 Reference manual:
ipairs (t)
Returns three values (an iterator function, the table t, and 0) so
that the construction
for i,v in ipairs(t) do body end
will iterate over the key–value pairs (1,t[1]), (2,t[2]), ..., up to
the first absent index.
pairs (t)
If t has a metamethod __pairs, calls it with t as argument and returns
the first three results from the call.
Otherwise, returns three values: the next function, the table t, and
nil, so that the construction
for k,v in pairs(t) do body end
will iterate over all key–value pairs of table t.
https://www.lua.org/manual/5.4/manual.html#pdf-ipairs
https://www.lua.org/manual/5.4/manual.html#pdf-pairs
Related
I am really newbie in lua. I have this lua code
local gun_info = {
g_sword={rate=0.5;spd=0;dmg=1;ammo=1;};
g_pistol={rate=0.5;spd=5;dmg=1;ammo=40;};
g_knife={rate=0.8;spd=5;dmg=1;ammo=1;};
g_shuriken={rate=0.3;spd=5;dmg=1;ammo=40;};
g_bomb={rate=0.8;spd=5;dmg=1;ammo=20;};
};
I just want get values of every ammo. Other properties are no needed.
for k, v in pairs(gun_info) do
print(k, v[1], v[2], v[3], v[4], v[5])
end
this prints out whole tables but I need just value of ammos
Use comma between table variables rather than semicolon. Using semicolon is not syntactically wrong but optional in Lua. Semicolon is usually used to separate multiple statements written in single line.
You can directly access the variable ammo by indexing the key of the table
for k, v in pairs(gun_info) do
print(k, v.ammo)
end
v.ammo and v[ammo] are not same in Lua.
Note: The order in which the elements appear in traversal will not be the same as you defined and can produce different order each time. This is due to the way tables are implemented in Lua.
Continuing to learn Lua.
I have wrote a function that removes the first sentence from each line and returns the result as a table of modified lines, where the first sentence was removed. Strangely, table.insert behaves weird in such function.
function mypackage.remove_first(table_of_lines)
local lns = table_of_lines
local new_lns = {}
for i=1,#lns do
table.insert(new_lns,string.gsub(lns[i],"^[^.]+. ","",1))
end
return new_lns
end
Unexpectedly, this gave me the following error.
[string "function mypackage.remove_first(table_of_lines)..."]:5: bad argument #2 to 'insert' (number expected, got string)
Why is "number expected" in the first place?
From table.insert docs
Inserts element value at position pos in list, shifting up the
elements list[pos], list[pos+1], ···, list[#list]. The default value
for pos is #list+1, so that a call table.insert(t,x) inserts x at the
end of list t.
Nothing is said about type requirements for table.insert. Ok, I decided to modify the example.
function mypackage.remove_first(table_of_lines)
local lns = table_of_lines
local new_lns = {}
for i=1,#lns do
local nofirst = string.gsub(lns[i],"^[^.]+. ","",1)
table.insert(new_lns,nofirst)
end
return new_lns
end
And now everything works. Can you explain what is going on here?
The problem is a bit complicated. It's a collision of three factors:
string.gsub returns two parameters; the second parameter is the number of matches.
table.insert can take 3 parameters. When it is given 3 parameters, the second parameter is expected to be an integer offset defining where to insert the object.
When you do this: func1(func2()), all of the return values of func2 are passed to func1, so long as you don't pass arguments after func2 in func1's argument list. So func1(func2(), something_else) will get only 2 arguments.
Therefore, when you do table.insert(ins, string.gsub(...)), this will invoke the 3-argument version, which expects the second argument to be the index to insert the object into. Hence the problem.
If you want to ensure discarding, then you can wrap the expression in parenthesis:
table.insert(new_lns, (string.gsub(lns[i], "^[^.]+. ", "", 1)))
What is the difference between tables and metatables in Corona? What are the types of metatables? How and where can I use them? What is the main purpose of using tables and metatables?
Tables in Lua are the main data type you can use to create dynamic, structured data. Other languages have arrays, lists, dictionaries (key-value storage), in Lua you only have tables. The only operations you can do with a basic table is indexing and storing a value using the tab[key] syntax, i.e.:
local tab = {}
tab['key1'] = 'Hello' -- storing a value using a string key
tab.key2 = 'World' -- this is syntax sugar, equivalent to previous
print(tab.key1, tab['key2']) -- indexing, the syntax is interchangable
You cannot do anything else with basic tables, for example adding them:
local v1={x=0,y=0}
local v2={x=1,y=1}
print(v1+v2)
--> stdin:1: attempt to perform arithmetic on local 'v1' (a table value)
A metatable allows you to modify the behavior of tables, to specify what should be done when tables are added, multiplied, concatenated (..), etc. A metatable is just a table, which contains functions with special keys, also called metamethods. You can assign a metatable to a table using setmetatable(). For example:
local Vector = {} -- this will be the metatable for vectors
function Vector.__add(v1, v2) -- what to do when vectors are added
-- create a new table and assign it a Vector metatable
return setmetatable({x=v1.x+v2.x, y=v1.y+v2.y}, Vector)
end
function Vector.__tostring(v) -- how a vector should be displayed
-- this is used by tostring() and print()
return '{x=' .. v.x .. ',y=' .. v.y .. '}'
end
local v1 = setmetatable({x=1, y=2}, Vector)
local v2 = setmetatable({x=3, y=4}, Vector)
-- vectors are added and the resulting vector is printed
print(v1 + v2) --> {x=4,y=6}
If you want to understand metatables better, you should definitely read the Programming in Lua chapter on metatables.
Lua (which is the language that Corona is based on) uses metatables for different purposes.
The relevant entry in the manual is Section 2.8.
A nice tutorial can be found here or here.
A metatable is just a table like any other, but is set as metatable on another table (which I'll call a base table further on, to make a difference between the 2 tables).
A metatable can contain anything, but the special keys (starting with a double underscore) are the interesting ones. The values set to this keys in this table will be called on special occasions. Which occasion depends on which key. The most interesting are:
__index: Will be used whenever a key in the base table is looked up, but does not exist. This can either contain table, in which the key will be looked up instead, or a function, which will be passed the original table and the key. This can be used for implementing methods on tables (OOP style), for redirection, fall through cases, setting defaults, etc etc
__newindex: Will be used whenever a new key is to be assigned in a table (which was previously nil). If it's a table, the key will be assigned in that table. If it's a function, that function will be passed the original table, key and value. This can be used for controlling access to a table, preprocessing data, redirection of assignments.
__call: enables you to set a function to be called if you use eg. table().
__add,__sub,__mul,__div,__mod are used to implement binary operations,
__unm is used to implement unary operations,
__concat is used for implementing concatenation (the .. operator)
__len is used for implementing the length operator (#)
__eq,__lt,__le are used for implementing comparisons
A small thing to know when using __index & co.: in those methods, you should use rawget and rawset in order to prevent calling the metamethod each time again, causing a loop.
As a small example:
t={1,2,3} -- basetable
mt={} -- metatable
mt.__index=function(t,k)
print("__index event from "..tostring(t).." key "..k)
return "currently unavailable"
end
mt.__newindex=function(t,k,v)
print("__newindex event from "..tostring(t).." key: "..k.." value: "..v)
if type(k)=="string" then
rawset(t,k,v:reverse())
else
rawset(t,k,v)
end
end
mt.__call=function(t,...)
print("call to table "..tostring(t).." with arguments: ".. table.concat({...},','))
print("All elements of the table:")
for k,v in pairs(t) do print(k,v) end
end
setmetatable(t,mt)
t[4]="foo" -- this will run the __newindex method
print(t[5]) -- this will run the __index method
t("foo","bar")
-- Multiple fall through example:
t={}
mt={}
mt2={}
setmetatable(t,mt) -- metatable on base table
setmetatable(mt,mt2) -- second layer of metatable
mt.__index=function(t,k) print('key '..k..' not found in '..namelookup[t]) return getmetatable(t)[k] end -- tries looking nonexistant indexes up in mt.
mt2.__index=mt.__index -- function was written portably, reuse it.
t[1]='A'
mt[2]='B'
mt2[3]='C'
namelookup={[t]="t",[mt]="mt",[mt2]="mt2"}
print(t[1],t[2],t[3],t[4])
Now these are but silly examples, you can do much more complex stuff. Take a look at the examples, take a look at the relevant chapters in Programming in Lua, and experiment. And try not to get confused ;)
I am trying to modify a collection of arrays inside of a variadic function. I end up working on a copy when trying to add to the arrays and they get lost after the call. Is there any way to pass values by ref in Lua?
function myfunc(...)
local args = {...}
--do work on args--
end
"do work" doesn't actually end up doing anything but it works outside the function just fine.
Obviously I could pass an array of arrays and not use ... but that kinda defeats the purpose of using ...
In Lua, you can't just choose to pass variables by reference or not. Basic types are never passed by reference (like numbers and booleans), others are always passed by reference (like tables, userdata and strings). In the case of strings this does not matter much, because they are immutable anyhow.
So either you pass your arguments you want to work on globally as strings like this:
a=2
b=3
function myfunc(...)
local args={...}
for k,v in pairs(args) do
_G[v]=_G[v]+k
end
end
myfunc('a')
print(a) -- 3
myfunc('a','b')
print(a,b) -- 4 5
Note that this only works on globals, since locals are not kept in a table.
Working with tables makes this kind of things less painful:
function myfunc(t)
for k,v in pairs(t) do
t[k]=v+k
end
end
tab1={a=2}
myfunc(tab1)
print(tab1.a) -- 3
tab2={a=2,b=3}
myfunc(tab2)
print(tab2.a,tab2.b) -- 3 5
The purpose of using ... is grouping the whole parameter list in one varible. That has little to do with the by-reference or by-value nature of the parameters.
All natural types in Lua are passed by value, with tables being the only exception.
The simplest way to do what you want is to pass an array of arrays. If the two extra characters seem like too much typing, know that you can remove the parenthesis instead:
foo({a,b,c})
foo{a,b,c} -- equivalent
In Lua, there seem to be two ways of appending an element to an array:
table.insert(t, i)
and
t[#t+1] = i
Which should I use, and why?
Which to use is a matter of preference and circumstance: as the # length operator was introduced in version 5.1, t[#t+1] = i will not work in Lua 5.0, whereas table.insert has been present since 5.0 and will work in both. On the other hand, t[#t+1] = i uses exclusively language-level operators, wheras table.insert involves a function (which has a slight amount of overhead to look up and call and depends on the table module in the environment).
In the second edition of Programming in Lua (an update of the Lua 5.0-oriented first edition), Roberto Ierusalimschy (the designer of Lua) states that he prefers t[#t+1] = i, as it's more visible.
Also, depending on your use case, the answer may be "neither". See the manual entry on the behavior of the length operator:
If the array has "holes" (that is, nil values between other non-nil values), then #t can be any of the indices that directly precedes a nil value (that is, it may consider any such nil value as the end of the array).
As such, if you're dealing with an array with holes, using either one (table.insert uses the length operator) may "append" your value to a lower index in the array than you want. How you define the size of your array in this scenario is up to you, and, again, depends on preference and circumstance: you can use table.maxn (disappearing in 5.2 but trivial to write), you can keep an n field in the table and update it when necessary, you can wrap the table in a metatable, or you could use another solution that better fits your situation (in a loop, a local tsize in the scope immediately outside the loop will often suffice).
The following is slightly on the amusing side but possibly with a grain of aesthetics. Even though there are obvious reasons that mytable:operation() is not supplied like mystring:operation(), one can easily roll one's own variant, and get a third notation if desired.
Table = {}
Table.__index = table
function Table.new()
local t = {}
setmetatable(t, Table)
return t
end
mytable = Table.new()
mytable:insert('Hello')
mytable:insert('World')
for _, s in ipairs(mytable) do
print(s)
end
insert can insert arbitrarily (as its name states), it only defaults to #t + 1, where as t[#t + 1] = i will always append to the (end of the) table. see section 5.5 in the lua manual.
'#' operator only use indexed key table.
t = {1, 2 ,3 ,4, 5, x=1, y=2}
at above code
print(#t) --> print 5 not 7
'#' operator whenever not using.
If you want to '#' operator, then check it to table elements type.
Insert function can using any type use.But element count to work slow than '#'