I am trying to write a Julia macro that transforms this:
[par1!( par2(d1,d2)+par3(d1,d2) ,d1,d2,dfix3) for d1 in DIM1, d2 in DIM2]
(not very inspiring) into something much more readable, like this:
#meq par1!(d1 in DIM1, d2 in DIM2, dfix3) = par2(d1,d2)+par3(d1,d2)
where par1!() is a function to set some multi-dimensional data and par2() is a getData()-type of function.
I am trying to implement it using a macro, but as I am on my first experience with julia marcro, I'm not sure how to "assemble" the final expression from the various pieces..
Here is what I done so far:
macro meq(eq)
# dump(eq)
lhs_par = eq.args[1].args[1]
rhs = eq.args[2]
lhs_dims = eq.args[1].args[2:end]
loop_counters = [d.args[2] for d in lhs_dims if typeof(d) == Expr]
loop_sets = [d.args[3] for d in lhs_dims if typeof(d) == Expr]
loop_wholeElements = [d for d in lhs_dims if typeof(d) == Expr]
lhs_dims_placeholders = []
for d in lhs_dims
if typeof(d) == Expr
push!(lhs_dims_placeholders,d.args[2])
else
push!(lhs_dims_placeholders,d)
end
end
outExp = quote
[$(lhs_par)($(rhs),$(lhs_dims_placeholders ...)) for $(loop_wholeElements ...) ]
end
#show(outExp)
return outExp
end
However the above macro doesn't compile and returns a syntax error (“invalid iteration specification”) due to the for $(loop_wholeElements) part… indeed I don’t know how to treat the expressions in lhs_dims_placeholders and loop_wholeElements in order to “assemble” the expanded expression…
EDIT:
The example posted, with d1, d2 and dfix3, is only a specific case, but the macro should be able to handle whichever dimensions are looped for..
I think the macro up there does that, but I don't know how to build the final expression.. :-(
Instead of manually doing those hard-coded args matching stuff, we could use MacroTools.jl as a handy tool for template matching:
julia> using MacroTools
julia> macro meq(ex)
#capture(ex, f_(d1_ in dim1_, d2_ in dim2_, dfix3_) = body__)
ret = :([$f($(body[]), $d1, $d2, $dfix3) for $d1 in $dim1, $d2 in $dim2])
end
#meq (macro with 1 method)
julia> prettify(#macroexpand #meq par1!(d1 in DIM1, d2 in DIM2, dfix3) = par2(d1,d2)+par3(d1,d2))
:([(Main.par1!)((Main.par2)(lobster, redpanda) + (Main.par3)(lobster, redpanda), lobster, redpanda, Main.dfix3) for lobster = Main.DIM1, redpanda = Main.DIM2])
UPDATE:
The desired final expression is a comprehension, it seems that for some reason Julia couldn't figure out for expr(where $expr #=> XXX in XXX) is a comprehension. The workaround is directly using its lowered form:
julia> using MacroTools
julia> par1(a, b, c, d) = a + b + c + d
par1 (generic function with 1 method)
julia> par2(a, b) = a + b
par2 (generic function with 1 method)
julia> macro meq(ex)
#capture(ex, par_(dims__) = rhs_)
loopElements = []
dimsPlaceholders = []
for d in dims
#capture(d, di_ in DIMi_) || (push!(dimsPlaceholders, d); continue)
# push!(loopElements, x)
push!(loopElements, :($di = $DIMi))
push!(dimsPlaceholders, di)
end
ret = Expr(:comprehension, :($par($(rhs),$(dimsPlaceholders...))), loopElements...)
end
#meq (macro with 1 method)
julia> prettify(#macroexpand #meq par1!(d1 in DIM1, d2 in DIM2, dfix3) = par2(d1,d2)+par3(d1,d2))
:($(Expr(:comprehension, :((Main.par1!)(begin
(Main.par2)(bee, wildebeest) + (Main.par3)(bee, wildebeest)
end, bee, wildebeest, Main.dfix3)), :(bee = Main.DIM1), :(wildebeest = Main.DIM2))))
julia> #meq par1(m in 1:2, n in 4:5, 3) = par2(m,n) + par2(m,n)
2×2 Array{Int64,2}:
18 21
21 24
Note that, the variable scope of d1,d2 in generated expression will be wrong if we use push!(loopElements, x) rather than push!(loopElements, :($di = $DIMi)). Let's wait for someone knowledgeable to give a thorough explanation.
If you do not want to rely on an external package for this, the solution I provided on the Julia discourse should also work
return :([$(Expr(:generator,:($(Expr(:call,lhs_par,rhs,lhs_dims_placeholders...))),loop_wholeElements...))])
The key is to use the :generator constructor to make the loop expression
Also, rhs can be replaced with rhs.args[n] in order to eliminate the quote block and insert the expression directly.
This then produces the exact expression:
:([(par1!(par2(d1, d2) + par3(d1, d2), d1, d2, dfix3) for d1 in DIM1, d2 in DIM2)])
EDIT:
Alright, so I went ahead and tested this:
return Expr(:comprehension,Expr(:generator,Expr(:call,lhs_par,rhs.args[2],lhs_dims_placeholders...),loop_wholeElements...))
end
Then computing the result like this
meq(:(par1!(d1 = 1:2, d2 = 1:2, 3) = par2(d1,d2)+par3(d1,d2))) |> eval
I want to implement recursion without a recursive function.
My code is the following:
for ff=1:6
for step=i:-1:1
if (step == maxStep)
R=-1;
if(isTM =='1')
R =1;
end
else
DielectricConst =cell2mat(valueArray(step,1));
Magnetic =cell2mat(valueArray(step,2));
currentFreq = cell2mat(frequencies(ff));
w = 2*3.1416*currentFreq;
kiz =w*(DielectricConst*Magnetic - E0*M0*sin(angle).*sin(angle))^(1/2);
DiConstPlusOne = cell2mat(valueArray(step+1,1));
MagPlusOne =cell2mat( valueArray(step+1,2));
kizPlusOne =w*(DiConstPlusOne*MagPlusOne - E0*M0*sin(angle).*sin(angle))^(1/2);
res =((DiConstPlusOne*kiz)-(DielectricConst*kizPlusOne))/((DiConstPlusOne*kiz)+(DielectricConst*kizPlusOne));
result =(res + R * exp(-((-1)^(1/2))*2*kizPlusOne*cell2mat( valueArray(step+1,3))))/(1+res*(R)*exp(-((-1)^(1/2))*2*kizPlusOne*cell2mat( valueArray(step+1,3))));
R=result;
end
% disp(R);
end
end
res and result values are always the same. Is it a pointer problem ?
In simulink, I made some model using "MATLAB function"block
but I met error message here.
here is code and error message.
function [VTAS,postVTAS]=fcn(mode,initialVTAS,a,t,preVTAS)
if mode == 1
VTAS = initialVTAS + (a * t) ;
postVTAS = VTAS;
elseif mode == 2
datasize = length(preVTAS);
lastvalue = preVTAS(datasize);
VTAS = lastvalue + 0;
postVTAS = VTAS;
end
end
Output argument 'VTAS' is not assigned on some execution paths.
Function 'MATLAB Function' (#36.25.28), line 1, column 26:
"fcn"
Launch diagnostic report.
I think there is no problem about output "VTAS"
please teach me what is a problems.
As the compiler tells you, under some circumstances there is no output value assigned to VTAS. The reason is that you only assign values to that output if mode is 1 or 2. The compiler doesn't know what values are feasible for mode. To remedy this, you need to make sure that VTAS is assigned under any and all circumstances.
This could be accomplished by, e.g. adding an else construct, like so:
function [VTAS,postVTAS]=fcn(mode,initialVTAS,a,t,preVTAS)
if mode == 1
VTAS = initialVTAS + (a * t) ;
postVTAS = VTAS;
elseif mode == 2
datasize = length(preVTAS);
lastvalue = preVTAS(datasize);
VTAS = lastvalue + 0;
postVTAS = VTAS;
else
VTAS = NaN;
postVTAS = NaN;
end
end
Edit:
Additionally, it would be good practice for the else case to throw an error. This would be helpful for debugging.
As a minor note, for every case, postVTAS is equal to VTAS, so essentially it is superfluous to return both from the function.
i am new in matlab and search everything. I am writing a the function. i could not able to understand why this error is comning :"Input argument "b" is undefined." . shall i intialise b =0 ? whereas it is the parameter coming from input console. my code:
function f = evenorodd( b )
%UNTITLED2 Summary of this function goes here
%zohaib
% Detailed explanation goes here
%f = b;%2;
f = [0 0];
f = rem(b,2);
if f == 0
disp(b+ 'is even')
else
disp(b+ 'is odd')
end
console:
??? Input argument "b" is undefined.
Error in ==> evenorodd at 6
f = rem(b,2);
From what I see, this is what you are trying to do:
function f = evenorodd( b )
f = rem(b,2);
if f == 0
fprintf('%i is even\n', b)
else
fprintf('%i is odd\n', b)
end
=======================
>> evenorodd(2);
2 is even
No need to initialize f as [0,0].
In MATLAB, you cant concatenate a number and string with + operator. Use fprintf.
The above function evenorodd takes one argument (integer) and returns 0 or 1.
I'm new to Matlab and now learning the basic grammar.
I've written the file GetBin.m:
function res = GetBin(num_bin, bin, val)
if val >= bin(num_bin - 1)
res = num_bin;
else
for i = (num_bin - 1) : 1
if val < bin(i)
res = i;
end
end
end
and I call it with:
num_bin = 5;
bin = [48.4,96.8,145.2,193.6]; % bin stands for the intermediate borders, so there are 5 bins
fea_val = GetBin(num_bin,bin,fea(1,1)) % fea is a pre-defined 280x4096 matrix
It returns error:
Error in GetBin (line 2)
if val >= bin(num_bin - 1)
Output argument "res" (and maybe others) not assigned during call to
"/Users/mac/Documents/MATLAB/GetBin.m>GetBin".
Could anybody tell me what's wrong here? Thanks.
You need to ensure that every possible path through your code assigns a value to res.
In your case, it looks like that's not the case, because you have a loop:
for i = (num_bins-1) : 1
...
end
That loop will never iterate (so it will never assign a value to res). You need to explicitly specify that it's a decrementing loop:
for i = (num_bins-1) : -1 : 1
...
end
For more info, see the documentation on the colon operator.
for i = (num_bin - 1) : -1 : 1