Subsref with cells - matlab

This issue appeared when I was answering this question. It should be some stupid error I am doing, but I can't get what error it is…
myMatrix = [22 33; 44 55]
Returns:
>> subsref(myMatrix, struct('type','()','subs',{{[1 2]}} ) );
ans =
22 44
While using it with cells:
myCell = {2 3; 4 5}
Returns:
>> subsref(myCell,struct('type','{}','subs',{{[1 2]}} ) );
ans =
2 % WHATTT?? Shouldn't this be 2 and 4 Matlab??
Checking the subsref documentation, we see:
See how MATLAB calls subsref for the expression:
A{1:2} The syntax A{1:2} calls B = subsref(A,S) where S.type='{}' and
S.subs={[1 2]}.
This seems not to be true because the returned value by subsref is just the first argument, not all arguments.
Then if one does:
>> [a,b]=subsref(myCell,struct('type','{}','subs',{{[1 2]}} ) )
a =
2
b =
4 % Surprise!
But this is not the same as myCell{[2 4]} which will automatically return:
>> myCell{[1 2]}
ans =
2
ans =
4
I would need to use subsref with one output for every index I use access myCell, or am I missing something?

When the curly braces ({}) are used for indexing a cell array, the output is a comma-separated list. This implicitly calls subsref but the behavior is slightly different from invoking it directly.
subsref by itself is a technically a function, and the comma-separated list returned by the curly braces simply behaves like varargout in this case. This means that you should specify an appropriate "sink" for all desired output results, just like you would do with any other function returning multiple parameters, otherwise they would be ignored.

dont ask me why, this is just something I tried:
myOutput=subsref(myCell,struct('type','()','subs',{{[1 2]}} ) )
note the 'type','()'!
this gives me:
myOutput =
[2] [4]
with myOutput as cell. Converting back:
>> myOutput=cell2mat(subsref(myCell,struct('type','()','subs',{{[1 2]}})))
myOutput =
2 4
This is just a "fast" answer, that will need some improvements or some background-info as well...

I was further investigating #EitanH answer and managed to find more details.
Yeah, it returns a comma separed list, but the function subsref should return a comma separed list just the A{:} does. Here is an example where the functions behave different, but this is an expected behavior, I would like the class .get method to return a list and one class common function to behave as common functions getting only the first argument from the cell.
classdef ListReturn
properties(Access = private)
a
end
properties(Dependent)
getA
end
methods
function self = ListReturn(a)
self.a = a;
end
function varargout = get.getA(self)
varargout = {self.a};
end
function varargout = getAFcn(self)
varargout = {self.a};
end
end
end
There is a substantial difference when calling the functions, exactly as the .get:
k=[ListReturn(2) ListReturn(3) ListReturn(4) ListReturn(5)]
>> k.getAFcn
ans =
2
>> k.getA
ans =
2
ans =
3
ans =
4
ans =
5
So it seems that using A{1:2} or A{:} works like a Cell.get(), whereas the subsref works as a common matlab function, returning only one argument when the output arguments are not specified, as one function in C,java,C++ would do. Anyway, I just feel like subsref should work as the .get.

Related

Handle undefined methods in Matlab by passing method's name and inputs to a function/method

Assume we have a Matlab class called MyClas, as below,
classdef MyClass
properties
Value
end
methods
function foo(input1, input2)
...
end
end
end
What I am looking for is having MyClass.anyThing(inputValue), where anyThing and inputValue are arbitrary strings and anyThing is not a defined method in MyClass, passes anyThing and inputValue as two strings to my foo function.
In other words, I want to be able to handle undefined methods by passing their name and inputs to a function/method as strings.
I am having a hard time to elaborate on my issue since I don't know if there is any technical term for what I am trying to pull off here. Yet, I managed to find this link from math works, which just customizes indexing operations and does not exactly address my issue.
You can emulate this by hacking in an implementation of subsref. Like this:
classdef MethodRedirector
methods
function varargout = subsref(obj, S)
if numel(S) == 2 && strcmp(S(1).type, '.') && ...
strcmp(S(2).type, '()')
% Looks like obj.method(args)
fprintf('In method invocation to: %s with args:\n', S(1).subs);
celldisp(S(2).subs);
else
[varargout{1:nargout}] = builtin('subsref', obj, S);
end
end
end
end
The restriction is that callers must use the obj.method(args...) syntax, and cannot use the (usually equivalent) method(obj, args...) syntax.
Here's how that looks in practice:
>> mr = MethodRedirector; mr.someMethod(1, '2', magic(3))
In method invocation to: someMethod with args:
ans{1} =
1
ans{2} =
2
ans{3} =
8 1 6
3 5 7
4 9 2

