Lamenting the lack of multiple assignment in MATLAB

September 25th, 2013 | Categories: Julia, mathematica, matlab, programming, python | Tags:

I support scientific applications at The University of Manchester (see my LinkedIn profile if you’re interested in the details) and part of my job involves working on code written by researchers in a variety of languages.  When I say ‘variety’ I really mean it – MATLAB, Mathematica, Python, C, Fortran, Julia, Maple, Visual Basic and PowerShell are some languages I’ve worked with this month for instance.

Having to juggle the semantics of so many languages in my head sometimes leads to momentary confusion when working on someone’s program.  For example, I’ve been doing a lot of Python work recently but this morning I was hacking away on someone’s MATLAB code.  Buried deep within the program, it would have been very sensible to be able to do the equivalent of this:

a=rand(3,3)

a =
    0.8147    0.9134    0.2785
    0.9058    0.6324    0.5469
    0.1270    0.0975    0.9575

>> [x,y,z]=a(:,1)

Indexing cannot yield multiple results.

That is, I want to be able to take the first column of the matrix a and broadcast it out to the variables x,y and z. The code I’m working on uses MUCH bigger matrices and this kind of assignment is occasionally useful since the variable names x,y,z have slightly more meaning than a(1,3), a(2,3), a(3,3).

The only concise way I’ve been able to do something like this using native MATLAB commands is to first convert to a cell. In MATLAB 2013a for instance:

>> temp=num2cell(a(:,1));
>> [x y z] = temp{:}

x =
    0.8147

y =
    0.9058

z =
    0.1270

This works but I think it looks ugly and introduces conversion overheads. The problem I had for a short time is that I subconsciously expected multiple assignment to ‘Just Work’ in MATLAB since the concept makes sense in several other languages I use regularly.

Python:

from pylab import rand
a=rand(3,3)
[a,b,c]=a[:,0]

Mathematica:

a = RandomReal[1, {3, 3}]
{x,y,z}=a[[All,1]]

Julia:

a=rand(3,3);
(x,y,z)=a[:,1]

