While I don't really know Lua, this is a rather unexpected and bizarre behavior.
Let's say I have my_module.lua:
local function dump(o) -- SO:9168058
if type(o) == 'table' then
local s = '{ '
for k,v in pairs(o) do
if type(k) ~= 'number' then k = '"'..k..'"' end
s = s .. '['..k..'] = ' .. dump(v) .. ','
end
return s .. '} '
else
return tostring(o)
end
end
local mymodule = {}
function mymodule.myfunction(indict)
print(dump(indict))
end
return mymodule
Ok, now I run this:
lua5.3 -e "mm=require('my_module'); mm:myfunction({aa=12})"
This should not be complicated - I "import" the module, and call a function in it with an argument that is an object (the table/associative array/dictionary {aa=12}). Then I simply try to print this argument from within the function. However, I get this:
$ lua5.3 -e "mm=require('my_module'); mm:myfunction({aa=12})"
{ ["myfunction"] = function: 0x5619aeddf770,}
So, the response to print(dump(indict)) inside myfunction, where indict is an argument passed to myfunction, Lua prints .... "myfunction" ????!
I cannot even wrap my head around this - how can this possibly happen ?
And how can I pass an object as an argument to a function, such that when I print the argument from within the function, the object which is the argument is printed - and not the function itself ??!
Btw, the same happens even if I just pass a number instead of an object, say:
lua5.3 -e "mm=require('my_module'); mm:myfunction(42)"
EDIT: did a bit of debugging - so with this:
function mymodule.myfunction(indict)
print(indict)
print(dump(indict))
end
... I get this printout when using a numeric argument:
$ lua5.3 -e "mm=require('my_module'); mm:myfunction(42)"
table: 0x55f15a7a07a0
{ ["myfunction"] = function: 0x55f15a7a07e0,}
... so it simply doesn't see this number anywhere, but the function sees itself as the first argument.
This reminded me, how in a Python class, you have to write methods as functions with "self" as first argument, so I tried this:
function mymodule.myfunction(self, indict)
print("self", self, "indict", indict)
print(dump(indict))
end
... which prints:
$ lua5.3 -e "mm=require('my_module'); mm:myfunction(42)"
self table: 0x560510b5a7d0 indict 42
42
... or in case of passing an obect:
$ lua5.3 -e "mm=require('my_module'); mm:myfunction({aa=12})"
self table: 0x55d51c9d5800 indict table: 0x55d51c9d5880
{ ["aa"] = 12,}
... well, that's more like it ...
Can anyone explain where does this come from - why do I need to add a "self" argument in this case?
In lua, the call a:b(x) passes a reference to the object a as the first (self) parameter to the function b.
Since your module definition is:
function mymodule.myfunction(indict)
and the call statement is mm:myfunction, the object/table mm is passed as the first parameter (which here is indict).
Either change the function definition to
function mymodule:myfunction(indict)
if you want to keep call like mm:myfunction, or call the function as mm.myfunction.
The behaviour is discussed in detail in the PiL book on OOP concepts.
The effect of the colon is to add an extra hidden parameter in a
method definition and to add an extra argument in a method call. The
colon is only a syntactic facility, although a convenient one; there
is nothing really new here.
Related
I have the following code:
dofile(arg[1])
function1 = arg[2]
argument = arg[3]
returned = _G[function1](argument)
print(returned)
It is designed to take three command-line arguments and run a function from a file.
So, i run the command lua libs.lua "printStuff.lua" "printStuff" "\"Hello, World\"", and i always end up with this:
"Hello, World"
nil
I don't understand why i always get "nil".
Here are the contents of printstuff.lua:
function printStuff(stuff)
print(stuff)
end
That is to be expected. What's going on here:
You're executing the file specified by the first argument, printstuff.lua, which will leave a function printStuff in the global table _G.
You're indexing the global table with the second argument, printStuff, obtaining that function
You're calling the function you just obtained with the third command line argument, "Hello World!", as parameter, which prints it, and storing the result of that in the global variable returned. The function printStuff doesn't return anything (there's no return in there, and even if there was, print doesn't return anything either), so you're assigning nil to returned.
You're printing returned, which is nil
Side note: I'd use the vararg ... instead of the arg table for improved readability:
local file, func, param = ...
dofile(file); print(func(param))
Why not simply...
-- xlua.lua
-- Example: lua xlua.lua os date "%H:%M:%S"
-- Or: lua xlua.lua _G print "Hello World"
-- Or: lua xlua.lua dofile /path/to/someusefull.lua "Arg for someusefull.lua"
local result = _G[arg[1]][arg[2]](arg[3])
-- 5. Only put out whats not nil
if (result ~= nil) then
print(result)
end
So i'm making my own OS for LIKO-12 and I need to run .lk12 files but i need to have a function to run a string as code and return a table that has all of the variables that code created
Example:
function foo()
return "Hello,"
end
function bar()
return " World!"
end
hello = foo()..bar()
should return a table with 2 functions:
A function called foo that returns "Hello,"
A function called bar that returns " World!"
and a variable called hello that has "Hello, World!"
You should be able to call a function from the code using
vars["foo"]() or vars.foo()
Can anyone help me?
When you create or use a global variable it's actually stored in a table called "the environment", or _ENV (which is not global, it's a local that you get automatically)
So your code:
function foo()
return "Hello,"
end
function bar()
return " World!"
end
hello = foo()..bar()
is really doing:
function _ENV.foo()
return "Hello,"
end
function _ENV.bar()
return " World!"
end
_ENV.hello = _ENV.foo().._ENV.bar()
so if we just set _ENV to a new table, and return it, then this is exactly the table you want. You can do this with load (which is how you run strings) by passing it as the 4th argument. This function will do that:
function loadAndReturnEnv(codeString)
local env = {}
load(codeString, nil, nil, env)() -- error checking skipped for demonstration
return env
end
However, note that all the usual global stuff like string.rep, table.sort, load, print don't exist in this new _ENV. If that's what you want, that's great. But I guess you probably do want them. In that case we can use the metatable __index feature, so that if the code looks for something in its _ENV and it's not there, it looks in our (the caller's) _ENV instead.
function loadAndReturnEnvWithGlobals(codeString)
-- note: env is the environment for the code we're about to run
-- (which will be called _ENV in that code), and _ENV is the environment
-- of the loadAndReturnEnvWithGlobals function (i.e. the "real" environment)
local env = {}
setmetatable(env, {__index=_ENV})
load(codeString, nil, nil, env)() -- error checking skipped for demonstration
setmetatable(env, nil) -- set the table back to normal before we return it
return env
end
But wait... the program can now call print when you run it, but if it defines a function, that function can't call print since we deleted the link back to the real environment after the code returned. So I think the best way to fix that is to leave the environment linked, and just copy the functions etc.
function loadAndReturnEnvWithGlobals2(codeString)
local env = {}
setmetatable(env, {__index=_ENV})
load(codeString, nil, nil, env)() -- error checking skipped for demonstration
local result = {}
for k,v in pairs(env) do result[k]=v end
return result
end
although this copies the values, so if you have any functions that change the variables, you won't see the changed variables.
Which version you use is up to you. They each have pros and cons.
Lets take a look at this pseudo code example:
-- Wraps a function with given parameters inside a callback
-- Usefull for sending class methods as callbacks.
-- E.g. callback(object.method, object)
local function callback(func, ...)
return function(...)
func(..., ...)
end
end
How would I go about this?
I know there is unpack, but it would get swallowed by the second vararg:
local function callback(func, ...)
local args = { ... }
return function(...)
func(table.unpack(args), ...)
end
end
callback(print, "First", "Second")("Third") --> prints First Third
The only option I found so far, is to concat those together and then unpack it:
local function callback(func, ...)
local args1 = { ... }
return function(...)
local args2 = { ... }
local args = { }
for _, value in ipairs(args1) do
table.insert(args, value)
end
for _, value in ipairs(args2) do
table.insert(args, value)
end
func(table.unpack(args))
end
end
Is this the only solution, or could I do better?
What I would like to have is either a function, that concats two arrays together (which should be faster than those two for loops) and then use table.unpack on it, or make those varargs concat.
From the Lua 5.4 Reference Manual: 3.4 Expressions
If an expression is used as the last (or the only) element of a list
of expressions, then no adjustment is made (unless the expression is
enclosed in parentheses). In all other contexts, Lua adjusts the
result list to one element, either discarding all values except the
first one or adding a single nil if there are no values.
So the only way is to manually combine both lists to a single one befor you use them.
There are several ways to do this.
for i,v in ipairs(list2) do
table.insert(list1, v)
end
for i,v in ipairs(list2) do
list1[#list1+i] = v
end
table.move(list2, 1, #list2, #list1 + 1, list1)
I'm not sure what kind of problem you're actually trying to solve here.
If you want to access your object from its methods use self.
-- Wraps a function with given parameters inside a callback
-- Usefull for sending class methods as callbacks.
-- E.g. callback(object.method, object)
Usually you would do something like this:
local callback = function(params) object:method(params) end
callback(params)
Instead of
callback(print, "First", "Second")("Third")
You could do this
local callback = function (...) print("First", "Second", ...) end
callback("Third")
Edit ( opinonated )
My main goal is to use member functions as callbacks. However, I'd
like to keep it general. The advantage of a callback function is to
omit the function and end keyword. It's shorter and looks closer to
what it would look like, if there would be no need for a self
argument. So this: object.event = function(...) return
self:method(...) end would become to this: object.event =
callback(self.method, self) what would be even nicer, is this (sadly
not possible in lua) object.event = self:method
So instead of
RegisterCallback(function() obj:method() end)
You say it is easier to do this, so you don't have to write function end?
local function callback(func, ...)
local args1 = { ... }
return function(...)
local args2 = { ... }
local args = { }
for _, value in ipairs(args1) do
table.insert(args, value)
end
for _, value in ipairs(args2) do
table.insert(args, value)
end
func(table.unpack(args))
end
end
RegisterCallback(callback(obj.method, obj)())
Your approach is not going to work if any of the arguments is nil to begin with. And there is not a single advantage. You're just typing other words while increasing the chance of running into errors.
You can control the param order to optimize your function.
local function callback(func, ...)
local args = {...}
return function(...)
func(..., table.unpack(args))
end
end
callback(print, "second", "third")("first") -- prints first second third
I'm creating a lua script that should run on the TI-Nspire calculator. The problem is that while running my script I get the error Attempt to index local 'self' (a nil value) when the button:activate() method is called. The parser says the error is in the 8th line in the code below. The problematic code is as follows:
button = class(view)
function button:init()
self.selected = false
end
function button:activate()
self.selected = true
end
I call the activate function like this:
item = button()
local action = "activate"
local arguments = {}
item[action](unpack(arguments))
I am aware the class() function doesn't exist in "stock" Lua, it's a function available in the TI-Nspire Lua implementation. You can find its definition and usage here.
obj:methodname(args) is sugar for obj.methodname(obj,args). So, if you want to use the syntax item[action](unpack(arguments)), you need to use item[action](item,unpack(arguments)). Otherwise, try item:activate(unpack(arguments)) if you can use method explicitly.
I am taking my first steps programming in Lua and get this error when I run my script:
attempt to index upvalue 'base' (a function value)
It's probably due to something very basic that I haven't grasped yet, but I can't find any good information about it when googling. Could someone explain to me what it means?
In this case it looks base is a function, but you're trying to index it like a table (eg. base[5] or base.somefield).
The 'upvalue' part is just telling you what kind of variable base is, in this case an upvalue (aka external local variable).
One "local" too many?
As Mike F explained, an "upvalue" is an external local variable. This error often occurs when a variable has been declared local in a forward declaration and then declared local again when it is initialized. This leaves the forward declared variable with a value of nil. This code snippet demonstrates what not to do:
local foo -- a forward declaration
local function useFoo()
print( foo.bar ) -- foo is an upvalue and this will produce the error in question
-- not only is foo.bar == nil at this point, but so is foo
end
local function f()
-- one LOCAL too many coming up...
local foo = {} -- this is a **new** foo with function scope
foo.bar = "Hi!"
-- the local foo has been initialized to a table
-- the upvalue (external local variable) foo declared above is not
-- initialized
useFoo()
end
f()
In this case, removing the local in front of foo when it is initialized in f() fixes the example, i.e.
foo = {}
foo.bar = "Hi!"
Now the call to useFoo() will produce the desired output
Hi!