Using eval in Julia to deal with varargs - eval

I have just started using Julia. I am trying to use eval (in Julia) in order to define a set of variables in a function. Let's say I want to set v1 equal to 2:
function fun_test(varargs...)
v1 = 0;
if length(varargs) > 0
j = collect(linspace(1,length(varargs)-1,length(varargs)/2));
for i in j
expr_vargs = parse("$(varargs[i]) = $(varargs[i+1]);");
eval(expr_vargs);
end
end
println(v1)
end
Calling the function as:
fun_test("v1", "2");
It doesn't work, since println returns 0 (the initial value of v1). However, if I run an analogous eval call in the Julia's terminal, then it works.
Could you please clarify why it doesn't work and how to fix it?

eval runs in toplevel scope, not in function scope. It is not possible to dynamically update bindings in function scope. Without knowing your precise use case, I suspect there is a way to do things without dynamic rebinding. In particular, v1, v2, etc. is probably best made into an array, V.
Nevertheless, if you really must, you can always define v1 as a global variable in a submodule:
module FunTest
v1 = 0
function fun_test(varargs...)
if length(varargs) > 0
j = collect(linspace(1,length(varargs)-1,length(varargs)/2));
for i in j
#eval $(varargs[i]) = $(varargs[i+1])
end
end
println(v1)
end
export fun_test
end
using .FunTest
fun_test(:v1, 2) # result: 2
(I have also modified your code to avoid parseing strings, which is best done through expression interpolation.)

Related

Overriding assignment of value in Lua

I am using Lua v5.2.2 within a C application (embedded environment/MCU).
I need to expose some "parameters" in Lua, that for reading and writing you need to directly access the hardware (thus a C call is needed). I am looking however for other means to implement this than using plain old getters and setters.
I am mostly exploring the meta-programming power of Lua, but also I believe I can create a simpler interface for the user.
What I want to achieve is behaviour like the following:
my_param = createParameter{name="hw_param1", type="number", min=0, max=100}
my_param = 5
result = my_param + 3
On the first line a new parameter is created. This is a call towards a C function. Userdata is pushed to stack with a properly initialized struct. The hardware is also initialized as needed. A new table is returned.
On the second line an assignment is done to the parameter object. I want this to call a C function with a single argument (that of the assignment), so the value can be stored to the hardware registers.
On the third line the parameter is read. I again need a call towards a C function that will get the value of the parameter from the hardware registers, and that will return the result.
Note that the actual value of this parameter may change outside the scope of Lua, so reading the value once during initialization is not correct. The C function must be called each time to get the actual value. Similarly writing to the value must cause an immediate write to the hardware.
How can I accomplish this? Specifically can I alter the metatable of the parameter to achieve lines 2 and 3? (I am aware of how to implement line 1).
Also is it necessary to return a table from the constructor? May I, for example, return a primitive Lua type (e.g. a number) that will behave like above?
Yes, you can modify the metatable metamethods.
Line 2 would completely change the variable's value that it holds.
However, if you were to set a field in the parameter object like: my_param.x = n, the __newindex metamethod would get invoked; which you could overwrite the metamethod to behave as you would like. In your case you would make it set the parameter's field and update it with a C function call.
Regarding line 3, same principle applies, instead this time you would just use the __add metamethod, and manipulate the object when __add is invoked.
http://lua-users.org/wiki/MetamethodsTutorial
This isn't exactly what you're asking for, but it's close:
function createParameter(t)
param = {}
param.data = t
backingTable = {}
metatable = {}
function metatable.__index(t, k)
-- You can intercept the value here if you
-- want and pass it on to your C fuction.
return backingTable[k]
end
function metatable.__newindex(t, k, v)
-- You can intercept the value here if you
-- want and pass it on to your C fuction.
backingTable[k] = v
end
setmetatable(param, metatable)
return param
end
--------------------------------------------------------
my_param = createParameter{name="hw_param1", type="number", min=0, max=100}
my_param.value = 5
result = my_param.value + 3
print(result) -- prints 8
print(my_param.data.name) -- prints hw_param1
You might be able to do something tricky by assigning a metatable to the global table _G, but I think that would be kind of tricky to get set up right and could lead to unexpected outcomes.
Edit:
If you really hate having to have a level of indirection, and you really want to be able to set it directly, here's how you can do it by setting the global table.
globalMetatable = {}
globalParamNames = {}
globalParams = {}
function globalMetatable.__index(t, k)
if globalParamNames[k] then
-- You can intercept the value here if you
-- want and pass it on to your C fuction.
print("Read from param " .. k)
return globalParams[k]
else
rawget(_G, k)
end
end
function globalMetatable.__newindex(t, k, v)
if globalParamNames[k] then
-- You can intercept the value here if you
-- want and pass it on to your C fuction.
print("Wrote to param " .. k)
globalParams[k] = v
else
rawset(_G, k, v)
end
end
setmetatable(_G, globalMetatable)
function createParameter(t)
globalParamNames[t.varname] = true
end
--------------------------------------------------------
createParameter{varname="my_param", name="hw_param1", type="number", min=0, max=100}
my_param = 5
result = my_param + 3
print(result) -- prints 8
print(my_param) -- prints 5

