Anonymous Function : asymmetric read/write behaviour

I'm trying to use function handles to create an equivalent to a "with" statement in VBA or a alias in Python but I find myself facing a behaviour I don't understand. maybe some of you could help me understand why I get an error.
Boiled down to the simplest, here's what I get:
a=@(x)x{1};
y={10};
a(y)
ans = 10
try a(y)=100
catch ME
ME
end
ME =
MException with properties: identifier: 'MATLAB:matrix:unableToUseTypeAsIndex' message: 'Unable to use a value of type cell as an index.' cause: {} stack: [3×1 struct] Correction: []
The reading part works well but the next line results in an error. Why is there an asymmetry while evaluating the parentasis?
EDIT :
The most efficient to create a VBA:with or Python:alias equivalent in Matlab I found is to do an actual copy of a structure subpart, make modifications and reassign the edited branch copy to its original structure as:
S=struct();
S.Branch0 = "Just Some Data";
S.Branch1=struct();
for i=1:10
% Setting up preexisting values in the structure
for j=1:10
S.Branch1(i).NotCopiedProperty{j} = i;
end
S.Branch1(i).CopiedProperty = 10:-1:1;
% Create a phantom copy of the whole branch
with = S.Branch1;
% Add properties to the phantom branch
with(i).NewProperty1='Some Text';
with(i).NewProperty2={'A1','A2'};
% Create a copy of the original data and the edit it
with(i).CopiedProperty(i) = 0;
% Overwrite the original subBranch with the phantom branch
S.Branch1=with;
end
disp(S)
Branch0: "Just Some Data" Branch1: [1×10 struct]
disp(S.Branch1)
1×10 struct array with fields: NotCopiedProperty CopiedProperty NewProperty1 NewProperty2
Limitations : If new fields are to be added in a loop, the "with" structure must capture all instances at looping level.
% DOES NOT WORK
with = S.Branch1(1); % Points to ELEMENT
with.NewProperty1='New Text';
S.Branch1(1)=with;
% DO INSTEAD :
with = S.Branch1; % Points to ARRAY
with(i).NewProperty1='New Text';
S.Branch1=with;
% STILL WORKS
with = S.Branch1(1);
with.ExistingProperty1='Replacement Text';
S.Branch1(1)=with;

2 Comments

What exactly are you hoping to do with that last line? If a(y) returned 10 on both sides, what would you expect this command to do in MATLAB?
10 = 100
They are trying to create an alias. So they would like a(y) to be an alias for y{1} including for assignment purposes. They would like a(y)=100 to act the same as y{1} = 100

Sign in to comment.

 Accepted Answer

a(1) = @(x)x{1}
a = function_handle with value:
@(x)x{1}
a(1) = @(x)x{1}.^2 + 3
a = function_handle with value:
@(x)x{1}.^2+3
a
a = function_handle with value:
@(x)x{1}.^2+3
When an anonymous function appears on the left of an = then it is a request to modify the anonymous function. If the anonymous function already exists then the request will fail unless the subscript is one of 1 or true or false or []
If you want to be able to use a syntax like a(y)=100 to mean y{1}=100 then you would need to define a class to do the operations, and y would have to be a handle class
There is no simple "syntactic sugar" in MATLAB that can emulate with

6 Comments

