Isn't it dangerous to allow Comma-Separated-List functionality in indexed assignments?

6 views (last 30 days)
I have a function below called renamefields() which, as the name suggests, renames a desired subset of struct array fields as desired. It works now, as the following simple example shows,
[S(1:2).a]=deal(10,20);
T=renamefields(S,'a','b');
[S.a]
ans = 1×2
10 20
[T.b]
ans = 1×2
10 20
Prior to getting it working, though, I had a bug in the code that I've also included below as renamefieldsBUG(). Repeating the steps above, you can see the effect:
T=renamefieldsBUG(S,'a','b');
[T.b]
ans = 1×2
10 10
I know why this occurs. It is because in the buggy version, the right hand side of the assignment S.(srcfields{j}) is a comma-separated list. When the left hand side of a comma-separated list assignment has only a single variable, all but the first element of the list is discarded (see doc). Is this not dangerous behavior, however? It seems like if you wanted this behavior, it should be done with an explicit command. Otherwise, as the example shows, code can be bug-prone.
function S=renamefields(S,srcfields,destfields)
%Rename fields
%
%S=renamefield(S,srcfields,destfields)
N=numel(S);
if ischar(srcfields), srcfields={srcfields}; end
if ischar(destfields), destfields={destfields}; end
M=numel(srcfields);
assert(M==numel(destfields),'src and dest length don''t agree.')
for i=1:N
for j=1:M
S(i).(destfields{j})=S(i).(srcfields{j});
end
end
S=rmfield(S,srcfields);
end
function S=renamefieldsBUG(S,srcfields,destfields)
%Rename fields
%
%S=renamefield(S,srcfields,destfields)
N=numel(S);
if ischar(srcfields), srcfields={srcfields}; end
if ischar(destfields), destfields={destfields}; end
M=numel(srcfields);
assert(M==numel(destfields),'src and dest length don''t agree.')
for i=1:N
for j=1:M
S(i).(destfields{j})=S.(srcfields{j}); %<----BUG: should be S(i) on right side
end
end
S=rmfield(S,srcfields);
end
  2 Comments
Paul
Paul on 2 Feb 2023
Isn't this behavior needed when calling a function with fewer output arguments than the function actually returns? That is, I thought a function returns a CSL, the elements of which are paired up with the outputs on the calling side.
a = test
a = 1
Hmm, now that I've typed that, I'm not so sure that a CSL is the actual mechanism for return arguments.
function [a,b] = test
a = 1;
b = 2;
end
Matt J
Matt J on 2 Feb 2023
@Paul Isn't this behavior needed when calling a function with fewer output arguments than the function actually returns?
I have no issue with having that behavior in function call syntax. My issue is having it with indexing syntax.

Sign in to comment.

Answers (1)

Walter Roberson
Walter Roberson on 2 Feb 2023
Consider
x = rand(1,10);
y = power(max(x),2) %also known as y = max(x).^2
How many outputs does max(x) potentially have? Two. Should we require an explicit syntax for discarding all outputs other than the first? It could not be
y = power(discard_trailing_output(max(x)), 2)
-- not without a very serious backwards incompatibility.
Effectively, every function that potentially has more than one output, returns a comma separated list. A function that really cares can detect the situation by examining nargout and chosing not to assign to variables named in the corresponding output positions, or by choosing to assign only to as much of varargout as nargout says is used. But a relatively small fraction of functions do that; most return multiple variables effectively as CSL and MATLAB discards the extras.
  2 Comments
Matt J
Matt J on 2 Feb 2023
Edited: Matt J on 2 Feb 2023
Should we require an explicit syntax for discarding all outputs other than the first?
Yes, as I've suggested in my post, it would be sufficient to require a function call syntax. max() already has that syntax, so one is already alert to the fact that additional outputs are discarded.
What is treacherous in my posted example is that the behavior happens for indexing expressions. Normally, with indexing expressions, we are warned that the right side and left side don't match, e.g.,
x(1)=1:4
Unable to perform assignment because the indices on the left side are not compatible with the size of the right side.
Matt J
Matt J on 2 Feb 2023
Edited: Matt J on 2 Feb 2023
What is also a bit strange to me is that the deal() command has the opposite behavior,
[a,b]=deal(1,2,3)
Error using deal
The number of outputs should match the number of inputs.
Why isn't the 3, which wasn't requested, simply discarded? To my tastes, it would be better/safer if deal would discard unrequested outputs. We could then use that to get this behavior if we wanted it.

Sign in to comment.

Products


Release

R2022a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!