How can I achieve cell array expansion as a function call in MATLAB?

I have the following cell array:
>> tmp0 = {'foo', '%s', 'one'; 'bar', '%d', 3}
tmp0 =
2×3 cell array
'foo' '%s' 'one'
'bar' '%d' [ 3]
I can use it like this with sprintf:
>> sprintf('%s,%d', tmp0{:,3})
ans =
'one,3'
I would like to be able to achieve the same thing with a function call, since if I have a function that generates a cell array, say genCell(), I don't think I can achieve something like genCell(){:} in MATLAB.
So I made this function:
function cellExp(cellIn)
cellIn{:}
end
Although dubious it seems to work as expected so far, since calling cellExp(tmp0(:,3)) seems to be the same as calling tmp0{:,3}
>> cellExp(tmp0(:,3))
ans =
'one'
ans =
3
>> tmp0{:,3}
ans =
'one'
ans =
3
However, ultimately, I cannot use it as desired:
>> sprintf('%s,%d', cellExp(tmp(:,3)))
Error using cellExp
Too many output arguments.
The last error message you are getting is because the output of cellExp(tmp0(:,3)) is comma-separeted list.
I am not sure exactly what you are looking for here, but I think this is one possibility for a function that will return your string based on a myCell = tmp0.
function myStr = mySprintf(myCell)
formatSpec = strjoin(myCell(:,2), ',');
[A1, A2] = myCell{:, 3};
myStr = sprintf(formatSpec, A1, A2);
end

How to display an array of objects as will?

I have defined a class like
classdef Test
properties
a
b
end
methods
function this = Test(a, b)
this.a = a;
this.b = b;
end
function disp(this)
fprintf('a=%d b=%d\n', this.a, this.b);
end
end
end
But when I want to display a vector of Test, it seems not print each elements of array using the disp function just defined.
>> out = [Test(1,2),Test(3,4)]
out =
a=1 b=3
a=2 b=4
The questions is how to display an array of objects appropriately? Is there a way to overload the disp function and print as the following:
out=
a=1 b=2
a=3 b=4
(In my considering , the output will be same as calling disp function to element of array one by one.. But the output seems like firstly print all the a's value 1 3 and then b's value 2 4.)
You are getting this result because in your statement out = [Test(1,2),Test(3,4)], the variable out becomes an array of the same class Test, but of size [1x2].
If you try out.a in your console, you'll get:
>> out.a
ans =
1
ans =
3
This is a coma separated list of all the values of a in the out array. This is also the first parameter that your custom disp function sees. It then sees another column vector of all the values of b. To understand what the function fprintf is presented with you can also try in your console:
>> [out.a,out.b]
ans =
1 3 2 4
>> [out.a;out.b]
ans =
1 3
2 4
Since fprintf works in column major order, it will consume all the values column wise first. In that case we can see that the last option we tried seems better.
Indeed, if you change your disp function to:
function disp(this)
fprintf('a=%d b=%d\n', [this.a ; this.b]);
end
You get the desired output:
>> out = [Test(1,2),Test(3,4),Test(5,6)]
out =
a=1 b=2
a=3 b=4
a=5 b=6
Whichever size of object array you define. Just keep in mind that if you input an array of Test object they will be considered column wise:
>> out = [ Test(1,2),Test(3,4) ; Test(5,6),Test(7,8) ]
out =
a=1 b=2
a=5 b=6
a=3 b=4
a=7 b=8
Last option, if you want even more granularity over the display of your object array, you can customise it the way you like inside the disp function:
function disp(this)
nElem = numel(this) ;
if nElem==1
fprintf('a=%d b=%d\n', this.a , this.b );
else
for k=1:nElem
fprintf('a=%d b=%d\n', this(k).a , this(k).b);
end
end
end
This produces the same display than before, but since the elements are treated one by one, you could customise even further without having to consider the way arrays are treated by fprintf.
With the syntax you use, you should overload the display function instead of the disp function.
See the (not that simple to read) corresponding page in the documentation.

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

