Access fields of struct using a macro - macros

I would like to have a macro that is similar to the #. macro that converts every variable in an expression into a field of a struct. For example x = y+z should become s.x = s.y + s.z where sis an instance of a struct s = S(1,2,3) with
struct S
x::Int
y::Int
z::float
end
I have found something similar in this question
use macro to extract several object fields in Julia
However it does not seem to work in my version of julia for some reason.

Accessing a struct's properties
The package PropertyUtils.jl has a #with macro that allows you do to this for value access:
julia> struct S
x::Int
y::Int
z::Float64
end
julia> s = S(1, 42, 3.14)
S(1, 42, 3.14)
julia> using PropertyUtils
julia> #with s begin
x * y - z
end
38.86
Using non-struct-member variables
If the expression contains a variable name that's not a property of the struct, then it's automatically treated as a normal variable.
julia> n = 100
100
julia> #with s begin
x * y - z + n
end
138.86
Modifying the struct members
However, you can't assign to the struct's fields using this. x = y + z will define a new variable called x and assign to it, not to s.x.
You can instead return the values you want to be assigned to the struct's fields from the #with block, and assign to them from that:
julia> mutable struct MS
x::Int
y::Int
z::Float64
end #we need a mutable struct as we want to assign to its fields
julia> mut = MS(1, 42, 3.14)
MS(1, 42, 3.14)
julia> mut.z, mut.x = #with mut let
_z = x * y - z + n
_x = round(y + z)
(_z, _x)
end
(138.86, 45.0)
julia> mut
MS(45, 42, 138.86)

Related

julia metaprogramming and nloops variable evaluation

I am a noob at metaprogramming so maybe I am not understanding this. I thought the purpose of the #nloops macro in Base.Cartesian was to make it possible to code an arbitrary number of nested for loops, in circumstances where the dimension is unknown a priori. In the documentation for the module, the following example is given:
#nloops 3 i A begin
s += #nref 3 A i
end
which evaluates to
for i_3 = 1:size(A,3)
for i_2 = 1:size(A,2)
for i_1 = 1:size(A,1)
s += A[i_1,i_2,i_3]
end
end
end
Here, the number 3 is known a priori. For my purposes, however, and for the purposes that I thought nloops was created, the number of nested levels is not known ahead of time. So I would not be able to hard code the integer 3. Even in the documentation, it is stated:
The (basic) syntax of #nloops is as follows:
The first argument must be an integer (not a variable) specifying the number of loops.
...
If I assign an integer value - say the dimension of an array that is passed to a function - to some variable, the nloops macro no longer works:
b = 3
#nloops b i A begin
s += #nref b A i
end
This returns an error:
ERROR: LoadError: MethodError: no method matching _nloops(::Symbol, ::Symbol, ::Symbol, ::Expr)
Closest candidates are:
_nloops(::Int64, ::Symbol, ::Symbol, ::Expr...) at cartesian.jl:43
...
I don't know how to have nloops evaluate the b variable as an integer rather than a symbol. I have looked at the documentation and tried various iterations of eval and other functions and macros but it is either interpreted as a symbol or an Expr. What is the correct, julian way to write this?
See supplying the number of expressions:
julia> A = rand(4, 4, 3) # 3D array (Array{Int, 3})
A generated function is kinda like a macro, in that the resulting expression is not returned, but compiled and executed on invocation/call, it also sees the type (and their type parameters of course) of the arguments, ie:
inside the generated function, A is Array{T, N}, not the value of the array.
so T is Int and N is 3!
Here inside the quoted expression, N is interpolated into the expression, with the syntax $N, which evaluates to 3:
julia> #generated function mysum(A::Array{T,N}) where {T,N}
quote
s = zero(T)
#nloops $N i A begin
s += #nref $N A i
end
s
end
end
mysum (generic function with 1 method)
julia> mysum(A)
23.2791638775186
You could construct the expression and then evaluate it, ie.:
julia> s = 0; n = 3;
julia> _3loops = quote
#nloops $n i A begin
global s += #nref $n A i
end
end
quote
#nloops 3 i A begin
global s += #nref(3, A, i)
end
end
julia> eval(_3loops)
julia> s
23.2791638775186
I have scrubbed manually the LineNumberNodes from the AST for readability (there is also MacroTools.prettify, that does it for you).
Running this example in the REPL needs to declare s as global inside the loop in Julia 1.0.

Hash function for custom type

