How to pass a table key in a function for use in a for loop? - lua

For some reason it doesn't appear to work to pass in a table key as a function argument, what is the trick to do this?
I'm trying to wrap the for loop iteration technique in vanilla Lua into a function that has three arguments: (1) the table to iterate, (2) the table_key to check each time, and (3) the value to find. If a match is found, return it, otherwise return nil.
function table_find_match(table, table_key, match_value)
for i=1, #table do
local this = table[i]
if this[table_key] == match_value then
return this[table_key]
end
end
return nil
end
local table_example = {
{
key_example = "string_value_1"
},
{
key_example = "string_value_2"
}
}
local result = table_find_match(table_example, key_example, "string_value_1")
print(result)

Found a solution, if I pass in the table key as a string it works, such as
table_find_match(table_example, "key_example", "string_value_1")
but I really dislike having to convert it into a string, if anyone knows any other workaround to this please share

If you pass it like table_find_match(table_example, key_example, "string_value_1")
the key_example is now considered as a (nil) variable if not defined before executing, so it has to be like
local key_example = "key_example"
local result = table_find_match(table_example, key_example, "string_value_1")
print(result)

Related

Writing function to check state's machine current state [Lua/Love2d]

I'm learning game development with LÖVE2D and Lua and lately I've been using a state machine class. I haven't coded the class myself, but I've went through the code and I think I pretty much got it, besides this one problem.
The thing is, I'm trying to prompt the class for its current state, just so I can use it inside an if, but no matter what, I cannot get it right.
Here's the relevant code of the class:
StateMachine = Class{}
function StateMachine:init(states)
self.empty = {
render = function() end,
update = function() end,
enter = function() end,
exit = function() end
}
self.states = states or {} -- [name] -> [function that returns states]
self.current = self.empty
end
function StateMachine:change(stateName, enterParams)
assert(self.states[stateName]) -- state must exist!
self.current:exit()
self.current = self.states[stateName]()
self.current:enter(enterParams)
end
What I'm basically trying to do is:
function StateMachine:is(stateName)
if self.current == self.states[stateName] then
-- this never executes
return true
end
return false
end
I've tried changing self.states[stateName] to other things to test it out and also tried printing stuff to the console to see why the comparison is never true. It seems self.current returns a pointer to a table and thus never matches whatever is on the other side of the comparison operator.
Thanks for your help!
self.current is set to the return value of self.states[stateName] in StateMachine:change
function StateMachine:change(stateName, enterParams)
...
self.current = self.states[stateName]() -- note the () indicating the call
This means, unless the return value is self, self.current will not be equal to the function or object self.states[stateName] that it is compared to in StateMachine:is
function StateMachine:is(stateName)
if self.current == self.states[stateName] then -- here we are comparing the function to the return value
I would suggest expanding your state object to have a :getName function that would return the stateName or to store the name in your StateMachine under a key such as currentStateName.
I had the exact same question & I'd like to add--perhaps some of the comments explain this in language I just didn't understand :D--but in the getCurrentState function I created I had to do this:
function StateMachine:getCurrentState()
variable = self.currentStateName
return variable
where ofc variable is just some placeholder. but I had to grab the reference that self.currentStateName was pointing to, otherwise the comparison always failed.

Computercraft function that returns an array, use first element for boolean