MATLAB "bug" (or really weird behavior) with structs and empty cell arrays

I have no idea what's going on here. I'm using R2006b. Any chance someone out there with a newer version could test to see if they get the same behavior, before I file a bug report?
code: (bug1.m)
function bug1
S = struct('nothing',{},'something',{});
add_something(S, 'boing'); % does what I expect
add_something(S.something,'test'); % weird behavior
end
function add_something(X,str)
disp('X=');
disp(X);
disp('str=');
disp(str);
end
output:
>> bug1
X=
str=
boing
X=
test
str=
??? Input argument "str" is undefined.
Error in ==> bug1>add_something at 11
disp(str);
Error in ==> bug1 at 4
add_something(S.something,'test');
It looks like the emptiness/nothingness of S.something allows it to shift the arguments for a function call. This seems like Very Bad Behavior. In the short term I want to find away around it (I'm trying to make a function that adds items to an initially empty cell array that's a member of a structure).
Edit:
Corollary question: so there's no way to construct a struct literal containing any empty cell arrays?
As you already discovered yourself, this isn't a bug but a "feature". In other words, it is the normal behavior of the STRUCT function. If you pass empty cell arrays as field values to STRUCT, it assumes you want an empty structure array with the given field names.
>> s=struct('a',{},'b',{})
s =
0x0 struct array with fields:
a
b
To pass an empty cell array as an actual field value, you would do the following:
>> s = struct('a',{{}},'b',{{}})
s =
a: {}
b: {}
Incidentally, any time you want to set a field value to a cell array using STRUCT requires that you encompass it in another cell array. For example, this creates a single structure element with fields that contain a cell array and a vector:
>> s = struct('strings',{{'hello','yes'}},'lengths',[5 3])
s =
strings: {'hello' 'yes'}
lengths: [5 3]
But this creates an array of two structure elements, distributing the cell array but replicating the vector:
>> s = struct('strings',{'hello','yes'},'lengths',[5 3])
s =
1x2 struct array with fields:
strings
lengths
>> s(1)
ans =
strings: 'hello'
lengths: [5 3]
>> s(2)
ans =
strings: 'yes'
lengths: [5 3]
ARGH... I think I found the answer. struct() has multiple behaviors, including:
Note If any of the values fields is
an empty cell array {}, the MATLAB
software creates an empty structure
array in which all fields are also
empty.
and apparently if you pass a member of a 0x0 structure as an argument, it's like some kind of empty phantom that doesn't really show up in the argument list. (that's still probably a bug)
bug2.m:
function bug2(arg1, arg2)
disp(sprintf('number of arguments = %d\narg1 = ', nargin));
disp(arg1);
test case:
>> nothing = struct('something',{})
nothing =
0x0 struct array with fields:
something
>> bug2(nothing,'there')
number of arguments = 2
arg1 =
>> bug2(nothing.something,'there')
number of arguments = 1
arg1 =
there
This behaviour persists in 2008b, and is in fact not really a bug (although i wouldn't say the designers intended for it):
When you step into add_something(S,'boing') and watch the first argument (say by selecting it and pressing F9), you'd get some output relating to the empty structure S.
Step into add_something(S.something,'test') and watch the first argument, and you'd see it's in fact interpreted as 'test' !
The syntax struct.fieldname is designed to return an object of type 'comma separated list'. Functions in matlab are designed to receive an object of this exact type: the argument names are given to the values in the list, in the order they are passed. In your case, since the first argument is an empty list, the comma-separated-list the function receives starts really at the second value you pass - namely, 'test'.
Output is identical in R2008b:
>> bug1
X=
str=
boing
X=
test
str=
??? Input argument "str" is undefined.
Error in ==> bug1>add_something at 11
disp(str);
Error in ==> bug1 at 4
add_something(S.something,'test'); % weird behavior