This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
isNaN's profile picture

[Lua][Question] Calling a function from a string.

Started by isNaN, 02 March 2013 - 09:00 AM
isNaN #1
Posted 02 March 2013 - 10:00 AM
Hi, long time stalker, new poster here.
I'm trying to build an array of computers counting items through tubes, reporting to a master server and I've hit a problem I can't seem to be able to fix on my own (lua-users, google, other searches, this forum, etc) I might be asking the question wrong, which is why I can't find what I'm looking for.

I have a background writing OOP in AS3, Java and PHP and I've done quite a bit of scripting (AS2, PHP, JavaScript) but I'm very new to Lua so there's quite a lot I don't grasp yet.

So simplifying my code down to the issue itself (removing irrelevant code, like the double table lookups and the params I want to call with the function etc) I've got something like this:




-- Calling a function from a string, setup and attempt log. (I called the app test)

  -- function and string.

local function foo()
  print("Hello, world!")
end

local str = "foo"

  --Now I attempt to call local str as a function,
  --These are some of my attempts, neither of which works.
  --All commented out, with the thrown error included.

-- loadstring(str)()          --> test:15: attempt to call nil (line 15 is this line)
-- loadstring(str .. "()")()   -->  string:1: attempt to call nil (line 1 is the first comment?!?)
-- _G[str]()				   --> test:17: attempt to call nil (line 17 is this line)
-- _G[str .. "()"]()		  --> test:18: attempt to call nil (line 18 is this line)

-- wits end..


There were more attempts as well, but they all fall back on these techniques.
And I don't understand the error from the second loadstring at all.

Hope some of you can help, thanks! :)/>/>

PS: Not a native English speaker, so I apologize for any grammar errors in advance. :)/>/>
Lyqyd #2
Posted 02 March 2013 - 11:09 AM
Split into new topic.

This is a classic example of asking for x when you need y. In this case, you seem to be trying to use references to functions in order to tell something else which function to call. What you should be doing is passing it that function. Functions in Lua are values just like any other. The name "foo" has no more meaning to that function than it does to a table or string. The reason it's not working is that loaded strings are run in another environment, so cannot access foo. However, the approach you are using is flawed and far more circuitous than you require.


function foo()
  print("Hello World!")
end

function foo_p(...)
  print(...)
end

function bar(func)
  func()
end

function baz(func, ...)
  return func(...)
end

bar(foo) --outputs "Hello World!"
baz(foo_p, "A string") --outputs "A string"
GopherAtl #3
Posted 02 March 2013 - 01:48 PM
Just to add to lyqyd's excellent response, if what you ultimately need to do is run some function based on an actual string, such as one from a rednet message or read(), you have a couple of options. The 2nd and 3rd commented-out method in your code would work if the function foo were not local. Of course, as you probably know since you made foo local, defining a lot of global functions and variables is usually best avoided; you can use a variation of the third commented attempt and make your own local table to hold functions. Example,


--//local table to hold functions
local functionTable={}

--//declare functions as members of the table - DON'T repeat the "local" keyword here! it will error, and is not necessary
--//since foo is being defined inside functionTable, which is already local.
function functionTable.foo()
  --//do stuff
  print("foo!")
end

function functionTable.bar()
  --//do different stuff
  print("bar!")
end

--//read from user
local input=read()
--//see if what they entered is a valid function
if functionTable[input] then
  --//it was, run it
  functionTable[input]()
else
  print("I don't know how to "..input.."!")
end
isNaN #4
Posted 02 March 2013 - 02:51 PM
Thank you Lyqyd and GopherAtl for the quick reply!

So if I understand you correctly we are unable to call functions directly based on a string, completely?

I guess my issue is mostly an OCD problem from an OOP background. (You have no idea how often I've cursed at similar stuff in javascript :P/>)

The resulting string is often a result of a combination of table lookups in my current setup, now I guess I could write my functions in a table, but in my eyes that results in slightly uglier code.. (OCD, i know…)

So if I complicate the beginning code a bit i could have something like this. (This is a working example, for thread-completions sake.)



-- //simplified table-lookup ._call(fn,param) functionality in Lua.

-- //recieved input table from modem (simplified)
local tableCall = {
  [1] = {
	fn	= "foo",
	param = "Hello, world!"
  },
  [2] = {
	fn	= "bar",
	param = "Hey, planet!"
  }
}

-- //this computers key-assoc table
local tableKey = {
  foo = "fooFn",
  bar = "barFn"
}


-- //this computers fn-assoc.table
local tableFunc = {
  fooFn = function(args)
	print("fn: fooFn, param: " .. args)
  end,

  barFn = function(args)
	print("fn: barFn, param: " .. args)
  end
}

-- //this computers app logic (simplified)
for k,v in pairs(tableCall) do
  -- //set params
  local fn	= tableKey[v["fn"]]
  local param = v["param"]
  -- //do the call if it exists in tableFunc
  if tableFunc[fn] then
	tableFunc[fn](param)
  end
end


So, there is no way for me to not have to declare my functions inside a table and still keep the tableCall functionality? (if this is the case, I guess we should put "[solved]" in the topic.)

Also @GopherAtl Neat trick the way you double-comment-out to get the forum code parser to color code the lua-comments properly. I'll keep that in mind if I need to make more code-posts. :)/>
ChunLing #5
Posted 03 March 2013 - 12:00 AM

'K I think I got what you are trying to do, but still don't know why you're trying to do it.
immibis #6
Posted 03 March 2013 - 12:01 AM
Locals can't be accessed by name - you have to refer to them directly. But just pass the function itself, as Lyqyd said.
JokerRH #7
Posted 03 March 2013 - 02:17 AM
So if I understand you correctly we are unable to call functions directly based on a string, completely?

No, you can. You just have to remove your local and then do the following:

function func()
  print("T1")
end

f = "func"
getfenv()[f]()  --> T1

getfenv will return your function enviroment (a table). That excludes local functions!
ChunLing #8
Posted 03 March 2013 - 05:47 PM
Still using a table though…I don't understand the objection to that since everything in Lua is in tables, but what I think may be more useful to know is why it would be necessary to access the function by a string rather than by using the function. Is this to let a user select the functions to run or something?