In MATLAB, when you have something along the lines of
A = 7;
where the left hand side is completely unindexed, then no matter what class A is this would be a request to completely overwrite A with the new value. So MATLAB is completely unable to handle a situation where you want A to resolve to some kind of property in an object. You might want the ability to define something like
A = temperature_object(-2, 'C')
A = 7
as meaning to change the temperature stored in A to 7 while preserving the units... but that is just not possible with that syntax in MATLAB; MATLAB would clobber A and write in plain numeric 7 in this example.
If you have a numeric class, or an object that does not redefine indexing, and you index it at the left side of an assignment, like
A(y) = 100
then whatever is in () will be evaluated as an index into the array, and an error would be generated if the (y) does not resolve to a logical value or to [] or to a non-negative integer. If it does resolve to one of those then the value of y would be used as the offste into the array to assign the new value into (possibly after type conversion)
When your y is a cell array, then your a(y) = 100 was a request to use the cell array as an index into a -- but cell arrays are not valid indexes in most cases.
If you have an object that redefines subsref or matlab.mixin.indexing.RedefinesParen class then the object class is free to define indexing however it feels fit. table is an example of a class that redefines () indexing to permit cell arrays and numeric arrays in very useful ways .
So it is possible to define a class that could accept a cell array (such as y) as an () index for assignment purposes, and do something useful with it. However, what would get changed is the data associated class object a not the data associated with y .
For aliasing purposes, you would like to be able to say something like
a = With_Reference({1}); %assuming With_Reference is a class
y={10,20};
a(y) = 100;
and afterwards expect that now y = {100,20} . But y is stored outside of a . In order for the hypothetical With_Reference class to be able to modify an object outside of itself, then either the object to be modified must be a handle object, or else the hypothetical With_Reference would have to be written in terms of using assignin
I made a workaround copying the part of structure I was working on, instead of trying to point at it:
With=Part.Of{ i }.Structure( j );
With.Properties=SomeData;
Part.Of{ i }.Structure( j )=With
Of course it's slower and less memory efficient, but it allows for a better readability of the code.
"Of course it's slower and less memory efficient"
How exactly do you know that?
Like that
%%Creating a structure
s=struct();
s.Parent.Property1=100000;
s.Parent.Property2=1:s.Parent.Property1;
%% Measuring ct with direct access (pointer like)
tic
for i=1:s.Parent.Property1
% this line writes zeros, to the array while requiring some calculation
% time
s.Parent.Property2(i)=abs(s.Parent.Property2(i))-i;
end
m.direct=seconds(toc);
%% Measuring ct creating a copy of the sub-structure for every calculation
% "with" statement inside the loop to evaluate the impact on a sample greater
% than 1
s.Parent.Property2=1:s.Parent.Property1;
tic
for i=1:s.Parent.Property1
with=s.Parent;
with.Property2(i)=abs(with.Property2(i))-i;
s.Parent=with;
end
m.with=seconds(toc);
disp(m);
direct: 0.073754 sec with: 4.315 sec
%% Using eval can't work for the same reasons as the anonymous function
with = "s.Parent";
% Works as expected when the function is on the right side of = (read)
output=eval(with).Property1
output = 100000
% Throws an error when the function in on the left side (write)
eval(with).Property1=0;
Unable to use a value of type string as an index.
Copying a part of the structure N times and edit will obviously take longer than accessing and editing the data directly.
More time also means a greater memory usage as there are 2 instances of the structure or object at any time in the loop using the "With" variation.
If you only extract the Parent property once (since you're not modifying it in your second loop) the times tell a different story:
%%Creating a structure
s=struct();
s.Parent.Property1=100000;
s.Parent.Property2=1:s.Parent.Property1;
%% Measuring ct with direct access (pointer like)
tic
for i=1:s.Parent.Property1
% this line writes zeros, to the array while requiring some calculation
% time
s.Parent.Property2(i)=abs(s.Parent.Property2(i))-i;
end
m.direct=seconds(toc);
%% Measuring ct creating a copy of the sub-structure for every calculation
% "with" statement inside the loop to evaluate the impact on a sample greater
% than 1
s.Parent.Property2=1:s.Parent.Property1;
tic
with=s.Parent;
for i=1:with.Property1
with.Property2(i)=abs(with.Property2(i))-i;
end
s.Parent=with;
m.with=seconds(toc);
disp(m);
direct: 0.075196 sec with: 0.022997 sec
More time also means a greater memory usage as there are 2 instances of the structure or object at any time in the loop using the "With" variation.
It's not that simple. MATLAB does something known as copy-on-write, in which it only makes copies of an array when it needs to.
tic
A = ones(1e4);
toc
Elapsed time is 0.256635 seconds.
tic
B = A;
toc
Elapsed time is 0.001599 seconds.
If the statement B = A; made a copy of A, it would have taken a similar amount of time as the line that created A. Instead, the copy's only made when B is modified.
tic
B(1) = 2;
toc
Elapsed time is 0.356562 seconds.
How does this work with struct arrays?
tic
S = struct('A', ones(1e4), 'C', 1);
toc
Elapsed time is 0.252987 seconds.
tic
T = S.C;
T(1) = 2;
toc
Elapsed time is 0.002666 seconds.
tic
S.C = T;
toc
Elapsed time is 0.001336 seconds.
The lines that create or manipulate T don't interact with S.A at all.
These are very interresting findings!
In my case, the idea is to iterate through an index that would be applied at the with definition level and modify the data. That said, with only modified parts of the substructure being actively copied only when they are being modified, the workaround may seem more efficient than I expected.
Thanks for the insight!
I foresee the possible useage of a "with" cells array pointing to various subpart in the structure, allowing for intricate "with". It has to be tested.

Sign in to comment.

More Answers (0)

Products

Release

R2023b

Community Treasure Hunt

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

Start Hunting!