4

I would like to assign the values in a vector of length 2 to multiple variables. The output of size() is able to do this:

% works
[m,n] = size([0 0]);

However splitting this into two lines doesn't work:

sz = size([0 0]);
[m,n] = sz;
% returns:
%   ??? Too many output arguments.

What is special about the return value of size that is lost when it is assigned to a variable?

benathon
  • 7,455
  • 2
  • 41
  • 70
  • 2
    Have a look at [`varargout`](http://www.mathworks.com/help/matlab/ref/varargout.html?refresh=true) and also at [`nargout`](https://nf.nci.org.au/facilities/software/Matlab/techdoc/ref/nargin.html) – Dan Sep 18 '14 at 07:49
  • `size()` is a function. So when you do `size([0 0])`, it goes inside the internal function and returns two arguments. In the second case, `sz` is a variable which is a vector of length 2. Now you are trying to assign a single variable to two variables. That won't happen. You can write your own function to do that. – Autonomous Sep 18 '14 at 07:50
  • look here for multiple variable assignments: http://stackoverflow.com/questions/2740704/is-there-anything-like-deal-for-normal-matlab-arrays – zinjaai Sep 18 '14 at 08:11
  • Thanks Parag S. Chandakkar I decided to write my own function to handle arbitrary length vectors – benathon Sep 18 '14 at 08:36
  • Wow Dan, I wish I had known about varargout before I wrote that silly eval() business – benathon Sep 18 '14 at 08:39

6 Answers6

4

Matlab's output arguments are interesting this way. A function can have a variable number of outputs depending on how many the ‘user’ asked for.

When you write

[m,n] = size([0 0]);

you are requesting two output arguments. Inside the function itself this would correspond to the variable nargout equal to 2.

But when you write

sz = size([0 0]);

the size function recognises that it's a single output argument, and gives you both m and n as a vector instead of two singleton outputs. This style of behaviour is (I think) generally uncommon in Matlab.

Also note that Matlab doesn't allow multiple arguments to break up vectors:

x = [1 1]
[y,z] = x

returns Too many output arguments.

Will Robertson
  • 62,540
  • 32
  • 99
  • 117
3

The custom function you introduced is quite an overkill and uses functions like eval which are considered bad practice. It can be done much shorter. That's all you need:

function [ varargout ] = split( Vector )

varargout = num2cell( Vector );

end

And because of varargout you have a variable-length output argument list and you don't need to edit your function for more argements.

It works for vectors as well as for matrices:

[a,b,c,d] = split( [1,2;3,4] )

a =  1

b =  3

c =  2

d =  4

If you don't like the matrix compatibility, include a condition and check the dimensions of the input vector.

Robert Seifert
  • 25,078
  • 11
  • 68
  • 113
  • 1
    The first sentence alone deserves a +1. Using `varargout` is the smoothest way to do this. There's also an approach using anonymous function that doesn't require a separate script (see my answer). Unless you introduce some nargin, nargout checks, this solution will have the same pitfalls as mine. The difference is: You can introduce such tests, while it's impossible using just an anonymous function. – Stewie Griffin Sep 18 '14 at 09:49
  • 1
    @RobertP. I thought about the deal solution as well. But the OP obviously just used `size` as an example and actually needs to split more values. Its harder with `deal` then. – Robert Seifert Sep 18 '14 at 10:02
  • I agree my function is way overkill – benathon Sep 18 '14 at 19:38
3

If you for some reason don't want to have this in a separate function, you can have an anonymous function return multiple outputs like this:

split = @(x) deal(x(1), x(2))
A = zeros(5,3)
sz = size(A)

[x, y] = split(sz)
   x =  5
   y =  3

The deal function sees two left side arguments and thus produce the correct output. Note that this function will not respond well to wrong input and output. Check out Loren's blog for more information.

Stewie Griffin
  • 14,889
  • 11
  • 39
  • 70
2

You could do it transforming sz to a cell array, and then generating a comma-separated list from that array:

sz_cell = mat2cell(sz(:), ones(numel(sz),1));
[m, n] = sz_cell{:}; %// Or [m, n] = deal(sz_cell{:});
Luis Mendo
  • 110,752
  • 13
  • 76
  • 147
0

It isn't something special about the size value.It is just that it deals with sz as a single argument since you are copying to it the result of size and that's why it can't have 2 outputs.

You will get the same error by using any other function which defined by 1 output , for example : [m,n] = cos(0) etc..

George
  • 5,808
  • 15
  • 83
  • 160
0

I figured out a way to do this. Thanks to Will Robertson's answer I realized that writing a function was the only way to really get what I want. Here is split.

function [ o1, o2, o3, o4, o5, o6, o7, o8 ] = split( v )
%SPLIT Splits a vector of bounded length into individual return variables.
% Split() can handle arbitrarily long input vectors, but only a fixed
% number of output variables.  @benathon
%
% Usage:
%  vec = [1 2 3 4 5];
%  [a,b,c,d,e] = split(vec);
%  [f,g]       = split(vec);

% If you would like to upgrade split() to handle more output variables,
% simply add more output variables to the function definition and
% then change this variable
maxout = 8;

[~,n] = size(v);

if n < nargout
    error('input vector too short for number of output arguments');
end

% we only need to assign this many output variables
iterations = min(n,nargout);


% Matlab catches "Too many output arguments." before we can
%if( iterations > maxout )
%    error('Too many output, edit split.m to easily upgrade');
%end


i = 1;
while i <= iterations
    expression = sprintf('o%d=v(%d);', i, i);
    eval(expression);
    i = i + 1;
end

Look in the comments for usage. I've also made a gist: https://gist.github.com/esromneb/652fed46ae328b17e104

benathon
  • 7,455
  • 2
  • 41
  • 70