I'm trying to define a custom equality method for a new type I've constructed. This is a MWE getting at what I'm trying to do.
mutable struct a
first_num::Int
second_num::Int
end
import Base.==
import Base.hash
function hash(obj::a, h=33141651)
return hash((obj.first_num, obj.second_num), h)
end
function ==(obj1::a, obj2::a)
if hash(obj1) == hash(obj2)
return true
else
return false
end
end
a1 = a(2,3)
a2 = a(2,3)
a1 == a2
I then get an error like ERROR: TypeError: ==: in typeassert, expected UInt64, got Int64
Is h becoming Int64?
In addition, if hashing the tuple of attributes is simply not the correct way to do this, let me know.
Edit: Actually, I ran this and I'm getting MethodError: no method matching hash(::Tuple{Int64,Int64}, ::Int64). Is h being promoted to Int64?
The problem is that your literal value for h (33141651) is an Int rather than a UInt. Thus, when you call hash with the tuple h is an Int, but the internal tuple hash function expects a UInt. I don't think you have to specify a value for h at all, and something like this should be enough:
function Base.hash(obj::a, h::UInt)
return hash((obj.first_num, obj.second_num), h)
end
Full example for completeness:
mutable struct A
first::Int
second::Int
end
function Base.hash(obj::A, h::UInt)
return hash((obj.first, obj.second), h)
end
function Base.:(==)(obj1::A, obj2::A)
return hash(obj1) == hash(obj2)
end
With the following behaviour
julia> a = A(2,3); b = A(2,3)
A(2, 3)
julia> hash(a)
0x965b43497b212144
julia> hash(b)
0x965b43497b212144
julia> a == b
true

How to add, subtract, etc. two structs element-by-element when they have the same fields

