Creating a hard link among properties of an object?

Hello,
I have a class called Module that has 3 properties: InternalVariables, ExternalVariables, and AllVariables. InternalVariables and ExternalVariables both contain symbolic column vectors. I want to define AllVariables as the superposition of InternalVariables and ExternalVariables, and if I change either InternalVariables or ExternalVariables, AllVariables gets updated automatically. Is there a way to do this by using a hard link?
Thank you,
Kevin

 Accepted Answer

A couple of thoughts
Why not just make AllVariables a dependent property. Then every time it's queried it's calculated on the fly. MATLAB passes by reference so unless you have thousands of variables this should not be a performance penalty.
Another approach would be with listeners. The basic plan would be to have the constructor set listeners for changes to internal and external properties. This avoids having to have an explicit setter for each property. Of course, having an explicit setter for each property isn't necessarily a bad thing, especially if you need to validate the new property values anyway. In fact, I would probably just take this approach and use a setter every time.

4 Comments

Cedric
Cedric on 13 May 2014
Edited: Cedric on 13 May 2014
The way I understood the question, the OP stores what he calls internal and external variables in structs. I don't think that we can get the field name in a getter/setter, and if this is true, we cannot address the relevant struct (the one which has the field) when allVars is accessed.
We can query fieldnames (dynamically or not) of the structs as necessary. Why wouldn't this be possible in a setter or getter?
I built really fast a dummy example to illustrate
classdef TestClass < handle
properties
intVars
extVars
allVars
end
methods
function obj = TestClass()
obj.intVars = struct( 'a', 8:15, 'b', -3 ) ;
obj.extVars = struct( 'c', 7, 'd', 8 ) ;
end
function value = get.intVars( obj ), value = obj.intVars ; end
function set.intVars( obj, value ), obj.intVars = value ; end
function value = get.extVars( obj ), value = obj.extVars ; end
function set.extVars( obj, value ), obj.extVars = value ; end
function value = get.allVars( obj )
% If accessing e.g. TestClass.allVars.c, how do I know here
% that field 'c' was addressed => how can I pick extVars?
end
function set.allVars( obj, value )
% If accessing e.g. TestClass.allVars.c, how do I know here
% that field 'c' was addressed => how can I pick extVars?
end
end
end
On that note, have a good night! I'll be back tomorrow to go on the discussion.
I don't have much time right now (so will probably have to continue this tonight) but here the better of the two approaches I had in mind:
In the getter (recommended!)
classdef TestClass < handle
properties
intVars
extVars
end
properties (SetAccess = protected, Transient = true, Dependent = true)
allVars
end
methods
function obj = TestClass()
obj.intVars = struct( 'a', 8:15, 'b', -3 ) ;
obj.extVars = struct( 'c', 7, 'd', 8 ) ;
end
function val = get.allVars(obj)
val = obj.intVars;
fns = fieldnames(obj.extVars);
for ii = 1:numel(fns)
val.(fns{ii}) = obj.extVars.(fns{ii});
end
end
end
end
To use it:
T = TestClass
T.allVars
T.intVars.Hwllo = pi
T.allVars
T.extVars.World = exp(1)
T.allVars

Sign in to comment.

More Answers (1)

Cedric
Cedric on 12 May 2014
Edited: Cedric on 13 May 2014
NOTE: well, I spent an hour stuck in a mall with a tornado warning, so I had a little time to extend my first example. See EDIT below.
Did you try using setters and getters for these properties (in conjunction with dependent properties)? If they are stored in structs (and not as public/private/etc groups of properties), you may have to use subsasgn / subsref / substruct, etc.
EDIT : here is a new version of my previous example, which delegates indexing appropriately (I suppose, but I didn't have time to test) when fields of allVars contain arrays or structs.
classdef TestClass < handle
properties
intVars
extVars
allVars
end
methods
function obj = TestClass()
obj.intVars = struct( 'aInt', 3, 'bInt', 20:30, 'c', 7 ) ;
obj.extVars.dExt = struct( 'x', -2, 'y', 48 ) ;
obj.extVars.eExt = {'Hello', 'World'} ;
obj.extVars.c = 8 ;
end
function value = subsref( obj, S )
if length(S) > 1 && all( [S(1:2).type] == '.' ) && ...
strcmp( S(1).subs, 'allVars' )
is_int = isfield( obj.intVars, S(2).subs ) ;
is_ext = isfield( obj.extVars, S(2).subs ) ;
if is_int && is_ext
valueInt = obj.subsref_delegate( obj.intVars.(S(2).subs), S ) ;
valueExt = obj.subsref_delegate( obj.extVars.(S(2).subs), S ) ;
value = struct( 'int', valueInt, 'ext', valueExt ) ;
elseif is_int
value = obj.subsref_delegate( obj.intVars.(S(2).subs), S ) ;
else
value = obj.subsref_delegate( obj.extVars.(S(2).subs), S ) ;
end
else
value = builtin( 'subsref', obj, S ) ;
end
end
function obj = subsasgn_TODO( obj, S, value )
% .. your move.
end
end
methods (Static)
function value = subsref_delegate( x, S )
if length( S ) > 2
value = builtin( 'subsref', x, S(3:end) ) ;
else
value = x ;
end
end
end
end
With that..
>> tc = TestClass() ;
>> tc.intVars
ans =
aInt: 3
bInt: [20 21 22 23 24 25 26 27 28 29 30]
c: 7
>> tc.extVars
ans =
dExt: [1x1 struct]
eExt: {'Hello' 'World'}
c: 8
>> tc.extVars.dExt.x
ans =
-2
>> tc.allVars.aInt
ans =
3
>> tc.allVars.bInt
ans =
20 21 22 23 24 25 26 27 28 29 30
>> tc.allVars.bInt(7)
ans =
26
>> tc.allVars.c % c is a common property => output struct with int/ext values.
ans =
int: 7
ext: 8
>> tc.allVars.eExt{2}
ans =
World
>> tc.allVars.dExt.y
ans =
48

4 Comments

How can I use that to not have to re-assign AllVariables every time InternalVariables or ExternalVariables are changed? Can you give me a simple example?
Just edited my answer with an example. If it answers your question, still wait a day or so before accepting the answer, as others (like Matt J) might have good insights and/or a better approach to propose.
PS: you probably understood that we are overloading subsref and subsasgn, to catch accesses to allVars's fields. If you don't understand how this works, create the object tc:
>> tc = TestClass() ;
and place a break point at the beginning of subsref (place cursor on the line which starts with if length( S ) .., and press F12). Then evaluate
>> tc.intVars.aInt
Once in the debugger, look at the struct S and you will understand the logic (if not, ask me again). Note that the else clause, which is evaluated in situations that we don't want to manage (i.e. when the user is not indexing a field of allVars), with the call
... = builtin( 'subsref', ..
is just delegating the management of the indexing to the builtin subsref (meaning not our overloaded version).
Interesting comment on the tornado warning - I wonder if it was the same one that kept me at the hotel yesterday outside of Detroit.
Ah! Yes, I am in Ann Arbor ;-)

Sign in to comment.

Categories

Community Treasure Hunt

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

Start Hunting!