assign values to arrays with eval and symbols in Julia

I have a dictionary that has 3 key, each key has arbitrary 2d arrays. And I want to assign each of these values(2d arrays) to newly created arrays. So I wrote this :
levels = (:easy, :medium, :hard)
easy = []; medium = []; hard = [];
curriculum = Dict((k=>rand(3,3) for k in levels)...); # dictionary with 3 keys - each key is a symbol
for level in levels
eval(level) = curriculum[level]
end
There is no problem during execution but when I check the easy, medium or hard arrays after, there are no matrices inside.
What is the proper way of doing this?
B.R.
(edited)
What I understand is that during the loop iteration eval(level) behaves like local binding. Thus, at the end of the iteration it does not remember anything. But why? And how to convert to global again ?
There is no problem during execution but when I check the easy, medium or hard arrays after, there are no matrices inside.
What I understand is that during the loop iteration eval(level) behaves like local binding. Thus, at the end of the iteration it does not remember anything. But why? And how to convert to global again ?
The problem is that you are defining and then redefining an eval function in each iteration inside the for loop, instead of doing assignments as you believe.
easy = []; medium = []; hard = []
1.- There is no need to predefine this variables.
eval(level) = curriculum[level]
2.- Warning: This is being interpreted as a short style function definition!
julia> eval(level) = curriculum[level]
eval (generic function with 1 method)
julia> levels = :easy, :medium, :hard
(:easy, :medium, :hard)
julia> curriculum = Dict((k => rand(3, 3) for k in levels)...)
Dict{Symbol,Array{Float64,2}} with 3 entries:
:medium => [0.230877 0.244128 0.205712; 0.649405 0.887568 0.957849; 0.245076 0.476088 0.160561]
:hard => [0.0424943 0.157261 0.798607; 0.590535 0.78125 0.704322; 0.555827 0.675355 0.74026]
:easy => [0.715595 0.914093 0.517973; 0.750787 0.489784 0.491666; 0.1966 0.133281 0.249144]
julia> for level in levels
#eval $level = curriculum[$(Meta.quot(level))]
end
julia> easy
3×3 Array{Float64,2}:
0.715595 0.914093 0.517973
0.750787 0.489784 0.491666
0.1966 0.133281 0.249144
3.- Inside the for loop, in the first iteration, $level is interpreted as the easy variable identifier, while $(Meta.quot(level)) is interpreted as the symbol :easy, for which the extra level of quoting is needed.
julia> Meta.quot(:foo)
:(:foo)
julia> Expr(:quote, :foo)
:(:foo)
Edit there is a better way as pointed out in the accepted answer and comment by Ismael. Namely that:
global level = level is not necessary, see my answer, the issue is
that if you want to interpolate a symbol into an expression as a
symbol instead of as an identifier, then you need to wrap the symbol
into another quote expression.
Warning: this is (almost certainly) not the best way of setting out to accomplish whatever you want to do. Metaprogramming in julia is super powerful and useful but you should very rarely need to use eval. You can read more about it here. If you ask another question with the details of your end-goal you might get a better answer :)
Nonetheless:
If you want to do the assignment this way you'll have to create an Expression Expr that assigns the matrix object to the variable with the name you want. But because eval always executes the statement in the global scope you'll need to make the variable you want to use available in the global scope.
for level in levels
global level = level
eval(:($level = curriculum[level]))
end
or using the #eval macro:
for level in levels
global level = level
#eval $level = curriculum[level]
end

Looping over array values in Lua

I have a variable as follows
local armies = {
[1] = "ARMY_1",
[2] = "ARMY_3",
[3] = "ARMY_6",
[4] = "ARMY_7",
}
Now I want to do an action for each value. What is the best way to loop over the values? The typical thing I'm finding on the internet is this:
for i, armyName in pairs(armies) do
doStuffWithArmyName(armyName)
end
I don't like that as it results in an unused variable i. The following approach avoids that and is what I am currently using:
for i in pairs(armies) do
doStuffWithArmyName(armies[i])
end
However this is still not as readable and simple as I'd like, since this is iterating over the keys and then getting the value using the key (rather imperatively). Another boon I have with both approaches is that pairs is needed. The value being looped over here is one I have control over, and I'd prefer that it can be looped over as easily as possible.
Is there a better way to do such a loop if I only care about the values? Is there a way to address the concerns I listed?
I'm using Lua 5.0 (and am quite new to the language)
The idiomatic way to iterate over an array is:
for _, armyName in ipairs(armies) do
doStuffWithArmyName(armyName)
end
Note that:
Use ipairs over pairs for arrays
If the key isn't what you are interested, use _ as placeholder.
If, for some reason, that _ placeholder still concerns you, make your own iterator. Programming in Lua provides it as an example:
function values(t)
local i = 0
return function() i = i + 1; return t[i] end
end
Usage:
for v in values(armies) do
print(v)
end

Can I have an "If" statement in a function parameter?

