how to understand ":" in the following code? [duplicate] - lua

I am confused about the difference between function calls via . and via :
> x = {foo = function(a,b) return a end, bar = function(a,b) return b end, }
> return x.foo(3,4)
3
> return x.bar(3,4)
4
> return x:foo(3,4)
table: 0x10a120
> return x:bar(3,4)
3
What is the : doing ?

The colon is for implementing methods that pass self as the first parameter. So x:bar(3,4)should be the same as x.bar(x,3,4).

For definition it is exactly the same as specifying self manually - it will even produce same bytecode on compilation. I.e. function object:method(arg1, arg2) is same as function object.method(self, arg1, arg2).
On use : is almost the same as . - a special kind of call will be used internally to make sure object and any possible side-effects of calculations/access are calculated only once. Calling object:method(arg1, arg2) is otherwise same as object.method(object, arg1, arg2).

To be completely precise, obj:method(1, 2, 3) is the same as
do
local _obj = obj
_obj.method(_obj, 1, 2, 3)
end
Why the local variable? Because, as many have pointed out, obj:method() only indexes _ENV once to get obj. This normally just important when considering speed, but consider this situation:
local tab do
local obj_local = { method = function(self, n) print n end }
tab = setmetatable({}, {__index = function(idx)
print "Accessing "..idx
if idx=="obj" then return obj_local end
end})
end
tab.obj.method(tab.obj, 20)
--> Accessing obj
--> Accessing obj
--> 20
tab.obj:method(10)
--> Accessing obj
--> 10
Now imagine the __index metamethod did more than just printing something. Imagine it increased a counter, logged something to a file or deleted a random user from your database. There's a big difference between doing that twice or only once. In this case, there's a clear difference between obj.method(obj, etc) and obj:method(etc).

Related

LUA: Looking for a specific table by its variable