I have various structs with fields W, P, E, which contain numerical values. I want to develop a clean way to add and subtract these structs without unpacking and repacking in subfunctions each time (which has been my solution thus far)
For example, given:
S.W = 2
S.P = 3
S.E = 4
M.W = 20
M.P = 30
M.E = 40
I want to be able to do X = S + M and end up with:
X.W = 22
X.P = 33
X.E = 44
My current attempt to do this, is by means of a new class, which looks as follows:
classdef CV
properties
W
P
E
end
methods
function r = plus(o1,o2)
r = CV;
r.E = o1.E + o2.E;
r.P = o1.P + o2.P;
r.W = o1.W + o2.W;
end
end
end
This allows for doing S + M and returns a new variable in the same form as the inputs. I'm generally unfamiliar with classes, and wanted to know if this is proper form. If so, I might go ahead and add functions for minus and times in the methods section. However, this seems like it requires a lot of repetitive code and I feel there must be a simpler solution. Any advice is much appreciated.
The following code directly works on structs without nesting them in a class. It is assumed that the two input structs have the same field names (in this example W, P, and E), however, the order of those may be arbitrary (you mentioned in a comment that this is important for your application).
function X = structplus(S, M)
fn = fieldnames(S);
for i = 1 : numel(fn)
X.(fn{i}) = M.(fn{i}) + S.(fn{i});
end
end
So defining
S.W = 2
S.P = 3
S.E = 4
M.E = 40
M.P = 30
M.W = 20
(note the reverse order of M) and calling
X = structplus(S, M)
yields a struct with field names that are ordered like the first argument:
X =
struct with fields:
W: 22
P: 33
E: 44
To expand on Le Phlaux's answer, you can provide a function handle to the required binary operator (e.g. #plus, #minus) and work on sub-structures recursively
function out = structBinaryFunc(in1, in2, func)
fn = fieldnames(in1);
for ii = 1:numel(fn)
if isstruct(in1.(fn{ii}))
out.(fn{ii}) = structBinaryFunc(in1.(fn{ii}), in2.(fn{ii}), func)
else
out.(fn{ii}) = func(in1.(fn{ii}), in2.(fn{ii}));
end
end
For your example you would call X = structBinaryFunc(S, M, #plus);.

using a name macro for variables from a functions inputs

If I have 2 variables, a=[.3, .2, .4]; b=[.1, .2, .3]; I can create a string with the name of the variable using a macro:
macro varname(arg)
string(arg)
end
#varname(a)
now say I have a function and I want to pass it an arbitrary number of arguments and use the actual variable names that are being given to function to create dictionary keys:
function test(arguments...)
Dict(Symbol(#varname(i)) => i for i in arguments)
end
this won't work because #varname will take i and create "i", so for example:
out=test(a,b)
the output I would like is:
Dict("a" => [.3, .2, .4], "b" => [.1, .2, .3])
Is there a way to achieve this behavior?
Parameters.jl has such a macro. It works like this:
using Parameters
d = Dict{Symbol,Any}(:a=>5.0,:b=>2,:c=>"Hi!")
#unpack a, c = d
a == 5.0 #true
c == "Hi!" #true
d = Dict{Symbol,Any}()
#pack d = a, c
d # Dict{Symbol,Any}(:a=>5.0,:c=>"Hi!")
If you want to know how it's done, just check its source:
https://github.com/mauro3/Parameters.jl/blob/v0.7.3/src/Parameters.jl#L594

Call by reference, value, and name

I'm trying to understand the conceptual difference between call by reference, value, and name.
So I have the following pseudocode:
foo(a, b, c)
{
b =b++;
a = a++;
c = a + b*10
}
X=1;
Y=2;
Z=3;
foo(X, Y+2, Z);
What's X, Y, and Z after the foo call if a, b, and c are all call by reference?
if a, b, and c are call-by-value/result?
if a, b, and c are call-by-name?
Another scenario:
X=1;
Y=2;
Z=3;
foo(X, Y+2, X);
I'm trying to get a head start on studying for an upcoming final and this seemed like a good review problem to go over. Pass-by-name is definitely the most foreign to me.
When you pass a parameter by value, it just copies the value within the function parameter and whatever is done with that variable within the function doesn't reflect the original variable e.g.
foo(a, b, c)
{
b =b++;
a = a++;
c = a + b*10
}
X=1;
Y=2;
Z=3;
foo(X, Y+2, Z);
//printing will print the unchanged values because variables were sent by value so any //changes made to the variables in foo doesn't affect the original.
print X; //prints 1
print Y; //prints 2
print Z; //prints 3
but when we send the parameters by reference, it copies the address of the variable which means whatever we do with the variables within the function, is actually done at the original memory location e.g.
foo(a, b, c)
{
b =b++;
a = a++;
c = a + b*10
}
X=1;
Y=2;
Z=3;
foo(X, Y+2, Z);
print X; //prints 2
print Y; //prints 5
print Z; //prints 52
for the pass by name;
Pass-by-name
Call by Value : normal way... values of actual parameters are copied to formal parameters.
Call by Reference : instead of the parameters, their addresses are passed and formal parameters are pointing to the actual parameters.
Call by Name : like macros, the whole function definition replaces the function call and formal parameters are just another name for the actual parameters.
By value - there is no changes out the function. all your actions vanish when the function finished.
By reference - your actions indeed changes the variables.
By name - I've never heard ...
Passing x+1 is not change, just tells to the function 3 instead 2 or etc...
This won't change the value of X, Y or Z if it is pass-by-value. When you use a function such as "foo()", it basically copies the variables (x, y and z) into other variables (a, b, and c) and does certain actions with them, without changing the originals (x, y and z). For you to change a value you would have to return a value, something like this:
foo(a, b, c)
{
a = a++;
b = b++;
c = a + b * 10;
return c;
}
x = 1;
y = 2;
z = 3;
z = foo(x, y+2)
Then x and y would be the same, but z would be (x+1)+(y+1)*10 which in this case would be 32.
in javascript :
primitive type variable like string,number are always pass as pass
by value.
Array and Object is passed as pass by reference or pass by value based on these condition.
if you are changing value of that Object or array with new Object or Array then it is pass by Value.
object1 = {item: "car"};
array1=[1,2,3];
here you are assigning new object or array.you are not changing the value of property
of old object.so it is pass by value.
if you are changing a property value of an object or array then it is pass by Reference.
object1.item= "car";
array1[0]=9;
here you are changing a property value of old object.you are not assigning new object or array to old one.so it is pass by reference.
Code
function passVar(object1, object2, number1) {
object1.key1= "laptop";
object2 = {
key2: "computer"
};
number1 = number1 + 1;
}
var object1 = {
key1: "car"
};
var object2 = {
key2: "bike"
};
var number1 = 10;
passVar(object1, object2, number1);
console.log(object1.key1);
console.log(object2.key2);
console.log(number1);
Output: -
laptop
bike
10
In Call by value, a copy of the variable is passed whereas in Call by reference, a variable itself is passed. In Call by value, actual and formal arguments will be created in different memory locations whereas in Call by reference, actual and formal arguments will be created in the same memory location.