Loren on the Art of MATLAB

January 24th, 2008

Deal or No Deal

This post continues in the theme from my last post, where people routinely come to me on a topic. Today I want to distinguish between indexed assignment, where you can take advantage of scalar expansion, and assignment to several output arrays, often arising from a comma-separated list from cell or struct arrays.

Contents

First Explore Cell Arrays

Let's first explore some aspects of creating and populating the contents of a cell array. I first initialize 2 arrays, each of length 3.

a = {1 magic(3) 'hello'};
d = {'hi' 17 pi};

Next I initialize another cell array to be the same as a.

c = a;

Now I attempt to change the contents of all the cells in c by assigning a new value that I want to show up in each cell.

try
    [c{:}] = 'aloha'; % error unless c is a scalar
catch cellE
    disp(cellE.message)
end
Too many output arguments.

As you can see, this didn't work. The reason is that I have 3 entities on the left-hand side, because c is a cell array of length 3 and I used the comma-separated list notion. MATLAB doesn't know how to make 3 separate arrays out of 1 by simple assignment.

I could use regular indexing on c to populate it however. In this case, the right-hand side has length 1 (a cell array with string contents) and the left-hand side is all of c. MATLAB can do scalar expansion for regular assignment into arrays which is why this works.

c(:) = {'ahola'}
c = 
    'ahola'    'ahola'    'ahola'

How else might we populate all the cells in c? One way is to use the function deal. I prefer the line above; I think the code without deal is clearer and it doesn't have the extra function call overhead.

[c{:}] = deal('ola')
c = 
    'ola'    'ola'    'ola'

If I have the values I want to store in the contents of c in another cell array, let's say d, I can again use deal but I don't have to. Compare the following 2 statements.

[c{:}] = deal(d{:})
[c{:}] = d{:}
c = 
    'hi'    [17]    [3.1416]
c = 
    'hi'    [17]    [3.1416]

In the first of these, I expand the cell array on the right to a comma-separated list and pass it through the function deal which then distributes the outputs into c. In the second statement, I bypass the function call to deal with some relatively new (MATLAB 7) syntax.

Now Explore structs

Let's do a similar exercise with structs now.

s = struct('f',a);
s.f
ans =
     1
ans =
     8     1     6
     3     5     7
     4     9     2
ans =
hello
try
    [s.f] = 'bonjour'; % error unless s is a scalar
catch structE
    disp(structE.message)
end
Too many output arguments.
[s.f] = deal('hej');
s.f
ans =
hej
ans =
hej
ans =
hej
[s.f] = d{:};
s.f
ans =
hi
ans =
    17
ans =
    3.1416

As you can see, I can bypass the function deal when assigning to a struct array in analogous way to cell array assignment.

References

Here are some prime references for getting more details on today's topic.

Why Do You Use deal?

I'd like to hear from you about situations where you feel using deal is your only option or you prefer it (perhaps readability?) to the no deal syntax. Let me know here.


Get the MATLAB code

Published with MATLAB® 7.5

