Access complex matlab struct with function/string - matlab

I have a matlab struct which several levels (e.g. a.b(j).c(i).d). I would like to write a function which gives me the fields I want. If the struct has only one level, it would be easy:
function x = test(struct, label1)
x = struct.(label1)
end
E.g. if I have the struct a.b I could get b via: test('b'). However, this does not work with subfields, if I have a struct a.b.c I can't use test('b.c') to access it.
Is there any way to pass a string with the full field name (with dots) to a function to retrieve this field? Or is there even a better way to get only the fields that I select through the function arguments?
The goal? Sure, it would be a useless function for one fieldname, but I wan't to pass a list of fieldnames as argument to receive exactly these fields.

You should use subsref function:
function x = test(strct, label1)
F=regexp(label1, '\.', 'split');
F(2:2:2*end)=F;
F(1:2:end)={'.'};
x=subsref(strct, substruct(F{:}));
end
To access a.b.c you can use:
subsref(a, substruct('.', 'b', '.', 'c'));
The test function first splits the input using .as a delimiter and creates a cell array F where every other element is filled with . and then its elements are passed to substruct as arguments.
Note that if there are arrays of structs involved it will get the first one. For a.b(i).c(j).d passing b.c.d will return a.b(1).c(1).d. See this question for the way it should be handled.
As a side note, I renamed your input variable struct to strct because struct is a built-in MATLAB command.

One way is to create a self calling function to do this:
a.b.c.d.e.f.g = 'abc'
value = getSubField ( a, 'b.c.d.e.f.g' )
'abc'
function output = getSubField ( myStruct, fpath )
% convert the provided path to a cell
if ~iscell(fpath); fpath = strread ( fpath, '%s', 'delimiter', '.' ); end
% extract the field (should really check if it exists)
output = myStruct.(fpath{1});
% remove the one we just found
fpath(1) = [];
% if fpath still has items in it -> self call to get the value
if isstruct ( output ) && ~isempty(fpath)
% Pass in the sub field and the remaining fpath
output = getSubField ( output, fpath );
end
end

Related

Creating a function with variable number of inputs?

I am trying to define the following function in MATLAB:
file = #(var1,var2,var3,var4) ['var1=' num2str(var1) 'var2=' num2str(var2) 'var3=' num2str(var3) 'var4=' num2str(var4)'];
However, I want the function to expand as I add more parameters; if I wanted to add the variable vark, I want the function to be:
file = #(var1,var2,var3,var4,vark) ['var1=' num2str(var1) 'var2=' num2str(var2) 'var3=' num2str(var3) 'var4=' num2str(var4) 'vark=' num2str(vark)'];
Is there a systematic way to do this?
Use fprintf with varargin for this:
f = #(varargin) fprintf('var%i= %i\n', [(1:numel(varargin));[varargin{:}]])
f(5,6,7,88)
var1= 5
var2= 6
var3= 7
var4= 88
The format I've used is: 'var%i= %i\n'. This means it will first write var then %i says it should input an integer. Thereafter it should write = followed by a new number: %i and a newline \n.
It will choose the integer in odd positions for var%i and integers in the even positions for the actual number. Since the linear index in MATLAB goes column for column we place the vector [1 2 3 4 5 ...] on top, and the content of the variable in the second row.
By the way: If you actually want it on the format you specified in the question, skip the \n:
f = #(varargin) fprintf('var%i= %i', [(1:numel(varargin));[varargin{:}]])
f(6,12,3,15,5553)
var1= 6var2= 12var3= 3var4= 15var5= 5553
Also, you can change the second %i to floats (%f), doubles (%d) etc.
If you want to use actual variable names var1, var2, var3, ... in your input then I can only say one thing: Don't! It's a horrible idea. Use cells, structs, or anything else than numbered variable names.
Just to be crytsal clear: Don't use the output from this in MATLAB in combination with eval! eval is evil. The Mathworks actually warns you about this in the official documentation!
How about calling the function as many times as the number of parameters? I wrote this considering the specific form of the character string returned by your function where k is assumed to be the index of the 'kth' variable to be entered. Array var can be the list of your numeric parameters.
file=#(var,i)[strcat('var',num2str(i),'=') num2str(var) ];
var=[2,3,4,5];
str='';
for i=1:length(var);
str=strcat(str,file(var(i),i));
end
If you want a function to accept a flexible number of input arguments, you need varargin.
In case you want the final string to be composed of the names of your variables as in your workspace, I found no way, since you need varargin and then it looks impossible. But if you are fine with having var1, var2 in your string, you can define this function and then use it:
function str = strgen(varargin)
str = '';
for ii = 1:numel(varargin);
str = sprintf('%s var%d = %s', str, ii, num2str(varargin{ii}));
end
str = str(2:end); % to remove the initial blank space
It is also compatible with strings. Testing it:
% A = pi;
% B = 'Hello!';
strgen(A, B)
ans =
var1 = 3.1416 var2 = Hello!

