As said in the title, I have a recursive function and I am trying to build a tree data structure out of it to save my results. Every node consists in just one single number. The problem is that when I input the tree to the next call of the function, it seems that only the value of the tree is passed along, not the actual tree. Does anyone know how to pass a reference to the tree instead?
Initial Call:
tree = struct('left', 'empty','right', 'empty','feature','empty');
decisiontree_train(AttributeSet, LabelSet, 50, tree, 'node');
Recursive Function:
function decisiontree_train( data, labels, before_split_purity_percentage, tree, branch )
% a1 is 0, a2 is 1
[ a1_split_data, a2_split_data, a1_split_labels, a2_split_labels, ...
split_feature ] = decisiontree_split( data, labels );
new_tree = struct('left', 'empty','right', 'empty','feature','empty');
if strcmp(branch, 'left')
tree.left = new_tree;
new_tree.feature = split_feature;
elseif strcmp(branch, 'right')
tree.right = new_tree;
new_tree.feature = split_feature;
elseif strcmp(branch, 'node')
tree.feature = split_feature;
new_tree = tree;
end
[ after_split_purity_percentage ] = decisiontree_classcount( a1_split_labels );
if after_split_purity_percentage < 100 && ...
after_split_purity_percentage > before_split_purity_percentage
decisiontree_train(a1_split_data, a1_split_labels, ...
after_split_purity_percentage, new_tree, 'left');
end
[ after_split_purity_percentage ] = decisiontree_classcount( a2_split_labels );
if after_split_purity_percentage < 100 && ...
after_split_purity_percentage > before_split_purity_percentage
decisiontree_train(a2_split_data, a2_split_labels, ...
after_split_purity_percentage, new_tree, 'right');
end
% add variable to workspace
% assignin('base', 'a1_split_data', a1_split_data)
end
Unless you use object oriented matlab, there is no pass by reference. While asking a different question, the answers somehow apply to your case as well. If you are using Matlab 2015b or newer, use Matlab OOP and implement your tree using a handle class. If performance isn't a big concern, do the same.
For the likely reason that both isn't true, you have to work around the issue. Matlab uses copy-on-wrote. Thus changing your functions to take your tree structure as a first input argument and returning the modified it isn't a bad idea. In typical cases only very little data is really copied.
Related
Is there a way to have one function that can return two different variables, but only one at a time AND knowing which one is returned in the function call?
example:
I have the following function in which only one of the outputs is valid (the other one would be [])
function [a,b] = AlternatingOutput (input)
if input == 1
return ONLY A
else
return ONLY B
end
end
and i call it in a script
[a,b] = AlternatingOutput (input)
i want a way to say the following (pseudocode):
if (function outputs a)
[a,~] = AlternatingOutput(input)
elseif (function outputs b)
[~,b] = AlternatingOutput(input)
end
the script is run in a loop, and later i need the newest Valid values for a and b, so i cannot overwrite one of the two with []
I do understand that I could just write a function that checks which variable will be output, but I was wondering if there is a more elegant way.
I hope I have made my question clear, and I hope someone can answer me :)
There is no way to tell if an output argument is actually used. You may check the number of output arguments using nargout and it would allow to distinguish between [a] = AlternatingOutput(input) and [~,b] = AlternatingOutput(input)
I don't know the full context of your problem, but maybe you can put all your variables into a struct? Simply pass this struct everytime you call the function and let it decide which variables to manipulate. (This might be slow in some programming languages, but not in matlab).
How about retuning a cell?
function [ ab ] = testfun( input )
if input
ab={'ax'};
else
ab={2};
end
end
No worries about what is in the cell.
thb you could return what ever you want, Matlab does not check the type anyways
If only one of the outputs from the function AlternatingOutput is valid, then you only need to return one output:
function [X] = AlternatingOutput(input)
if input == 1
X = A;
else
X = B;
end
end
To allocate the retured value to either a or b in the loop, put them into a cell:
C = {AlternatingOutput(1), AlternatingOutput(2)};
and then use input to determine which value is change. If input is either 1 or 2 you can just do
for counter = ...
input = mod(input,2)+1;
C{input}=AlternatingOutput(input);
end
If your function doesn't mind accepting more input variables, why not pass a and b as input:
function [a,b] = AlternatingOutput(a,b,input)
if input == 1
a = new_value_for_a;
% b retains its former value
else
% a retains its former value
b = new_value_for_b;
end
end
Then it can be easily called from your script in a loop:
for i= ...
[a,b] = AlternatingOutput(a,b,input);
...
...
end
I got an odd behaviour of my functions and since i'm not so used to matlab coding i guess is due to something really easy that i don't get.
I can't understand how this could print something different
fx(Punti(1,:),Punti(2,:))
fx(Punti(2,:),Punti(3,:))
fx(Punti(3,:),Punti(4,:))
fx(Punti(4,:),Punti(5,:))
from this
for i_unic=1:4
fx(Punti(i_unic,:),Punti(i_unic+1,:))
end
Consider fx as a generic function.
Is it possible that fx uses some variables that for some reason are erased at the end of each iteration?
EDIT
-->"Punti" is just matrix containing the points a SCARA robot should follow
-->fx is the function "Retta" and it's the following
function retta(PuntoA,PuntoB,Asse_A,q_ini,rot,contaerro,varargin)
global SCARA40
global inizio XX YY ZZ
global seg_Nsteps
npassi = seg_Nsteps;
ipuntofin = inizio + npassi;
for ipunto = inizio : ipuntofin
P4 = PuntoA + (ipunto-inizio)*(PuntoB-PuntoA)/npassi;
q = kineinversa(Asse_A,P4,q_ini,rot);
Mec = SCARA40.fkine(q);
Pec = Mec(:,4);
if (dot((P4-Pec),(P4-Pec),3)>0.0001)
fprintf(1,'\n P4 Desid. = [%9.1f %9.1f %9.1f %9.1f ] \n',P4);
fprintf(1,'\n P4 Attuato = [%9.1f %9.1f %9.1f %9.1f ] \n',Pec);
contaerro = contaerro + 1;
else
q_ini = q;
end
SCARA40.plot(q);
XX(ipunto) = Pec(1);
YY(ipunto) = Pec(2);
ZZ(ipunto) = Pec(3);
if(nargin>6)
color = varargin{1};
else
color = 'r';
end
plot3(XX,YY,ZZ,color,'LineWidth',1 );
drawnow;
hold on
end
end
the test function with the results
Punti = [ 10,10,0,1 ;10,-10,0,1 ;-10,-10,0,1 ; -10,10,0,1 ] ;
%inizio=1
%retta(Punti(1,:)',Punti(2,:)',Asse_A,q_ini,rot,contaerro)
%inizio=21
%retta(Punti(2,:)',Punti(3,:)',Asse_A,q_ini,rot,contaerro)
%inizio=41
%retta(Punti(3,:)',Punti(4,:)',Asse_A,q_ini,rot,contaerro)
%inizio=61
inizio=1
for i=1:length(Punti)-1
retta(Punti(i,:)',Punti(i+1,:)',Asse_A,q_ini,rot,contaerro)
inizio=inizio+20;
end
the two images have been generated restarting Matlab
Addressing the question in the most general sense (since there is no sample given for the function fx or the function/variable Punti) then the reason you are getting different results is likely that the state of your variables/workspace is different when you test one case versus the other. How could this happen? Here are some obvious ways...
Your functions (or possibly other functions they call) are making use of the random number generator, and the starting state of the RNG is different when you test the loop versus unrolled loop case.
Your functions are sharing global variables that aren't reset to some default value at the start of each test case. You mention in a comment that the functions use global variables, so this is likely your problem.
Your functions aren't really functions, but scripts. Scripts all share a common workspace (the base workspace), whereas a function (and specifically each call to a function) will have its own unique workspace. If fx is actually a script, each call may change any or all of the variables in the base workspace. Furthermore, any other scripts, or anything you type into the command line, can change things as well. The contents of the base workspace may therefore be different when you test the loop versus unrolled loop case.
If I were to hazard a guess, I'd say that if you were to exit and restart MATLAB before each test case (i.e. reset everything to the same default starting state) you would probably get the same exact result for the loop versus unrolled loop case.
As a follow-up to my previous question about how to assign fields to a structure variable with a dynamic hierarchy, I would now like to be able to query those fields with isfield. However, isfield will only take one argument, not a list as with setfield.
To summarize my problem:
I have a function that organizes data into a structure variable. Depending on certain flags, the data is saved into the substructures with a different number of levels.
For instance, the accepted answer to my previous question has me doing this to build my structure:
foo = struct();
% Pick one...
true_false_statement = true;
% true_false_statement = false;
if true_false_statement
extra_level = {};
else
extra_level = {'baz'};
end
foo = setfield(foo, extra_level{:}, 'bar1', 1);
which gives me foo.bar1 = 1 if true_false_statement is true, and foo.baz.bar1 = 1 otherwise.
Now I want to test for the existence of the field (for instance to pre-allocate an array). If I do this:
if ~isfield(foo, extra_levels{:}, 'bar1')
foo = setfield(foo, extra_level{:}, 'bar1', zeros(1,100));
end
I get an error because isfield will only accept two arguments.
The best I've been able to come up with is to write a separate function with a try...catch block.
function tf = isfield_dyn(structure_variable, intervening_levels, field)
try
getfield(structure_variable, intervening_levels{:}, field);
tf = true;
catch err
if strcmpi(err.identifier, 'MATLAB:nonExistentField')
tf = false;
else
rethrow(err);
end
end
As mentioned below in the comments, this is a hacky hack way to do this, and it doesn't even work all that well.
Is there a more elegant built-in way to do this, or some other more robust way to write a custom function to do this?
You might find the private utility functions getsubfield, setsubfield, rmsubfield, and issubfield from the FieldTrip toolbox very handy. From the documentation of getsubfield:
% GETSUBFIELD returns a field from a structure just like the standard
% GETFIELD function, except that you can also specify nested fields
% using a '.' in the fieldname. The nesting can be arbitrary deep.
%
% Use as
% f = getsubfield(s, 'fieldname')
% or as
% f = getsubfield(s, 'fieldname.subfieldname')
%
% See also GETFIELD, ISSUBFIELD, SETSUBFIELD
I am somewhat confused because
isfield(foo, 'bar1')
isfield(foo, 'baz')
seem to work just fine on your example struct.
Of course, if you want to test more fields, just write a loop over those fieldnames and test them one by one. That may not look vectorized, but is definitely better than abusing a try-catch block to guide your flow.
I have data as a struct with several layers, for example:
data.A.B
The data I want to access is in layer B. But the problem is that field names in B can be different depending on where the data comes from. Therefore I can't just type:
data.A.B.myData
myData is itself a struct
I can use:
fieldnames(data.A)
to find the names, but this doesn't help my much. I would have to write code sections for every possible field name that can occur at this level. And that's just what i trying to avoid.
Is there a way to get down to the data I have (myData) without knowing the field names of B?
Traditionally, you can loop over the fieldnames and perform the search of myData at a specific sub-structure of the struct. However, if you don't know which sub-structure you need to search, then you can perform a recursive algorithm. Below is an example. It will return the first match of myData in the struct or an empty matrix if no match found. The code can be improved to find all matches of myData.
function S2=getmyfield(S1,queriedField)
if isstruct(S1)
% Get all fieldnames of S1
fieldArray=fieldnames(S1);
% Find any match with the queried field. You can also use isfield().
% If there is a match return the value of S1.(queriedField),
% else perform a loop and recurse this function.
matchTF=strcmp(queriedField,fieldArray);
if any(matchTF)
S2=S1.(fieldArray{matchTF});
return;
else
S2=[];
i=0; % an iterator count
while isempty(S2)
i=i+1;
S2=getmyfield(S1.(fieldArray{i}),queriedField);
end
end
else
S2=[];
end
end
Cheers.
You just need a recursive function that checks fieldnames at each level for the structure.
This is roughly what you need (it could be improved to supply the path to the found field).
function [ value, found ] = FindField( rootStruct, fieldName )
%FindField - Find a field with a structure
value = [];
found = 0;
if isstruct( rootStruct )
fields = fieldnames(rootStruct);
for fi=1:length(fields)
if strcmp(fields{fi}, fieldName )
value = rootStruct.(fieldName);
found = true;
return;
end
[value, found ] = FindField( rootStruct.(fields{fi}), fieldName );
if found
return;
end
end
end
end
Usage example:
a.b = 1;
a.b.c = 2;
a.b.d = struct('Index',1,'Special',2);
FindField(a,'d')
ans =
Index: 1
Special: 2
Working on an assignment involving Genetic Algorithms (loads of headaches, loads of fun). I need to be able to test differing crossover methods and differing mutation methods, to compare their results (part of the paper I have to write for the course). As such, I want to just pass the function names into the Repopulate method, as function handles.
function newpop = Repopulate(population, crossOverMethod, mutationMethod)
...
child = crossOverMethod(parent1, parent2, #mutationMethod);
...
function child = crossOverMethod(parent1, parent2, mutationMethod)
...
if (mutateThisChild == true)
child = mutationMethod(child);
end
...
The key point here is like 3, parameter 3: how do I pass mutationMethod down another level? If I use the # symbol, I get told:
"mutationMethod" was previously used as a variable,
conflicting with its use here as the name of a function or command.
If I don't use the # symbol, then mutationMethod gets called, with no parameters, and is quite unhappy.
While I am aware that yes, I could just rewrite my code to make it work differently, I'm now curious as to how to make it actually work.
Any help is greatly appreciated.
Actually just dont use the # symbol, use it when you call the Repopulate function instead.
Example:
function x = fun1(a,m)
x = fun2(a,m);
end
function y = fun2(b,n)
y = n(b);
end
which we call as:
> fun1([1 2 3], #sum)
6
Refer to the documentation for Passing Function Handle Arguments
Note you can check if the argument is a function handle by: isa(m,'function_handle'). Therefore you can make your function Repopulate more flexible by accepting both a function handle and a function name as a string:
function x = fun(a,m)
if ischar(m)
f = str2func(m);
elseif isa(m,'function_handle')
f = m;
else
error('expecting a function')
end
x = fun2(a,f);
end
which now can be called both ways:
fun1([1 2 3], #sum)
fun1([1 2 3], 'sum')