18 Responses to “Deal or No Deal”

  1. Markus replied on :

    Say you have some struct containing names and phone numbers:

    s = struct(’name’, {}, ‘phone’, {})

    Then you can assign names to some fields in one line:

    [s(1:3).name] = deal(’Loren’, ‘Doug’, ‘Steve’);

    Without using deal, you would have to create a cell array and then assign:

    c = {’Loren’, ‘Doug’, ‘Steve’};
    [s(1:3).name] = c{:};

    In this case the use of deal allows more compact code.

    Markus

  2. Dan K replied on :

    Loren,
    I have always puzzled over the [c{:}] notation on the left hand side, but I think I may have had an epiphany. Is the reason for that notation that it is a shorthand form of the same method used for returning multiple outputs from a function? In short, is it simply the short form of writing:
    [c{1}, c{2}, c{3}]? I guess what this question is really indicating is I’m not sure *exactly* what square brackets mean on the left hand side of an expression. On the right hand side, it’s not problem: “concatenate these inputs into a new array.” But that doesn’t really hold for the left hand side, I think. Can you give the english statement which left hand side brackets is equivalent to?

    Thanks,
    Dan

  3. Omid replied on :

    Loren,
    I’m wondering why this works,

    [b{1:3}]=deal(1,2,3);
    [a{1:3}]=b{:};

    while this doesn’t,

    [a{:}]=1,2,3;
    ??? Too many output arguments.

    As far as I know, 1,2,3 is a comma-separated list too –dare I say just as good as b{:}? So why shouldn’t the latter work just as the former does?
    And actually, executing either

    b{:}

    or

    1,2,3

    gives the same output as one would expect,

    ans =
    1
    ans =
    2
    ans =
    3

    What is it that I’m missing here?

    -Omid

  4. Oliver A. Chapman, P.E. replied on :

    Loren,

    This is an important topic. But, I’m stumbling over the first part of the example.

    After your first example, the one that failed & generated an error, you say, “. . . because c is a cell array of length 3 and I used the comma-separated list notion.”

    Where is the comma-separated list notion? ‘aloha’ doesn’t look like a comma-separated list to my eye. I can’t find this term defined in the MatLab documentation, yet there are several pages of documentation regarding comma-separated lists. This appears to be a key concept that is well understood by MatLab developers, but not me.

    Although I can see that the failed syntax enclosed the target variable with the square brackets, I don’t understand the significance of the square brackets.

    Further, when I compare the failed syntax with the one without the square brackets, that didn’t generate an error, they seem very similar.

    Maybe the key question is, “How does the square brackets syntax interact with lists of comma-separated lists when the square bracket are used on the assignment side of the equals sign?”

    Thanks.

  5. Iain replied on :

    I have used deal when creating lambda functions that generate more than one output:

    @(x) deal(fn1(x), fn2(x))

    Actually, for this purpose I quickly swapped to using this version of deal:

    function varargout = mydeal(varargin)
    varargout = varargin(1:max(1,nargout));

    I wanted to create functions that behave like normal Matlab functions. That is, any outputs that aren’t asked for are silently discarded. Matlab’s deal throws an error if I do:

    fn = @(x) deal(x, 2*x); y = fn(2);

    So I use mydeal instead.

  6. Eric M. replied on :

    I try to avoid the deal function if at all possible. I have found that it can be much slower than using the commma seperated list notion.

  7. Loren replied on :

    Folks-

    Thanks for all the great comments. I think Markus and Iain have both pointed out times when using deal (or a doctored version) make sense.

    Dan, you are exactly right about what [] mean on the left side. It is the way MATLAB allows you to return more than one output and is not concatenation like it is when appearing on the right hand side. The comma-separated list c{1},c{2},c{3} is 3 outputs for MATLAB since each cell in a cell array is basically its own variable (though associated with all others in the same cell array). So [c{:}] builds up that list for you and allows you to return multiple left-hand sides from functions.

    Omid-
    MATLAB treats the physically appearance of , or ; or a newline as a line terminator. So 1,2,3 is 3 separate statements in MATLAB. However, the construct b{:} is treated as an entity unto itself, and allows concatenation into a regular or cell array when placed between [] or {}.

    Oliver-
    The comma-separated list referred to in that statement was for the left-hand side [c{:}] and not to ‘aloha’ which is a simple string value. The [] on the left allows me to assemble a list of output variables. And the c{:} in between builds the list c{1},c{2},c{3}.

    –Loren

  8. Oleg T. replied on :

    Hello, Loren and everyone.

    Deal is for sure a powerful tool, but the most frequent way I use deal is to swap values:
    [b,a] = deal(a,b);

  9. Daniel Goodman replied on :

    Ok, so:

    %what if I have a vector of integers:

    num_vec = [1 7 3 5 9];

    %and I structure array with same number of fields:

    [mystruct(1:5).a] = deal(’a',’b',’c',’d',’e');

    %I want to assign num_vec as the the ‘b’ field of mystruct.
    %The following does not work:

    mystruct = setfield(mystruct,{1:5},’b',num_vec)

    %and deal only works with cells:

    [mystruct.b] = deal(num_vec);

    There should be a one line way to assign a scalar array to
    a field of a structure array. Please tell me how to do
    this. Going between comma-separated lists, cells, and arrays is unnecessariy frustrating, and is one of my only major complaints about MATLAB.

  10. Loren replied on :

    Daniel-

    Convert your numbers into a cell array:

    mycell = num2cell(num_vec);

    Then you can do this:

    [mystruct.b] = mycell{:}

    The point is you have to have your RHS in cells to distribute them easily.

    –Loren

  11. Tom replied on :

    I have an observation about the performance of the expression mycell{:} - its running time is quadratic in the number of elements, which makes it quite slow for large number of elements. This also seems to affect the [s.field] syntax for structs.

    I have a cell array where each cell contains a numeric vector. What I’d like to do is concatenate certain subsets of the whole cell array, like this: cat(1, c{:}). However, look at the running times of c{:} with different number of elements (in 2008b) and notice how each increase by power of 10 increases the running time by about a factor of 100:

    >> for ii = [100 1000 10000 100000], clear c; c(1:ii)={1}; tic; c{:}; toc; end
    Elapsed time is 0.000056 seconds.
    Elapsed time is 0.003090 seconds.
    Elapsed time is 0.304656 seconds.
    Elapsed time is 45.346996 seconds.

    It would be interesting to know why this is happening. This, of course, also affects cell2mat - the last two lines of code from that file are

    % Finally, concatenate the final rows of cells into a matrix
    m = cat(1,c{:});

    Do you have any suggestions on how to achieve cat(1, c{:}) but with a reasonable running time?

    Thanks.

    Tom

  12. Loren replied on :

    Tom-

    Your test case is almost a worst-case scenario in that you are assigning all the cell values to the same array. In the guts of MATLAB, these are all pointing to the same memory which slows the {:} operation down more than if the values were distinct. In either case however there is quadratic performance as you observe (and the development team is aware of this). Alas, I do not have a timeframe for changing this runtime characteristic.

    The way to work around it is with a for loop, which should give linear speed with pre-allocation. Not as pretty, but hopefully should help you out for now.

    –loren

  13. Tom replied on :

    Loren,

    I also contacted support who explained this in a little more detail.

    In reality, my data will not be shared in this way, so this becomes a non-issue. Once I created a cell array with no shared cells, I got the performance characteristics I was expecting.

    Many thanks,

    Tom

  14. wei replied on :

    Loren,
    Question is if deal or better way to create structure array in the situation below? I tried but hadn’t succeeded.

    I have Stateflow.Data in a cell,
    >> dparam

    dparam =

    [1×1 Stateflow.Data] [2×1 Stateflow.Data]

    Then I create a structure array x by

    >>clear x; x=dparam{1}; x(2:3)=dparam{2};
    Now I can perform
    >> get(x,’Name’)

    ans =

    ‘gain’
    ‘gain’
    ‘gain1′

  15. Loren replied on :

    Wei-

    There does not seem to be any particular advantage to using deal with your data structure.

    –Loren

  16. wei replied on :

    Loren,
    I was trying to avoid a loop. Thank you for answering. Could deal or else help to eliminate temp variable ‘name’ below?

    >> name = get(da,’Name’);
    >> sfData = struct(’Name’,cell(3,1));
    >> [sfData(:).Name]=name{:};

  17. Loren replied on :

    Wei-

    I don’t know where you are talking about using a loop. It’s hard to tell what your final outcome is supposed to be. For now, I can’t tell if you can avoid the temporary variable, but is it really hurting anything?

    –Loren

  18. wei replied on :

    Loren,
    The loop is referring #14 where I have to index through cell array dparam to produce struct array x.

    In #16 array x is da
    >> da

    da =

    Stateflow.Data: 1-by-3

    From which I want to extract info to create a struct array sfData. Now I’m thinking to avoid temp ‘name’.

Leave a Reply

Wrap code fragments inside <pre> tags, like this:

<pre class="code">
a = magic(3);
sum(a)
</pre>

If you have a "<" character in your code, either follow it with a space or replace it with "&lt;" (including the semicolon).


Loren Shure works on design of the MATLAB language at The MathWorks. She writes here about once a week on MATLAB programming and related topics.

  • Loren: Dirga- You might consider looking at the function interp2. –loren
  • Dirga: I do not know whether the term I used is correct or not. I have set of measurement data. It is the density of...
  • Loren: Alex- You could overload sort in the same way that you create any class method. That would get you the shorter...
  • Alan Hester: can you put infomation in the interlaced images that the subconscious will pick up on and remember that...
  • Andrew Kraev: Hello, here is simple code to calculate for an optional vector a[i], i=1,..,n the sum by i of products,...
  • Alex: It would be nice to be able to sort an array of objects of a custom class, provided the class implements lt()...
  • Stu: Excellent post. No, I wasn’t aware of that property of for...end structures. Thank you!!
  • Loren: Eleanor- You either need an algorithm to determine which 0s should be replaced by NaN values or you will need...
  • Eleanor: Loren- I have a different sort of problem with NaNs and 0s. My data has zeros where data is missing, but...
  • Aslak Grinsted: Here’s another fun piece of code inspired by cellular automata. This one generates what looks...

These postings are the author's and don't necessarily represent the opinions of The MathWorks.