I’ll admit that I don’t often need this construct in MATLAB but it would definitely be occasionally useful. I wonder what other opinions there are out there? Do you think multiple assignment is useful (in any language)?

  1. MySchizoBuddy
    September 25th, 2013 at 11:49
    Reply | Quote | #1

    in “coding the matrix” coursera course which uses python. We used multiple assignments very extensively. In python 3 they have added “rest of them” feature. like
    a, b, *c = range(10)
    a = 0, b = 1, c = rest of them.
    or
    a, *b, c = range(10)
    a = 0, c = 9, b = rest of them

  2. September 25th, 2013 at 15:49
    Reply | Quote | #2

    Agreed. The funny thing is that MATLAB functions do support multiple assignment; only MATLAB arrays do not.

  3. Barry Rowlingson
    September 25th, 2013 at 15:51
    Reply | Quote | #3

    Noo! Splitting multiple returned function values like that is nearly always wrong! If a function returns several things, its because they belong together, so it should be hard to break them apart! You can lose a lot of meaning and hence readability by breaking things up like this.

    I avoid it!

  4. Júlio Hoffimann Mendes
    September 25th, 2013 at 16:39
    Reply | Quote | #4

    Just a footnote: At least in GNU Octave, you have CoW (Copy on Write) which means no extra overhead if the value being copied isn’t modified. In other words, col1 = a(:,1); col2 = a(:,2); and so on costs ZERO if you don’t change col1,col2,…

  5. September 25th, 2013 at 17:01
    Reply | Quote | #5

    can you do:
    >>A=rand(4,3);
    >>save temp.dat A /ascii
    >>type temp.dat
    >>load temp.dat
    >>temp
    results in a variable named “temp” assigned to the 4X3 matrix.

    If you could manipulate the load argument to load only a column?

  6. Alex
    September 26th, 2013 at 03:47
    Reply | Quote | #6

    [x,y,z] = deal(a(:,1))

    Is the closest thing to what you’d want in matlab. The deal function can be used to copy a single input to multiple outputs.

  7. Mike Croucher
    September 26th, 2013 at 09:52
    Reply | Quote | #7

    Hi Alex

    That doesn’t do what we want:

    >> a=rand(3)

    a =
    0.8147 0.9134 0.2785
    0.9058 0.6324 0.5469
    0.1270 0.0975 0.9575

    >> [x,y,z] = deal(a(:,1))

    x =
    0.8147
    0.9058
    0.1270

    y =
    0.8147
    0.9058
    0.1270

    z =
    0.8147
    0.9058
    0.1270

    x,y,z have each got a copy of the first column!

  8. ludolph
    September 26th, 2013 at 11:10
    Reply | Quote | #8

    Multiple assignment is very obscure operation, because of its internal inconsistency.

    In your example:
    [x,y,z] = a(:,1) is a priori not known what is the expected result. There is plenty of possibilities:
    1. x = a(1,1), y = a(2,1, z=a(3,1)
    2. x = a(1:2,1), y=a(3,1), z = []
    … etc

    So, I think the MATLAB guys are right, that do not implement multiple assignment.

  9. Mike Croucher
    September 26th, 2013 at 13:14
    Reply | Quote | #9

    and yet Julia, Python and Mathematica seem to do it fine?

  10. Amro
    September 26th, 2013 at 18:28

    In Octave you could write:

    >> a = rand(3);
    >> [x,y,z] = num2cell(a(:,1)){:}

    Since MATLAB does not support directly indexing into a result, you’ll have to use a temporary variable to hold the result:

    >> tmp = num2cell(a(:,1));
    >> [x,y,z] = deal(tmp{:});

    or use a hack to reduce it to a one-liner:

    >> [x,y,z] = subsref(num2cell(a(:,1)), substruct(‘{}’,{‘:’}))

    This request shows up quite often on Stack Overflow :)
    http://stackoverflow.com/questions/linked/2337126

    Similarly, say you wanted to extract the columns into separate variables, you would write:

    >> [x,y,z] = subsref(num2cell(a,1), substruct(‘{}’,{‘:’}))

    in Octave simply as:

    >> [x,y,z] = num2cell(a,1){:}

    which is equivalent to: x=a(:,1); y=a(:,2); z=a(:,3);

  11. Steve L
    September 26th, 2013 at 19:17

    David Ketcheson,

    MATLAB functions are different from MATLAB indexing. Each output argument from a function consists of either an entire variable from that function’s workspace or an entire cell from the VARARGOUT cell array. The boundaries of what should and should not be included in each output are clear.

    As ludolph’s example shows, things get a bit more complicated when you’re indexing into a variable. There isn’t necessarily such a clear boundary between what should be in each “output.” There’s also the question of what happens if you specify more (probably error) or fewer (more complicated) outputs than there are elements/rows/columns/pages/etc. in the result of the indexing expression.

    a = magic(3);
    [x, y] = a(:, 1); % What should x and y be, since a(:, 1) has three elements?

    Mike Croucher,

    What do Python, Mathematica, and Julia do with their equivalents of the expression above? I can think of at least two or three possibilities.

    All,

    Expanding that example just a bit, what should be the result of this code?

    a = magic(4);
    [x, y] = a(:, [2 3]);

    1) One option would be to error because the result of the indexing expression does not have exactly two elements.

    2) A second (linear indexing inspired) option would be to store a(1, 2) in x and a(2, 2) in y.

    3) A third option would be to store a(:, 2) in x and a(:, 3) in y, since the indexing expression has two columns and there are two outputs. This would be ambiguous if the indexing expression resulted in a 2-by-2 matrix; do we split by rows or columns? Do we need to modify the syntax used for indexing in MATLAB to determine how to split the result in this ambiguous case?

    4) A fourth (DEAL-based) option would be to store a(:, [2 3]) in both x and y.

    Finally, you could encapsulate the NUM2CELL-based approach into a function if you wanted:

    function varargout = dealOut(A)
    varargout = num2cell(A);

  12. James
    February 26th, 2014 at 01:10

    For Mathematica example, didn’t you mean

    {x,y,z}=a[[All,1]]

  13. Mike Croucher
    February 26th, 2014 at 10:25

    Yes, I did. Thanks for pointing it out. Now fixed.