I'm writing a Julia macro that takes an expression and serializes it, so that I can run it somewhere else. The macro therefore takes the expression and replaces all symbols with variables. The expression is then serialized and evaluated somewhere else.
My problem is related to evaluating variables that are not in the global scope. I.e. the following works fine, as a is defined in the global scope:
macro myprintf(ex)
print(eval(ex))
end
# works
a = 2
#myprintf a
This throws an error as the macro doesn't see a, which is defined in the local scope of the loop (run in a new session):
macro myprintf(ex)
print(eval(ex))
end
# UndefVarError: a not defined
for j=1:3
a = 2
#myprintf a
end
Is there any way I can access a inside the macro if it is defined in a local scope such as a loop? I'm aware that I'm not necessarily using macros as intended as I am calling eval on the expression inside the macro definition. The overall idea is that I want to serialize the expression that's passed to the macro and evaluate it somewhere else later (e.g. in a different Julia session).
eval only works in global scope. But then I don't see how much use there is in replacing the variables by evaluated literals, resulting in an expression of literals.
Anyway, a different approach to the original problem could be mimick what R does: constructing thunks out of an expression plus its environment at the place where it was called. To recreate that in Julia, you'd have to
Figure out the free variables in the expression
Create an environment data structure of their values
Wrap the original expression in a closure such that it takes the local variables from the environment
Put everything into a new object
Example:
for j=1:3
a = 2
#saveexpr a + 2
end
should expand to something like
for j=1:3
a = 2
SerializedExpr(
(; a),
function (env)
let (a,) = env
a + 2
end
end,
:(a + 2))
end
If saving the closure is not feasible in your use case, I guess you have to either implement your own evaluator, or use something like JuliaInterpreter.jl.
Related
I'm writting a basic event handler in lua which uses some code located in another module
require "caves"
script.on_event({defines.events.on_player_dropped_item}, function(e)
caves.init_layer(game)
player = game.players[e.player_index]
caves.move_down(player)
end
)
but whenever the event is triggered i get following error
attempt to index global 'caves' (a nil value)
why is this and how do i solve it?
You open up the module in question and see what it exports (which global variables are assigned and which locals are returned in the bottom of the file). Or pester the mod author to create interface.
Lua require(filename) only looks up a file filename.lua and runs it, which stands for module initialization. If anything is returned by running the file, it is assigned into lua's (not-so) hidden table (might as well say, a cache of the require function), if nothing is returned but there was no errors, the boolean true is assigned to that table to indicate that filename.lua has already been loaded before. The same true is returned to the variable to the left of equals in the caves = require('caves').
Anything else is left up to author's conscience.
If inside the module file functions are written like this (two variants shown):
init_layer = function(game)
%do smth
end
function move_down(player)
%do smth
end
then after call to require these functions are in your global environment, overwriting your variables with same names.
If they are like this:
local init_layer = function(game)
%do smth
end
local function move_down(player)
%do smth
end
then you won't get them from outside.
Your code expects that the module is written as:
caves = {
init_layer = function(game)
%do smth
end
}
caves.move_down=function(player)
%do smth
end
This is the old way of doing modules, it is currently moved away, but not forbidden. Many massive libraries like torch still use it because you'd end up assigning them to the same named globals anyway.
Кирилл's answer is relevant to newer style:
local caves={
%same as above
}
%same as above
return caves
We here cannot know more about this. The rest is up to you, lua scripts are de-facto open-source anyways.
Addendum: The event_handler is not part of lua language, it is something provided by your host program in which lua is embedded and the corresponding tag is redundant.
You should consult your software documentation on what script.on_event does in this particular case it is likely does not matter, but in general the function that takes another function as argument can dump it to string and then try to load it and run in the different environment without the upvalues and globals that the latter may reference.
require() does not create global table automatically, it returns module value to where you call this function. To access module via global variable, you should assign it manually:
local caves = require "caves"
The variable scope behavior seems quite strange. The code block
tp = 1
function test2()
println(tp)
end
works perfectly well while
function test()
if tp==0
tp=tp-1
end
end
gives the exception "tp not defined". What is wrong?
This is tricky due to the way variables are implicitly defined as local or global, and the fact that definitions later in a function can affect their scoping in the whole function.
In the first case, tp defaults to being a global variable, and it works as you expected. However, in the second case, you assign to tp. This, as is noted in the scope of variables section of the manual:
"An assignment x = y introduces a new local variable x only if x is neither declared global nor introduced as local by any enclosing scope before or after the current line of code."
So, by assigning to tp, you've implicitly declared it as a local variable! It will now shadow the definition of your global… except that you try to access it first. The solution is simple: explicitly declare any variables to be global if you want to assign to them:
function test()
global tp
if tp==0
tp=tp-1
end
end
The behavior here is finely nuanced, but it's very consistent. I know it took me a few reads through that part of the manual before I finally understood how this works. If you can think of a better way to describe it, please say something!
i'd like to know if there is any possibility to read out dynamic variable names?
Since the programm that passes the variables to my script calls them just "in1, in2, in3" etc.
Hopefully there is any way to make a loop, because it is pretty annoying to handle every input separately...
Here is what i've tried so far, but it just gives me an error.
for i=1,19,2 do
myvar[i] = ["in"..i]
end
I'm quite new to Lua, but i hope the solution is not that difficult :D
Edit:
Oh I'll try to give you some more information. The "Main" Program is no not written in Lua and just set theese "in1 ... " variables. It is a kind of robotic programmic software and has a lot of funktions build in. Thats the whole thing so i can not simply use other variable names or an array. So it is not a function or anything else related to Lua...
Here is a little Screenshot http://www.bilderload.com/daten/unbenanntFAQET.jpg
At the moment the Lua script just passes the the first input.
It depends on what you mean by "dynamic variable names."
The names of local variables do not exist. Local variables are any variable declared as a function parameter or with the local keyword. Local variables are compiled into offsets into the Lua stack, so their names don't exist. You can't index something by name to get them.
Global variables are members of the global table. Therefore, these ways to set a global variable are equivalent:
globalVar = 4
_G.globalVar = 4
_G["globalVar"] = 4
Since the programm that passes the variables to my script calls them just "in1, in2, in3" etc.
The program that passes variables to your script doesn't get to name them. A variable is just a placeholder for a value. It has no ownership of that value. When your function gets arguments, your function gets to name them.
You haven't said much about the structure of your program, so I can't really give good advice. But if you just want to take some number of values as parameters and access them as inputs, you can do that in two ways. You can take a table containing values as a parameter, or you can take a varargs:
function MyFunc1(theArgs)
for i, arg in ipairs(theArgs) do
--Do something with arg.
end
end
function MyFunc2(...)
for i, arg in ipairs({...}) do
--Do something with arg.
end
end
MyFunc1 {2, 44, 22} --Can be called with no () because it takes a single value as an expression. The table.
MyFunc2(2, 44, 22)
Whoever wrote the code that spits out these "dynamic variables" didn't do a good job. Having them is a bad habit, and might result in data loss, cluttering of the global name space, ...
If you can change it, it'd be much better to just output a table containing the results.
That said, you're not to far off with your solution, but ["in"..i] is no valid Lua syntax. You're indexing into nothing. If those variables are globals, your code should read:
for i=1,19,2 do
myvar[i] = _G["in"..i]
end
This reads the values contained by your variables out of the global table.
Try this
myvar={ in1, in2, in3, in4, in5, in6, in7, in8, in9, in10, in11,
in12, in13, in14, in15, in16, in17, in18, in19 }
if the variables are passed as global variables, or this
myvar = {...}
if the variables are passed as arguments to the script.
I'm completely confused by Lua's variable scoping and function argument passing (value or reference).
See the code below:
local a = 9 -- since it's define local, should not have func scope
local t = {4,6} -- since it's define local, should not have func scope
function moda(a)
a = 10 -- creates a global var?
end
function modt(t)
t[1] = 7 -- create a global var?
t[2] = 8
end
moda(a)
modt(t)
print(a) -- print 9 (function does not modify the parent variable)
print(t[1]..t[2]) -- print 78 (some how modt is modifying the parent t var)
As such, this behavior completely confuses me.
Does this mean that table variables
are passed to the function by
reference and not value?
How is the global variable creation
conflicting with the already define
local variable?
Why is modt able to
modify the table yet moda is not able
to modify the a variable?
You guessed right, table variables are passed by reference. Citing Lua 5.1 Reference Manual:
There are eight basic types in Lua: nil, boolean, number, string, function, userdata, thread, and table.
....
Tables, functions, threads, and (full) userdata values are objects: variables do not actually contain these values, only references to them. Assignment, parameter passing, and function returns always manipulate references to such values; these operations do not imply any kind of copy.
So nil, booleans, numbers and strings are passed by value. This exactly explains the behavior you observe.
Lua's function, table, userdata and thread (coroutine) types are passed by reference. The other types are passed by value. Or as some people like to put it; all types are passed by value, but function, table, userdata and thread are reference types.
string is also a kind of reference type, but is immutable, interned and copy-on-write - it behaves like a value type, but with better performance.
Here's what's happening:
local a = 9
local t = {4,6}
function moda(a)
a = 10 -- sets 'a', which is a local introduced in the parameter list
end
function modt(t)
t[1] = 7 -- modifies the table referred to by the local 't' introduced in the parameter list
t[2] = 8
end
Perhaps this will put things into perspective as to why things are the way they are:
local a = 9
local t = {4,6}
function moda()
a = 10 -- modifies the upvalue 'a'
end
function modt()
t[1] = 7 -- modifies the table referred to by the upvalue 't'
t[2] = 8
end
-- 'moda' and 'modt' are closures already containing 'a' and 't',
-- so we don't have to pass any parameters to modify those variables
moda()
modt()
print(a) -- now print 10
print(t[1]..t[2]) -- still print 78
jA_cOp is correct when he says "all types are passed by value, but function, table, userdata and thread are reference types."
The difference between this and "tables are passed by reference" is important.
In this case it makes no difference,
function modt_1(x)
x.foo = "bar"
end
Result: both "pass table by reference" and "pass table by value, but table is a reference type" will do the same: x now has its foo field set to "bar".
But for this function it makes a world of difference
function modt_2(x)
x = {}
end
In this case pass by reference will result in the argument getting changed to the empty table. However in the "pass by value, but its a reference type", a new table will locally be bound to x, and the argument will remain unchanged. If you try this in lua you will find that it is the second (values are references) that occurs.
I won't repeat what has already been said on Bas Bossink and jA_cOp's answers about reference types, but:
-- since it's define local, should not have func scope
This is incorrect. Variables in Lua are lexically scoped, meaning they are defined in a block of code and all its nested blocks.
What local does is create a new variable that is limited to the block where the statement is, a block being either the body of a function, a "level of indentation" or a file.
This means whenever you make a reference to a variable, Lua will "scan upwards" until it finds a block of code in which that variable is declared local, defaulting to global scope if there is no such declaration.
In this case, a and t are being declared local but the declaration is in global scope, so a and t are global; or at most, they are local to the current file.
They are then not being redeclared local inside the functions, but they are declared as parameters, which has the same effect. Had they not been function parameters, any reference inside the function bodies would still refer to the variables outside.
There's a Scope Tutorial on lua-users.org with some examples that may help you more than my attempt at an explanation. Programming in Lua's section on the subject is also a good read.
Does this mean that table variables are passed to the function by reference and not value?
Yes.
How is the global variable creation conflicting with the already define local variable?
It isn't. It might appear that way because you have a global variable called t and pass it to a function with an argument called t, but the two ts are different. If you rename the argument to something else, e,g, q the output will be exactly the same. modt(t) is able to modify the global variable t only because you are passing it by reference. If you call modt({}), for example, the global t will be unaffected.
Why is modt able to modify the table yet moda is not able to modify the a variable?
Because arguments are local. Naming your argument a is similar to declaring a local variable with local a except obviously the argument receives the passed-in value and a regular local variable does not. If your argument was called z (or was not present at all) then moda would indeed modify the global a.
I work with scilab, but during a project, scilab has to deal with a large number of variables.
I was wondering if i can do the following
var_list = who_user();
for _var_ = var_list do
if _var_ is global then
writetofile(human_readablefile, _var_)
end
end
clear()
of course this is a pseudocode, and i have a few questions before i implement it.
I can not get var_list = who_user() working. so i believe the function does not return anything. I am reluctant to hack into the code of the "who_user" macro itself. Is there any other way to get the list of user variables in another variable?
Is there a way to find the global variables out of them?
If not, then what are some memory management techniques in scilab?
I am able to answer your first query:
From a slight modification of the who_user function itself:
function nams = who_user1()
//get user variables
[nams,mem]=who('get'); //get all variables
p=predef(); //number of system variable
st=stacksize()
nams=nams(1:$-p+1);mem=mem(1:$-p+1);
//modifiable system variables
excluded=['demolist','scicos_pal','%scicos_menu',..
'%scicos_short','%helps','%helps_modules','MSDOS','who_user','%scicos_display_mode', ...
'%scicos_help'];
ke=grep(nams,excluded)
nams(ke)=[];mem(ke)=[];
n=size(nams,1);
if n==0 then return,end
//format names on n*10 characters
ll=length(nams)+2;m=int((ll-1)/10)+1;
for k=1:max(m)
ks=find(m==k);
if ks<>[] then nams(ks)=part(nams(ks),1:(k*10));end
end
endfunction
This function should give you the list you desire (I have modified the name to who_user1).
You can find out whether a specific variable is global or not by using the isglobal() function, but you need to pass a variable to isglobal(), not the string that is the name of the variable. The function I've listed above returns a vector of strings.
An alternative approach you could try would be to rewrite the above function to return the variables (rather than their names) directly using varargout and then testing them for being globals.