Edited for more details:
I'm trying to have a turtle that is sitting in front of a sapling wait for it to grow before cutting it down. It compares the log to the item in front until it matches. The system I'm currently using works, but I was hoping there was a slightly more minimal way to write it.
checkTarget = {
forward = function(tgt)
check = {turtle.inspect()} --creates table with first as boolean, second as information table
local rtn = {false, check[2]}
if type(tgt) == "table" then
for k, v in pairs(tgt) do
if check[2].name == v then
rtn = {true, v}
break
end
end
elseif tgt == nil then
return check[1]
elseif check[2].name == tgt then
rtn[1] = true
end
return rtn
end,--continued
This takes an argument, either a string or an array of strings, to compare against. When it checks the block in front it saves the detailed information to the second element in rtn and the first to a default of false. If the string matches the checked block's name, then it changes rtn[1] to true and returns all of it, which is the table at the bottom when doing checkTarget.forward("minecraft:log").
My question was, I am currently making a disposable variable to store the array that is returned from checkTarget, and then calling the variable's first element to get if it's true or not. I was hoping there was a way to include it in the if statement without the disposable variable (tempV)
repeat
local tempV = fox.checkTarget.forward("minecraft:log")
if tempV[1] then
cut()
fox.goTo({x = 0, y = 0, z = 0})
fox.face(0)
end
tempV = fox.checkTarget.forward("minecraft:log")
until not run
{
false,
{
state = {
stage = 0,
type = "birch",
},
name = "minecraft:sapling",
metadata = 2
}
}
Instead of
local tempV = fox.checkTarget.forward("minecraft:log")
if tempV[1] then
end
You can do
if fox.checkTarget.forward("minecraft:log")[1] then
end
and then calling the variable's first element to get if it's true or
not.
With tempV[1] you're not calling the first element, you're indexing it.
To call something you have to use the call operator () which doesn't make sense as a boolean is not callable.

Can anyone explain how we will get the output of the following code in Lua?

function ReturnTwoVal()
return "1","2"
end
function ReturnThreeVals()
return "x","y","Z"
end
TblA = {ReturnThreeVals(),ReturnTwoVal() }
print(TblA[2],TblA[1], TblA[2], TblA[3], TblA[4])
Output will be: 1 x 1 2 nil
Expressions that return multiple values are adjusted to a single value, unless they are the last expression in a function call or table constructor.
Therefore,
TblA = {ReturnThreeVals(),ReturnTwoVal() }
is equivalent to
TblA = {"x", "1","2"}

Lua Metatables - calling functions with colon syntax

I have the following problem, somebody can help me?
comp = {}
comp.__index = function(obj,val)
if val == "insert" then
return rawget(obj,"gr")["insert"]
end
return rawget(obj, val)
end
comp.new = function()
local ret = {}
setmetatable(ret, comp)
ret.gr = display.newGroup()
return ret
end
local pru = comp.new()
pru.gr:insert(display.newImage("wakatuBlue.png"))
This line works, but I don't want to access the insert method using the gr property, I want to call the insert method directly and the metatable __index function does the work
pru:insert(display.newImage("wakatuBlue.png"))
This line doesn't work and I get this error: "bad argument #-2 to 'insert' (Proxy expected, got nil)", but this is the way that I'm looking to use
Do you want something like this?
comp = {}
comp.__index = function(obj,val)
if val == "insert" then
return rawget(obj,"gr"):insert(val)
end
return rawget(obj, val)
end
__index works just fine; it's because your last call is interpreted as:
pru.insert(pru, display.newImage("wakatuBlue.png"))
whereas you want/need it to be:
pru.insert(pru.gr, display.newImage("wakatuBlue.png"))
You either need to call it like this or explain what you are trying to do.

Storing values in a userdata object from lua

What I want to do is this:
object.foo = "bar"
print(object.foo)
where "object" is a userdata.
I've been googling for a while (using the keyword __newindex and lua_rawset) but I can't any examples that do what I want it to do.
I want to do this in with the lua api in c++
Let us write this in Lua code so that we can make quick experiments with the code
function create_object()
-- ## Create new userdatum with a metatable
local obj = newproxy(true)
local store = {}
getmetatable(obj).__index = store
getmetatable(obj).__newindex = store
return obj
end
ud = create_object()
ud.a = 10
print(ud.a)
-- prints '10'
If you work with userdata you probably want to do the above using the C API. However the Lua code should make it clear extactly which steps are necessary. (The newproxy(..) function simply creates a dummy userdata from Lua.)
I gave up trying to do this in C++ so I did it in lua. I loop through all the metatables (_R) and assign the meta methods.
_R.METAVALUES = {}
for key, meta in pairs(_R) do
meta.__oldindex = meta.__oldindex or meta.__index
function meta.__index(self, key)
_R.METAVALUES[tostring(self)] = _R.METAVALUES[tostring(self)] or {}
if _R.METAVALUES[tostring(self)][key] then
return _R.METAVALUES[tostring(self)][key]
end
return meta.__oldindex(self, key)
end
function meta.__newindex(self, key, value)
_R.METAVALUES[tostring(self)] = _R.METAVALUES[tostring(self)] or {}
_R.METAVALUES[tostring(self)][key] = value
end
function meta:__gc()
_R.METAVALUES[tostring(self)] = nil
end
end
The problem with this is what I'm supposed to use for index. tostring(self) only works for those objects with an ID returned to tostring. Not all objects have an ID such as Vec3 and Ang3 and all that.
You could also use a simple table...
config = { tooltype1 = "Tool",
tooltype2 = "HopperBin",
number = 5,
}
print(config.tooltype1) --"Tool"
print(config.tooltype2) --"HopperBin"
print(config.number) --5

Resources