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

Loading strings for variable naming

Started by Doyle3694, 14 December 2012 - 06:44 AM
Doyle3694 #1
Posted 14 December 2012 - 07:44 AM
Hello everyone! Out of clear curiosity, none of my programs needs this, I would like to know if there is any way to load a string as a variable name, let's say I make a function in which you pass a desired variable name and then it will make that variable and make it equal to "hello", no practical use there, but would there be any way to do that. I would suspect loadstring(), but that runs a string as a function if im not misstaken?
Orwell #2
Posted 14 December 2012 - 07:55 AM
Indeed, loadstring returns a function. But you can use it like this:

local var = loadstring("return "..varname)()
I would go for a more robust method and use the environment table. Like this:

local var = getfenv()[varname]

Edit: I'm on the phone, so I can't test this.
Doyle3694 #3
Posted 14 December 2012 - 08:39 AM
Can I do

local loadstring(var) = "hello"
?
Orwell #4
Posted 14 December 2012 - 09:30 AM
Can I do

local loadstring(var) = "hello"
?
No, it would still be:

local varname = "blah"
loadstring("return "..varname)() = "hello"
I'm not sure that you can assign return values though, but I think lua supports it.

loadstring puts the text in the body of a new function and returns a reference to that function. So if you do this:

local func = loadstring("return os.version")
local ver = func()	-- now ver holds a reference to the variable os.version
print(ver)
ver = 1     -- reassign a number to ver
It would be translated to:

local func = function() return os.version end
local ver = func()   -- now ver holds a reference to the variable os.version
print(ver)
ver = 1    -- reassign a number to ver

So in short this could be written as:

loadstring("return os.version")() = "hello"
You could also use the global table technique:

getfenv()[varname] = "hello"
GopherAtl #5
Posted 14 December 2012 - 09:45 AM
going out on a limb here, but depending on what you're trying to achieve exactly, a table to store these dynamic variables might be more convenient. Simple program to demonstrate:


--table to hold variables with strings as names
local userVars={}

while true do
	print("enter blank to exit")
	write("variable name >")
	local name=read()
	if name=="" then
	   break
	end
	if userVars[name] then
		print("current value of '"..name.."' is "..userVars[name])
	end
	write("New value:")
	local val=read()
	userVars[name]=val
	print("value set.")
end

print("final values:")
for k,v in pairs(userVars) do
   print(k.."=\""..v.."\"")
end

Again depends on what you're doing, but it can be safer than the global table approach which could end up clobbering things you might not want it to.
Doyle3694 #6
Posted 14 December 2012 - 10:05 AM
Gopher, i know what a table is, Im advanced at lua, my question was very specifically pointed against variables.


To Orwell: Thanks sense :)/>
Doyle3694 #7
Posted 14 December 2012 - 10:12 AM
Oh, would that make

function makeVar(name)
   return name
end
makeVar("lawl") = "herpaderp"
Orwell #8
Posted 14 December 2012 - 10:54 AM
Oh, would that make

function makeVar(name)
   return name
end
makeVar("lawl") = "herpaderp"
No, the function makeVar would just return the same string, not the variable with that name. Here is an example:

function makeVar(varname, var)
  getfenv()['varname']=var
end
makeVar("lawl","herpaderp")
or:

function makeVar(varname, var)
  loadstring("return "..varname)() = var
end
makeVar("lawl","herpaderp")
Lyqyd #9
Posted 14 December 2012 - 11:11 AM
Your first example looks plausible, but your second looks approximately equivalent to the code you're disagreeing with.
Orwell #10
Posted 14 December 2012 - 11:58 AM
Your first example looks plausible, but your second looks approximately equivalent to the code you're disagreeing with.
My second example is quite different from his though. He uses the parameter string as return value. While I use:

loadstring("return "..varname)()
These function calls give a reference to the variable with the name 'varname'. So if you return this, you return a reference to the variable with that name. Like this:

testVar = {"I am a table."}

local function getVar(varname)
  return loadstring("return "..varname)()
end
alias = getVar("testVar")
alias == testVar    -- True
Now the variable 'alias' is the exact same variable as 'testVar'. It's quite clear to me that Doyle's code would just make the variable 'alias' equal the string "testVar".

testVar = {"I am a table."}

local function getVar(varname)
  return varname
end
alias = getVar("testVar")
alias == "testVar"    -- True
Doyle3694 #11
Posted 21 December 2012 - 01:27 AM
Now, a week afterwards, I actually found a use for it, wondering if this code will work:

function varCheck(var, stan)
   if var == nil then					
     loadstring("return "..var)() = stan
   end						  
end									  
ChunLing #12
Posted 21 December 2012 - 01:46 AM
What?

You're concatenating a string and nil, trying to use that as an lua chunk in loadstring, calling the returned function, and setting that function = stan.

If you're trying to produce an error, then I think you've succeeded. But if you had something else in mind then perhaps not.
Doyle3694 #13
Posted 21 December 2012 - 01:59 AM
It was made for checking if a variable is nil, if so, it would just make it equal to the variable passed by the second argument. But now, when you say it, I realise how dumb it looks. though "and setting that function = stan." isn't completely true, the function is called ;)/> I guess I would have to have var passed as a string and then loadstring that in the if statement to check if it's nil? and then my code would(hopefully?) work?