Multiple commands in one, Matlab

Sometimes it is desired to make several calls in one command. A simple example could be strrep. Assume you want to replace all parentheses with brackets, all commas with dots and then remove all double quotations. The following pseudo code could then be desired:
strrep(myString, '()', '[]', ',', '.', '"', '')
Is there any way to accomplish this? You could of course go with:
strrep(strrep(strrep(myString, '()', '[]'), ',', '.'), '"', '')
Or save the strings in a cell array and use this in a for loop, but both solutions are incredibly ugly.
The most desired answer, is one that is generic for all functions that work in a similar way.
To directly answer your question, there is really no consistent way of doing this, no. It really depends on the function. If you search the documentation you will often find a way to do this. With strings, at least, you can usually pass cell arrays in place of strings to perform operations on multiple strings, and in this case multiple operations on the same string.
A Solution for This Particular Example
You can easily use regexprep to do this for you. You can pass a cell array of the expressions to match with a corresponding cell array of the replacement values.
regexprep('abc', {'a', 'b', 'c'}, {'1', '2', '3'});
%// '123'
For your specific example, you would do something like:
regexprep(myString, {'\(\)', ',', '"'}, {'[]', '.', ''})
And as an example:
myString = 'This, is a () "string"';
regexprep(myString, {'\(\)', ',', '"'}, {'[]', '.', ''})
%// 'This. is a [] string'
If you don't want to worry about escaping all of the expressions to be regex-compatible, you can use regexptranslate to do that for you.
expressions = regexptranslate('escape', {'()', ',', '"'});
regexprep(myString, expressions, {'[]', '.', ''});
Say you want function foo to work like this:
foo(Variable,Parameter1,Value1);
foo(Variable,Parameter1_1,Value1,Parameter2,Value2,...);
then using recursion:
function[Variable]=FooBar(Variable,varargin)
N=nargin-1; %\\ Count the input parameters
if N>=2
Parameter=varargin{1};
Value=varargin{2};
% Process the first Parameter-value pair
Variable=FooBar(Variable,varargin{3:N}); %\\ Cut first Parameter-Value pair off and pass the rest to foo again
end
This approach allows you to use chain of single parameters, pairs, triplets, quadruplets, etc.
In this perticullar example the pairs are executed as LIFO stack and last unpaired Parameter is ignored. You can also add some conditions to implement foo(IN,Parameter1,Value1,Modifier,Parameter2,Value2,...) and many other properties...
For your perticullar example:
function[MyString]=FooBar(MyString,varargin)
N=nargin-1; %\\ Count the input parameters
if N>=2
Parameter=varargin{1};
Value=varargin{2};
MyString=regexprep(MyString,Parameter,Value)
MyString=FooBar(MyString,varargin{3:N});%\\ Cut first Parameter-Value pair off and pass the rest to foo again
end
Examples:
>> myString='This, is a () "string"';
FooBar(myString,'()','[]','"','',',','.')
ans = This. is a [] string
>> myString='This, is a ("string")';
FooBar(myString,'()','[]','"','',',','.')
ans = This. is a (string)
>> myString='This, is a ("string")';
FooBar(myString,'(','[',')',']','"','',',','.')
ans = This. is a [string]
As already said by #Suever your example can be solved by regexprep and #thewaywewalk has hinted that there is no "general" soluction for all function calls.
Note I do not advocate this as a good way to code -> but its a quirky question and thus here is a suitable quirky solution....
There is lots of reason why you shouldn't do this - namely a nightmare to debug but you could in theory do this with an "intelligent" self calling function...
% Create your own function which takes the following inputs:
% fHandle - function handle to the function of choice
% property - your starting variable
% varargin - a cell array (or single var) of variables to
% pass into the fHandle on each call
% see examples below...
function output = multipleCalls ( fHandle, property, varargin )
% call your primary function using feval and your inputs
% with the 1st group of inputs from the 1st varargin
if iscell ( varargin{1} )
output = feval ( fHandle, property, varargin{1}{:} );
else
output = feval ( fHandle, property, varargin{1} );
end
% remove the varargin variable which has just been used.
varargin(1) = [];
% are they aremore multiple call?
if ~isempty ( varargin )
% if so self call to apply the subsequent calls.
output = multipleCalls ( fHandle, output, varargin{:} );
end
end
% modifying your example to use this method:
multipleCalls( #strrep, 'This, is a () "string"', { '()', '[]' }, { ',', '.' }, { '"', '' } )
% Its probably a longer command and is it any clearer -> probably not...
% Here is another example:
% Create a silly anonymous function
sillyFunction = #(a,b) a + b
% Then you can use it in the same way:
% Where 0 is what you start with and then
% each time you want to add 1, then 2, then 3 and finally 4
multipleCalls ( sillyFunction, 0, 1, 2, 3, 4 )

Matlab; Structure field name with valuable ( = number)

I am trying to assign valuable, which is number and given by for loop, to the name of structure field. For example, I would like to do as following,
A.bx, where A is name of structure(= char), b is part of field name ( = char) and x is valuable given by for loop. A and b is fixed or predefined.
Any comment is appreciated !
genvarname(str,list) generates a valid variable name in str [a string] in which at each iteration value in str is different from the exclusion list
And fieldname(S) returns a list of all the names of the field already in the structure S (use it to create a exclusion list)
Here is a code for what you want:
A = struct ();
for i = 1:5
A.(genvarname ('b', fieldnames (A))) = i;
end
Read about 1. genvarname(str,list) 2. fieldnames(S)
You can name you struct fields using simple sprintf
A = struct()
for ii = 1:10
fn = sprintf('b%d', ii );
A.(fn) = ii; % use the struct
end
I tend to agree with sebastian that suggested using arrays or cells over this type of field naming. In addition to cells and arrays you might find containers.Map to be very versatile and useful.

Cell elements as comma separated input arguments for varargin function

Imagine a function with a variable number of input arguments, alternately asking for a string and a value.
myfunction('string1',value1,'string2',value2,...)
e.g.
myfunction('A',5,'B',10)
I want to keep the ability to call the function like that and I dont want to change the evaluation of varargin inside the function. (Except ('string1','string2',...,value1,value2,...) if that helps)
But I also have my input strings and values stored in a cell array inputvar <4x1 cell>:
inputvar =
'A' [5] 'B' [10]
Also this cell array has a variable length.
My intention is to call my function somehow as follows:
myfunction( inputvar )
which is obviously not working. Any ideas how I could transform my cell to a valid input syntax?
I already tried to generate a string like
''string1',value1,'string2',value2'
and use eval to use it in the function call. But it didn't worked out. So alternatively is there a way to transfor a string to code?
You should be able to do it like this:
myfunction(inputvar{:})
{:} creates a comma separated list
EDIT:
For example:
function val = myfunction(string1,value1,string2,value2)
if string1 == 'A'
val = value1;
else
val = value2;
end
myfunction('A',5,'B',10)
myfunction('B',5,'B',10)
A = {'A',5,'B',10};
myfunction(A{:})
A = {'B',5,'B',10};
myfunction(A{:})
returns:
ans = 5
ans = 10
ans = 5
ans = 10

How do I retrieve the names of function parameters in matlab?

Aside from parsing the function file, is there a way to get the names of the input and output arguments to a function in matlab?
For example, given the following function file:
divide.m
function [value, remain] = divide(left, right)
value = floor(left / right);
remain = left / right - value;
end
From outside the function, I want to get an array of output arguments, here: ['value', 'remain'], and similarly for the input arguments: ['left', 'right'].
Is there an easy way to do this in matlab? Matlab usually seems to support reflection pretty well.
EDIT Background:
The aim of this is to present the function parameters in a window for the user to enter. I'm writing a kind of signal processing program, and functions to perform operations on these signals are stored in a subfolder. I already have a list and the names of each function from which the user can select, but some functions require additional arguments (e.g. a smooth function might take window size as a parameter).
At the moment, I can add a new function to the subfolder which the program will find, and the user can select it to perform an operation. What I'm missing is for the user to specify the input and output parameters, and here I've hit the hurdle here in that I can't find the names of the functions.
MATLAB offers a way to get information about class metadata (using the meta package), however this is only available for OOP classes not regular functions.
One trick is to write a class definition on the fly, which contain the source of the function you would like to process, and let MATLAB deal with the parsing of the source code (which can be tricky as you'd imagine: function definition line spans multiple lines, comments before the actual definition, etc...)
So the temporary file created in your case would look like:
classdef SomeTempClassName
methods
function [value, remain] = divide(left, right)
%# ...
end
end
end
which can be then passed to meta.class.fromName to parse for metadata...
Here is a quick-and-dirty implementation of this hack:
function [inputNames,outputNames] = getArgNames(functionFile)
%# get some random file name
fname = tempname;
[~,fname] = fileparts(fname);
%# read input function content as string
str = fileread(which(functionFile));
%# build a class containing that function source, and write it to file
fid = fopen([fname '.m'], 'w');
fprintf(fid, 'classdef %s; methods;\n %s\n end; end', fname, str);
fclose(fid);
%# terminating function definition with an end statement is not
%# always required, but now becomes required with classdef
missingEndErrMsg = 'An END might be missing, possibly matching CLASSDEF.';
c = checkcode([fname '.m']); %# run mlint code analyzer on file
if ismember(missingEndErrMsg,{c.message})
% append "end" keyword to class file
str = fileread([fname '.m']);
fid = fopen([fname '.m'], 'w');
fprintf(fid, '%s \n end', str);
fclose(fid);
end
%# refresh path to force MATLAB to detect new class
rehash
%# introspection (deal with cases of nested/sub-function)
m = meta.class.fromName(fname);
idx = find(ismember({m.MethodList.Name},functionFile));
inputNames = m.MethodList(idx).InputNames;
outputNames = m.MethodList(idx).OutputNames;
%# delete temp file when done
delete([fname '.m'])
end
and simply run as:
>> [in,out] = getArgNames('divide')
in =
'left'
'right'
out =
'value'
'remain'
If your problem is limited to the simple case where you want to parse the function declaration line of a primary function in a file (i.e. you won't be dealing with local functions, nested functions, or anonymous functions), then you can extract the input and output argument names as they appear in the file using some standard string operations and regular expressions. The function declaration line has a standard format, but you have to account for a few variations due to:
Varying amounts of white space or blank lines,
The presence of single-line or block comments, and
Having the declaration broken up on more than one line.
(It turns out that accounting for a block comment was the trickiest part...)
I've put together a function get_arg_names that will handle all the above. If you give it a path to the function file, it will return two cell arrays containing your input and output parameter strings (or empty cell arrays if there are none). Note that functions with variable input or output lists will simply list 'varargin' or 'varargout', respectively, for the variable names. Here's the function:
function [inputNames, outputNames] = get_arg_names(filePath)
% Open the file:
fid = fopen(filePath);
% Skip leading comments and empty lines:
defLine = '';
while all(isspace(defLine))
defLine = strip_comments(fgets(fid));
end
% Collect all lines if the definition is on multiple lines:
index = strfind(defLine, '...');
while ~isempty(index)
defLine = [defLine(1:index-1) strip_comments(fgets(fid))];
index = strfind(defLine, '...');
end
% Close the file:
fclose(fid);
% Create the regular expression to match:
matchStr = '\s*function\s+';
if any(defLine == '=')
matchStr = strcat(matchStr, '\[?(?<outArgs>[\w, ]*)\]?\s*=\s*');
end
matchStr = strcat(matchStr, '\w+\s*\(?(?<inArgs>[\w, ]*)\)?');
% Parse the definition line (case insensitive):
argStruct = regexpi(defLine, matchStr, 'names');
% Format the input argument names:
if isfield(argStruct, 'inArgs') && ~isempty(argStruct.inArgs)
inputNames = strtrim(textscan(argStruct.inArgs, '%s', ...
'Delimiter', ','));
else
inputNames = {};
end
% Format the output argument names:
if isfield(argStruct, 'outArgs') && ~isempty(argStruct.outArgs)
outputNames = strtrim(textscan(argStruct.outArgs, '%s', ...
'Delimiter', ','));
else
outputNames = {};
end
% Nested functions:
function str = strip_comments(str)
if strcmp(strtrim(str), '%{')
strip_comment_block;
str = strip_comments(fgets(fid));
else
str = strtok([' ' str], '%');
end
end
function strip_comment_block
str = strtrim(fgets(fid));
while ~strcmp(str, '%}')
if strcmp(str, '%{')
strip_comment_block;
end
str = strtrim(fgets(fid));
end
end
end
This is going to be very hard (read: impossible) to do for general functions (think of things like varargin, etc). Also, in general, relying on variable names as a form of documentation might be... not what you want. I'm going to suggest a different approach.
Since you control the program, what about specifying each module not just with the m-file, but also with a table entry with extra information. You could document the extra parameters, the function itself, notate when options are booleans and present them as checkboxes, etc.
Now, where to put this? I would suggest to have the main m-file function return the structure, as sort of a module loading step, with a function handle that points to the subfunction (or nested function) that does the real work. This preserves the single-file setup that I'm sure you want to keep, and makes for a much more configurable setup for your modules.
function module = divide_load()
module.fn = #my_divide;
module.name = 'Divide';
module.description = 'Divide two signals';
module.param(1).name = 'left';
module.param(1).description = 'left signal';
module.param(1).required_shape = 'columnvector';
% Etc, etc.
function [value, remain] = my_divide(left, right)
value = floor(left / right);
remain = left / right - value;
end
end
When you can't get information from a programming langauge about its contents (e.g., "reflection"), you have to step outside the language.
Another poster suggested "regular expressions", which always fail when applied to parsing real programs because regexps cannot parse context free langauges.
To do this reliably, you need a real M language parser, that will give you access to the parse tree. Then this is fairly easy.
Our DMS Software Reengineering Toolkit has an M language parser available for it, and could do this.
Have you considered using map containers?
You can write your functions along these lines . . .
function [outMAP] = divide(inMAP)
outMAP = containers.Map();
outMAP('value') = floor(inMAP('left') / inMAP('right'));
outMAP('remain') = inMAP('left') / inMAP('right') - outMAP('value');
end
...and call them like this ...
inMAP = containers.Map({'left', 'right'}, {4, 5});
outMAP = divide(inMAP);
...and then simply examine tha variable names using the following syntax...
>> keys(inMAP)
ans =
'left' 'right'
inputname(argnum) http://www.mathworks.com/help/techdoc/ref/inputname.html .