I'm currently starting work on a text adventure game in Lua--no addons, just pure Lua for my first project. In essence, here is my problem; I'm trying to find out how I can do a "reverse lookup" of a table using one of its variables. Here's an example of what I've tried to do:
print("What are you trying to take?")
bag = {}
gold = {name="Gold",ap=3}
x = io.read("*l")
if x == "Gold" then
table.insert(bag,gold)
print("You took the " .. gold.name .. ".")
end
Obviously, writing a line like this with every single object in the game would be very... exhausting--especially since I think I'll be able to use this solution for not just taking items but movement from room to room using a reverse lookup with each room's (x,y) coordinates. Anyone have any ideas on how to make a more flexible system that can find a table by the player typing in one of its variables? Thanks in advance!
-blockchainporter
This doesn't directly answer your question as you asked it, but I think it would serve the purpose of what you are trying to do. I create a table called 'loot' which can hold many objects, and the player can place any of these in their 'bag' by typing the name.
bag = {}
loot = {
{name="Gold", qty=3},
{name="Axe", qty=1},
}
print("What are you trying to take?")
x = io.read("*l")
i = 1
while loot[i] do
if (x == loot[i].name) then
table.insert(bag, table.remove(loot,i))
else
i = i + 1
end
end
For bonus points, you could check 'bag' to see if the player has some of that item already and then just update the quantity...
while loot[i] do
if (x == loot[i].name) then
j, found = 1, nil
while bag[j] do
if (x == bag[j].name) then
found = true
bag[j].qty = bag[j].qty + loot[i].qty
table.remove(loot,i)
end
j = j + 1
end
if (not found) then
table.insert(bag, table.remove(loot,i))
end
else
i = i + 1
end
end
Again, this isn't a 'reverse lookup' solution like you asked for... but I think it is closer to what you are trying to do by letting a user choose to loot something.
My disclaimer is that I don't use IO functions in my own lua usage, so I have to assume that your x = io.read("*l") is correct.
PS. If you only ever want objects to have a name and qty, and never any other properties (like condition, enchantment, or whatever) then you could also simplify my solution by using key/val pairs:
bag = {}
loot = { ["Gold"] = 3, ["Axe"] = 1 }
print("What are you trying to take?")
x = io.read("*l")
for name, qty in pairs(loot) do
if x == name then
bag.name = (bag.name or 0) + qty
loot.name = nil
end
end
I have a few notes to start before I specifically address your question. (I just want to do this before I forget, so please bear with me!)
I recommend printing to the terminal using stderr instead of stdout--the Lua function print uses the latter. When I am writing a Lua script, I often create a C-style function called eprintf to print formatted output to stderr. I implement it like this:
local function eprintf(fmt, ...)
io.stderr:write(string.format(fmt, ...))
return
end
Just be aware that, unlike print, this function does not automatically append a newline character to the output string; to do so, remember to put \n at the end of your fmt string.
Next, it may be useful to define a helper function that calls io.read("*l") to get an entire line of input. In writing some example code to help answer your question, I called my function getline--like the C++ function that has similar behavior--and defined it like this:
local function getline()
local read = tostring(io.read("*l"))
return read
end
If I correctly understand what it is you are trying to do, the player will have an inventory--which you have called bag--and he can put items into it by entering item names into stdin. So, for instance, if the player found a treasure chest with gold, a sword, and a potion in it and he wanted to take the gold, he would type Gold into stdin and it would be placed in his inventory.
Based on what you have so far, it looks like you are using Lua tables to create these items: each table has a name index and another called ap; and, if a player's text input matches an item's name, the player picks that up item.
I would recommend creating an Item class, which you could abstract nicely by placing it in its own script and then loading it as needed with require. This is a very basic Item class module I wrote:
----------------
-- Item class --
----------------
local Item = {__name = "Item"}
Item.__metatable = "metatable"
Item.__index = Item
-- __newindex metamethod.
function Item.__newindex(self, k, v)
local err = string.format(
"type `Item` does not have member `%s`",
tostring(k)
)
return error(err, 2)
end
-- Item constructor
function Item.new(name_in, ap_in)
assert((name_in ~= nil) and (ap_in ~= nil))
local self = {
name = name_in,
ap = ap_in
}
return setmetatable(self, Item)
end
return Item
From there, I wrote a main driver to encapsulate some of the behavior you described in your question. (Yes, I know my Lua code looks more like C.)
#!/usr/bin/lua
-------------
-- Modules --
-------------
local Item = assert(require("Item"))
local function eprintf(fmt, ...)
io.stderr:write(string.format(fmt, ...))
return
end
local function printf(fmt, ...)
io.stdout:write(string.format(fmt, ...))
return
end
local function getline()
local read = tostring(io.read("*l"))
return read
end
local function main(argc, argv)
local gold = Item.new("Gold", 3)
printf("gold.name = %s\ngold.ap = %i\n", gold.name, gold.ap)
return 0
end
main(#arg, arg)
Now, as for the reverse search which you described, at this point all you should have to do is check the user's input against an Item's name. Here it is in the main function:
local function main(argc, argv)
local gold = Item.new("Gold", 3)
local bag = {}
eprintf("What are you trying to take? ")
local input = getline()
if (input == gold.name) then
table.insert(bag, gold)
eprintf("You took the %s.\n", gold.name)
else
eprintf("Unrecognized item `%s`.\n", input)
end
return 0
end
I hope this helps!

attempt to call method 'func' (a nil value)

No matter how I approach Lua, I run into this error all the time, so I must not understand something inherit to the language:
attempt to call method 'func' (a nil value)
I've seen the error here a few times as well but the problem doesn't seem clear to me.
Here's my module:
actor.lua
Actor = {
x = 0,
mt = {},
new = function()
local new_actor = {}
new_actor.x = Actor.x
new_actor.mt = Actor.mt
return new_actor
end,
test = function(self, a, b)
print(a, b)
end
}
I'm using Löve.
main.lua
require "game/actor"
local a = Actor:new() --works fine
function love.load()
a.x = 10
print(a.x) --output: 10
a:test(11, 12) --error: attempt to call method 'test' (a nil value)
end
I'm also not sure when it's appropriate to use the previous styling over this in a module.
Actor = {
x = 0
}
Actor.mt = {}
function Actor.new()
print(42)
end
I'm honestly not sure what is more correct than the other but considering I run into a simple error either way, there's probably something I'm missing entirely?
It looks like you're trying to instance a kind of class made of metatables. You basically need to assign new_actor's metatable with Actor.mt. (Resuming the problem: when you're indexing new_actor you're not indexing Actor in this case)
setmetatable(new_actor, Actor.mt);
Even if the metatable is being added, it won't work until you put the meta "__index" event to index a table containing your class methods/values, in this case:
Actor.mt = {
__index = Actor
};
I'd suggest moving your class methods/values into a new table, like Actor.prototype, Actor.fn, etc... avoiding conflicts:
Actor.fn = {
test = function(self, a, b)
print(a, b)
end
};
Actor.mt = {
__index = Actor.fn
};
More about metatables in Lua 5.3 manual.

How to get variables that not declared yet but will declared soon?

How can I get variables that not declared yet?
Here are simple example:
a = b
b = 123
What I want from these 2 lines is a << 123. But obv it doesn't work.
I know the easy way to get the answer a = 123 is cut 1st line and paste it to lower than 2nd line.
But I'm in some problem. I need some function like 'WillDeclaredVar()' that I can use in like this:
a = WillDeclaredVar(b)
sheepCount = 123
b = sheepCount
print(a)
so I can get the answer '123'.
Or there are any built-in functions that will allows me to do similar thing?
===
I think the link given by timrau is not telling my case. the key point is how to get Variables 'that not declared yet'.
===
Adding actual Code:
triggerCount = 0 -- Counting number of 'Trigger' function
local Trigger = function (t)
triggerCount = triggerCount + 1
return Trigger (t)
end
-- following Triggers are same as while statement.
-- following Triggers doing: Add 1 MarineCount until get 64000 MarineCount
Trigger { -- Here the Trigger function. Now triggerCount = 1.
players = {P1}
actions = {
SetDeaths(P1, Add, 1, "Terran Marine")
},
flag = {preserved},
}
Portal(LoopStart);
-- function Portal(VariableName) returns VariableName = triggerCount. So LoopStart = 1.
Trigger { -- Now triggerCount = 2.
players = {P1}
actions = {
LinkList(LoopEnd, LoopStart);
-- function LinkList(From, To) changes 'From' Trigger's next pointer to the 'To' Trigger.
-- But now the problem happens. Because 'LoopEnd' is not declared yet.
},
flag = {preserved},
}
Trigger { -- Now triggerCount = 3.
players = {P1}
conditions = {
Deaths(P1, Exactly, 64000, "Terran Marine");
}
actions = {
_LinkList(LoopEnd);
-- Reset LoopEnd's next pointer(= LoopEscape) if MarineCount hits 64000
},
flag = {preserved},
}
Portal(LoopEnd); -- LoopEnd = 3.
Changing Order of Triggers will break the Trigger logic(while statement).
All i want is get easy to coding. To put in bluntly, I don't need to solve this problem(get undeclared var). I can imagine a few ways to avoid it. But if i using these ways then the coding work will be very complicated and the difficulty of coding will increases greatly. The difficulty made me stop coding in recent months.
How can I get variables that not declared yet?
Short of time travel, you can't.
Your example code doesn't explain the motivation for the question, because this:
a = WillDeclaredVar(b)
sheepCount = 123
b = sheepCount
print(a)
Can trivially be rearranged into this:
sheepCount = 123
b = sheepCount
a = WillDeclaredVar(b)
print(a)
It would be easier to answer your question if you showed the actual problem you're trying to solve (to avoid an XY problem).
However, as stated there are few things we can note.
First, you need to distinguish between declaring a variable and giving it a value. In Lua you can say:
local b
To declare b as a local variable, which presumably will make a slot for it in the stack frame and let you bind closures to it, before you give it a value. However, the line:
a = WillDeclaredVar(b)
Will pass WillDeclaredVar the value that b currently has, and there's no way for a to change retroactively as a result of b being assigned a new value. That's simply not going to happen, ever. Neither a nor WillDeclaredVar are even aware that b exists, they are receive the value it contains at the point of call.
You could however bind the variable b to a closure which will fetch b's current value when needed.
-- declare b before giving it a value, aka "forward reference"
local b
a = function() return b end
sheepCount = 123
b = sheepCount
print(a()) -- call a to get b's current value
Another way to do that would be to make b a global variable, which is really just a key into your environment table, so you could say:
a = WillDeclaredVar('b')
And have a be some object that can fetch the current value of __ENV['b'] when required.
However, neither of these will support this syntax:
print(a)
a needs to be a function, something that looks up the value of b when needed rather than simply holding a previously computed value. You could do it in this particular instance (i.e. a needs to be convertable to a string), by creating a proxy object that implements __tostring.
function WillDeclaredVar(variableName)
local proxy = { environment = _ENV or _G, variableName = variableName }
return setmetatable(proxy, {
__tostring = function(proxy)
return proxy.environment[proxy.variableName]
end
})
end
-- a will compute a value based on the current value of b when needed
a = WillDeclaredVar('b')
sheepCount = 123
b = sheepCount
print(a)
Output:
123
To make var1 be a reference for var2 write var1 = ReferenceF or var2 (please note a space inside "ReferenceFor"!)
do
local values, references, reference_flag = {}, {}
setmetatable(_G, {
__index = function (_, name)
if name == 'ReferenceF' then
reference_flag = true
elseif reference_flag then
reference_flag = false
return {[references] = name}
elseif references[name] then
return _G[references[name]]
else
return values[name]
end
end,
__newindex = function (_, name, val)
if type(val) == 'table' and val[references] then
references[name] = val[references]
else
values[name] = val
end
end
})
end
a = ReferenceF or b -- a is Reference For b
b = ReferenceF or c -- b is Reference For c
sheepCount = 123
c = sheepCount
print(a, b, c) --> 123 123 123

How to save boolean conditions and evaluate later

I need to create a structure. The structure must contain an array of "boolean conditions". Something like this:
function ReturnStructure ()
local structure = {
{A < 10},
{B == "smth"},
{FunctionReturnsTrueOrFalse(params)},
--...
}
return structure
end
structure = ReturnStructure()
print(structure[1][1]) -- prints true or false depending on the value of A
In fact these tables contain true or false, not conditions, because when we call function ReturnStructure and it creates a local table structure, all conditions in the fields will be executed. I want to create a structure whose fields will contain not boolean values, but something that I can execute (when I want to do it) and get a boolean value. I can achieve this by using anonymous functions:
function ReturnStructure ()
local structure = {
{function() return A < 10 end},
{function() return B == "smth" end},
{FunctionReturnsTrueOrFalse, params}, -- I don't call function in this line, a just put its adress and parameters to table.
--...
}
return structure
end
structure = ReturnStructure()
print(structure[1][1]) -- prints function: 0x109bdd0
print(structure[1][1]()) -- prints true or false. I execute condition in this string.
So, there is a code which works as I want it to, but it seems very ugly.
I want to hear some ideas on how to create a simpler and more beautiful table, without printing function () return ... in every field. I think that I should use a simple OOP implementation to create my structure as an object, but I don't know how to do it. I also will be happy to get some references to methods, implementations, articles etc., which can help me to find some ideas.
I want to hear some ideas on how to create a simpler and more beautiful table, without printing function () return ... in every field.
There aren't. If Lua had C#'s lambda syntax, you could write:
local structure = {
() => A < 10,
() => B == "smth",
() => FunctionReturnsTrueOrFalse(params),
But Lua likes to keep things small and simple, to avoid adding size and complexity to the language and its implementation, so we have one syntax for one function type.
You could store them as strings, then compile and run them later, but that's choosing form over function. You don't want to be invoking the compiler unnecessarily.
local structure = {
'A < 10',
'B == "smth"',
'FunctionReturnsTrueOrFalse(params)',
So your original solution is better. I don't particularly like the way that the first two items defer evaluating their arguments, while your third example evaluates the parameters at compile time. It would be more consistent to treat the FunctionReturnsTrueOrFalse the same
local structure = {
function() return A < 10 end,
function() return B == "smth" end,
function() return FunctionReturnsTrueOrFalse(param1) end,
This also means you don't need to put them in tables. Each is just a function you call, which simplifies the calling code as well.
If you really wanted to evaluate FunctionReturnsTrueOrFalse's arguments at compile time, I'd write a utility routine to build a closure from a function and its arguments:
local function bind(f, ...)
local args = {...}
return function() f(unpack(args)) end
end
Then use that to bind the function to its args:
local structure = {
function() return A < 10 end,
function() return B == "smth" end,
bind(FunctionReturnsTrueOrFalse, param1, param2, param3),
Then everything in your table is simply a function so you don't need special handling
function ReturnStructure ()
local structure = {
{'A < 10'},
{'B == "smth"'},
{FunctionReturnsTrueOrFalse, 'parameter'},
}
local function call_me(f, ...)
return (type(f)=='function' and f or
assert((load or loadstring)('return '..f)))(...)
end
return setmetatable({}, {
__index =
function(t,k)
if structure[k] then
return call_me((table.unpack or unpack)(structure[k]))
end
end,
__newindex = function(t,k,v) structure[k] = v end
})
end
A = 2
B = "anything"
function FunctionReturnsTrueOrFalse(par)
return #par > 5
end
structure = ReturnStructure()
print(structure[1]) -- true
print(structure[2]) -- false
print(structure[3]) -- true
structure[4] = {'1==0'}
print(structure[4]) -- false

Can I extract stack values from a parent function in Lua?

My game engine pushes a value on to the lua stack as a parameter to a function and then invokes it using lua_pcall. The lua code will run and call additional lua functions. Eventually this lua code will invoke a C function. Is it possible for this function to retrieve the value that was originally pushed on to the stack?
Its like this:
<engine function A>
pushes parameter value X on to stack for lua
<lua func>
<lua func>
<lua func>
<engine function B>
can I extract the values X that was pushed by function A here?
Yes, with a combination of getinfo, getlocal and getupvalue you can get all that information (you can even change those values using set* functions).
Here is a fragment from MobDebug that returns stack information along with a table of locals and upvalues at each level. The variables at each level will be indexed in the same order they appear in the code (starting from parameters). For each get* function you can use their C equivalents (lua_getinfo, lua_getlocal, and lua_getupvalue), but the logic should be exactly the same.
local function stack(start)
local function vars(f)
local func = debug.getinfo(f, "f").func
local i = 1
local locals = {}
while true do
local name, value = debug.getlocal(f, i)
if not name then break end
if string.sub(name, 1, 1) ~= '(' then locals[name] = {value, tostring(value)} end
i = i + 1
end
i = 1
local ups = {}
while func and true do -- check for func as it may be nil for tail calls
local name, value = debug.getupvalue(func, i)
if not name then break end
ups[name] = {value, tostring(value)}
i = i + 1
end
return locals, ups
end
local stack = {}
for i = (start or 0), 100 do
local source = debug.getinfo(i, "Snl")
if not source then break end
table.insert(stack, {
{source.name, source.source, source.linedefined,
source.currentline, source.what, source.namewhat, source.short_src},
vars(i+1)})
if source.what == 'main' then break end
end
return stack
end

Resources