If someone with experience can lead me the way on this one, I would be ever so greatful
Orwell #14
Posted 21 December 2012 - 04:18 AM
I tested this 2 days ago and I discovered that loadstring doesn't have access to the globals. So only the getfenv() method worked for me. I suggest dropping loadstring and using getfenv(). :)/> It's a cleaner method anyway.
Doyle3694 #15
Posted 21 December 2012 - 04:50 AM
OK Orwell, thanks ;)/> Can I get an explanation on getfenv() and it's uses?(Don't like using stuff I don't understand)
PixelToast #16
Posted 21 December 2012 - 05:47 AM
OK Orwell, thanks ;)/> Can I get an explanation on getfenv() and it's uses?(Don't like using stuff I don't understand)
getfenv([f])
Returns the current environment in use by the function. f can be a Lua function or a number that specifies the function at that stack level: Level 1 is the function calling getfenv. If the given function is not a Lua function, or if f is 0, getfenv returns the global environment. The default for f is 1.
Orwell #17
Posted 21 December 2012 - 05:51 AM
getfenv() returns the environment table of the function it's being called from. setfenv( func, tEnv ) sets tEnv as the environment table for the function 'func'.
I'm not sure how much you know of this but the environment table is a table holding all global variables. The table key is the variable name, the value obviously is the variable value. (The main environment table is _G). So, let's take a look at this code:

local tEnv = getfenv()  -- put the environment table for this function in tEnv
local printFunc = tEnv['print'] -- get the value for the key 'print' out of tEnv, it's a function pointer in this case
print( tostring( printFunc == print ) )  -- print the equality of the two function pointers
printFunc( tostring printFunc == print ) ) -- use the function pointer to call print
tEnv['print']("Test!") -- call the function pointer directly on the retreived env. table
getfenv()['print']("Test!") -- call the function pointer directly on the returned env. table from getfenv()

Hmmm, sort of ninja'd by PixelToast. But _G is only the global table and won't always work. (e.g. os.run() gives the loaded program an empty environment table)
PixelToast #18
Posted 21 December 2012 - 05:57 AM
you dont really want to use getfenv though
use
_G[string]=something
or
pcall(setfenv(function() code end,setmetatable({[string]=something},getfenv()))
if you are worried about os.run
Orwell #19
Posted 21 December 2012 - 06:00 AM
you dont really want to use getfenv though
use
_G[string]=something
Won't work in the case of os.run as I mentioned earlier.
Try this as a program:

x = "test"
print( _G["x"] )
That won't work because x won't be put in the global environment table, only in the one that the function has. (an empty one in the case of os.run)
PixelToast #20
Posted 21 December 2012 - 06:01 AM
-snip-
Won't work in the case of os.run as I mentioned earlier
updated post
Orwell #21
Posted 21 December 2012 - 06:15 AM
pcall(setfenv(function() code end,setmetatable({[string]=something},getfenv()))
if you are worried about os.run
That code is not even near flexible. Overriding the metatable of a function's environment table can be unwanted and isn't flexible at all. This code will get longer if you want to define multiple variables. And even then, he wants to access global variables defined somewhere else, not define new ones. And finally, you make a point of not using getfenv and then u use getfenv, setfenv and setmetatable… You can easily define
local G = getfenv()
at the top and use G from there on.
Doyle3694 #22
Posted 21 December 2012 - 06:32 AM
OK, think I got it. will that make

function varCheck(var, stan)
   if var == nil then
	  getfenv()[var] = stan		  
   end							
end	

a working version?
Orwell #23
Posted 21 December 2012 - 06:35 AM
OK, think I got it. will that make

function varCheck(var, stan)
   if var == nil then
	  getfenv()[var] = stan		  
   end							
end	

a working version?
No, you are still indexing a nil value. You wanted to get a variable by it's name, right? Then it would be like this:

function varCheck(varName, stan)
  getfenv()[varName] = getfenv()[varName] or stan
end	
Lyqyd #24
Posted 21 December 2012 - 06:36 AM
I very much doubt that nil is usable as an index.
Doyle3694 #25
Posted 21 December 2012 - 06:39 AM
oh yeah, to tired to think now. Thanks. Understand of all of that very well. Just being an idiot not thinking of it being passed as a string rather than a value.
Orwell #26
Posted 21 December 2012 - 06:40 AM
I very much doubt that nil is usable as an index.
Indeed, it isn't. That's why his function couldn't ever work, but mine would. Just be sure to pass the varName as a string.

Edit: ok, all is done then. :)/>
ChunLing #27
Posted 21 December 2012 - 07:21 AM
You have to admit, you didn't explain that varName was the string matching var's identifier. Though you did call it varName…which…does that count as an explanation? I can't tell.

I mean, I can see a tired Doyle trying to pass tostring(var) into the function as varName, which would be the wrong interpretation but consistent with what you said.
Edited on 21 December 2012 - 06:23 AM
Orwell #28
Posted 21 December 2012 - 07:25 AM
You have to admit, you didn't explain that varName was the string matching var's identifier. Though you did call it varName…which…does that count as an explanation? I can't tell.
I thought it would have been clear from the numerous times I explained that earlier in this thread. I actually had the word string in the sentence earlier, but I couldn't get grammar right so I dropped it. :P/> Anyway, I wasn't clear, he didn't get something, asks for it, and I explain it better. It's how forums work.
ChunLing #29
Posted 21 December 2012 - 08:11 AM
Ideally. But this thread is full of "I shouldn't laugh because some of this is actually pretty difficult, but… :wacko:/> ".

I mean, I know I shouldn't laugh anyway, but sometimes I do.

Anyway, some of this really is just hard to get a handle on, no matter how well you explain it.