I'd like to use an IF statement in my VBA code where I'm calling up a Function and passing to it two parameters. The reason I need the IF statement is because the parameter is an array object that I pass to the function. Here's an abbreviation of the code that contains the problem:
For i = 1 To UBound(FilterArray)
For j = LBound(ptsheets, 1) To UBound(ptsheets, 1)
Filter_PivotField_Master _
pvtField:=ThisWorkbook.Worksheets(ptsheets(j, 1)).PivotTables(ptsheets(j, 2)).PivotFields(FilterArray(i, 1)), _
FilterTypeArray:= If FilterArray(i, 2) = 1 Then
InvCodeArray
Else
BoardMonthArray
End If
Next j
Next i
As you can see, I'm looping through the ptsheets array and for each entry, I'm calling up the Filter_PivotField_Master Function. The function requires two inputs (a pivotfield and a string array for the filtering). The array called "FilterArray" simply contains the info needed to pass along to the function. Because I can't seem to store the string array (either InvCodeArray or BoardMonthArray) within the "FilterArray," I wanted to use this IF statement to choose between either array based on if FilterArray(i,2) is equal to "1" or not. Is an "IF" statement option possible here?
Don't do that. Extract a method/procedure out of the inner loop, and parameterize it instead.
For j = LBound(ptsheets, 1) To UBound(ptsheets, 1)
Filter_PivotField_Master _
pvtField:=ThisWorkbook.Worksheets(ptsheets(j, 1)).PivotTables(ptsheets(j, 2)).PivotFields(thePivotField), _
FilterTypeArray:= theFilterType
Next j
Then have the calling code (the outer loop) implement the conditional logic that determines what parameters to give it.
Dim thePivotField
Dim theFilterType
For i = 1 To UBound(FilterArray)
thePivotField = FilterArray(i, 1)
If FilterArray(i, 2) = 1 Then
theFiltertype = InvCodeArray
Else
theFilterType = BoardMonthArray
End If
TheExtractedMethod i, thePivotField, theFilterType
Next i
Your code will be much easier to follow - and to debug.
On a related note, I made a few assumptions just to get your snippet to compile with Option Explicit on (and an IIf instead of the illegal If...Else block), and Rubberduck's last release (v1.4.3) correctly extracted the selected inner loop into its own method:
There are a few known bugs with Rubberduck 1.4.3, and v2.0 is coming soon-ish, but I thought this might interest you. I don't know any other refactoring tools for VBA. Note that Option Explicit is pretty much a hard requirement for this to work, since Rubberduck works off declarations and can't resolve undeclared variables.
Disclaimer: I wrote this refactoring tool.
Use an immediate IF (IIF) for this purpose.
FilterTypeArray = IIF(FilterArray(i, 2) = 1, InvCodeArray, BoardMonthArray)

Defining Table or Array name as name from Previously Defined String in Matlab

So I would like to optimize my code such that I can look through an array such as
{'one','two','three'}
and create corresponding variables defined as tables or arrays
such as
one = table()
two = table()
three = table()
I am aware of the eval function however I would like to use this function in a loop s.t I allocate values to the new variable right after i create it
If I am understanding your question properly, given a cell array consisting only of strings, you wish to create variables in your workspace where each variable is declared as a string using the names from this cell array.
You could use eval, but I'm going to recommend something other than eval. eval should be avoided and instead of iterating those reasons, you can read Loren Shure's article on eval.
In any case, I would recommend you place these variables as dynamic fields inside a structure instead. Do something like this:
s = struct()
strs = {'one', 'two', 'three'};
for idx = 1 : numel(strs)
s.(strs{idx}) = table();
end
In this case, s would be a structure, and you can access the variable by the dot operator. In this case, you can access the corresponding variables by:
d = s.one; %// or
d2 = s.two; %// or
d3 = s.three;
If you want to place this into a function per se, you can place this into a function like so:
function [s] = createVariables(strs)
s = struct();
for idx = 1 : numel(strs)
s.(strs{idx}) = table();
end
This function will take in a cell array of strings, and outputs a structure that contains fields that correspond to the cell array of strings you put in. As such, you'd call it like so:
strs = {'one', 'two', 'three'};
s = createVariables(strs);
However, if you really really... I mean really... want to use eval, you can create your workspace variables like so:
strs = {'one', 'two', 'three'};
for idx = 1 : numel(strs)
eval([strs{idx} ' = table();']);
end
To place this into a function, do:
function [] = createVariables(strs)
for idx = 1 : numel(strs)
eval([strs{idx} ' = table();']);
end
However, be warned that if you run the function above, these variables will only be defined in the scope that the function was run in. You will not see these variables when the function exits. If you want to run a function so that the variables get defined in the workspace after you run the function, then eval is not the right solution for you. You should thus stick with the dynamic field approach that I talked about at the beginning of this post.
In any case, this will create one, two and three as workspace variables that are initialized to empty tables. However, I will argue with you that the first line of code is easier to read than the second piece of code, which is one of the main arguing points as to why eval should be avoided. If you stare at the second piece of code long enough, then you can certainly see what we're trying to achieve, but if you read the first piece of code, you can ascertain its purpose more clearly.

Resources