I've a question concerning classes in MATLAB.
I'm writing a parser, that doesn't always have the same input. Some variables are not defined at all times. Here is a short mock up script:
test_parser.m
classdef test_parser < matlab.mixin.Copyable
properties (AbortSet = true)
a
b
end
end
make_class.m
function result = make_class(array)
result = test_parser;
result.a = array(1);
if length(array)>1
result.b=array(2);
end
end
Now calling from the command window with different input lengths:
>> make_class([10])
ans =
test_parser with properties:
a: 10
b: []
>> make_class([10,20])
ans =
test_parser with properties:
a: 10
b: 20
In both cases the variable b is a property of test_parser, as specified. My wish would be, that b is optional, so just present if there is b in the input.
What is the best way to achieve this? I guess an optional parameter is not really a property?
If you want optional properties, you can inherit your class from dynamicprops. You can then add properties on the fly using the command addprop, test whether a property exists using isprop, and even respond to properties being added or removed by listening to the events PropertyAdded and PropertyRemoved.
In your example, you would then use:
classdef test_parser < matlab.mixin.Copyable & dynamicprops
properties (AbortSet = true)
a
end
end
function result = make_class(array)
result = test_parser;
result.a = array(1);
if length(array)>1
result.addprop('b')
result.b=array(2);
end
end
>> make_class([10])
ans =
test_parser with properties:
a: 10
>> make_class([10,20])
ans =
test_parser with properties:
a: 10
b: 20
Related
Suppose I have a function defined in foo.m. This function can take a parameter thing of type struct. Once foo makes changes to thing, I want to "lock" thing so that it can no longer be changed. I essentially want to make it constant. I want to do this to ensure it isn't modified further down the line. How do I do this in Matlab?
You should
define the variable in the function to be persistent
lock your function in the memory using mlock.
mlock locks the currently running function in memory so that subsequent clear functions do not remove it. Locking a function in memory also prevents any persistent variables defined in the file from getting reinitialized.
Solution 1: Good if you don't know what form your struct will have in advance
You could 'capture' that variable with an anonymous function handle and only refer to your structure with that from now on. An anonymous function handle captures the state of the workspace at the time it is created. You will be able to access its elements as if it were the original struct, but if you try to assign to it, you'll generate an error.
E.g.
>> S_.a = 1;
>> S_.b = 2;
>> S = #() S_;
>> S_.a = 3;
>> S_
S_ =
scalar structure containing the fields:
a = 3
b = 2
>> S()
ans =
scalar structure containing the fields:
a = 1
b = 2
It's almost identical in syntax, except for the annoyance that you'll have to call it with ().
I've used it on the terminal here, but obviously it can easily also be used in the context of a function.
Small caveat; if you redefine and overwrite the anonymous function, obviously, this backfires, since it will inherit whatever new workspace it had access to at the time of the redefinition.
Solution 2: Good if you know your struct's form in advance:
Assume you know in advance that your struct will only contain fields a and b. Create a class with the same properties restricting 'SetAccess', e.g.
classdef ConstStruct
properties (GetAccess = 'public', SetAccess = 'private')
a
b
end
methods
%constructor
function obj = ConstStruct(S)
obj.a = S.a;
obj.b = S.b;
end
end
end
Then in your main code:
>> MyStruct = struct('a',1,'b',2)
MyStruct =
a: 1
b: 2
>> MyStruct = ConstStruct(MyStruct)
MyStruct =
ConstStruct with properties:
a: 1
b: 2
>> MyStruct.a
ans =
1
>> MyStruct.a = 2
You cannot set the read-only property 'a' of 'ConstStruct'.
What I want to do is:
a = 5
foo = #(x) x+a
a = 3
foo(1) % recieve 4
Instead, I only get 6! On several other tests I ran, I get that a is evaluated when foo is, and and not when foo is called.
For various reasons, I can't work with
foo = #(x,a) x+a
What you are asking to do is not recommended. It will be difficult to debug.
That said, it can be done using the evalin function to reach out and get the current value of a.
a=5;
foo = #(x)evalin('caller','a')+x;
foo(1) %Returns 6
a=3;
foo(1) %Returns 5
Better (much better!) would be to push the definition of a into an apprpriate data structure or object, and create a function getCurrentValueOfA(). Then you can define foo as
foo = #(x) getCurrentValueOfA() + x;
When you create an anonymous function in Matlab it stores the current value of any variables that it needs and that are not part of its inputs.
So, when you created foo like this:
a = 5
foo = #(x) x+a
It stored the equivalent of this:
foo = #(x) x+5
Even if you change later the value of a the value of that constant stored inside foowon't change.
If in the other hand you want the value of a to change, you have to accept a as a parameter of the function as well.
Source: http://www.mathworks.com/help/matlab/matlab_prog/anonymous-functions.html
You can check the value of stored data with the functions command:
>> a = 5
foo = #(x) x+a
a =
5
foo =
#(x)x+a
>> handleInfo = functions(foo)
handleInfo =
function: '#(x)x+a'
type: 'anonymous'
file: ''
workspace: {[1x1 struct]}
>> handleInfo.workspace{1}
ans =
a: 5
I create my own class as below:
classdef testClass < handle
properties
value;
map = containers.Map('KeyType','double','ValueType','any');
end
end
My goal is for each object of testClass to maintain its own map. However, it turns out that there is only one map object that for the whole class: all objects of testClass access to the same containers.Map. For example, if I create two objects as follows
a = testClass;
b = testClass;
a.value = 'a';
b.value = 'b';
a.map(1) = 123;
b.map(2) = 321;
It ends up both a and b's map contains two key-value pairs:
>> a
a =
testClass handle
Properties:
value: 'a'
map: [2x1 containers.Map]
>> b
b =
testClass handle
Properties:
value: 'b'
map: [2x1 containers.Map]
Methods, Events, Superclasses
Both (key,value) pairs (1,123) and (2,321) appears in both a.map and b.map
>> a.map.keys
ans =
[1] [2]
>> a.map.values
ans =
[123] [321]
>> b.map.keys
ans =
[1] [2]
>> b.map.values
ans =
[123] [321]
Is this a bug? How can I keep independent containers.Map for each class object?
The problem is not that testClass is a handle, but rather that the initial value specified in the properties block is not evaluated when you think it is. MATLAB calculates the default value of class properties only once when the class is loaded, and then gives that value to each new instance of your class.
You can see this by looking at the metaclass for your testClass. For example:
c = testClass;
c.map(1) = 42;
hc = ?testClass;
hc.PropertyList(2).DefaultValue.keys % returns [1]
hc.PropertyList(2).DefaultValue.values % returns [42]
If you want each instance to have a different map, you must construct the map explicitly in the constructor. (And yes, I've been there, done that).
If I make the following toy class in MATLAB:
classdef testIt
properties
a
b
c
end
methods
function obj = testIt
obj.a = 1;
obj.b = 2;
end
function obj = set.a(obj,a)
obj.a = a;
end
function obj = set.b(obj,b)
obj.b = b;
end
function obj = addup(obj)
obj.c = obj.a + obj.b;
end
end
end
and then instantiate and call the addup method:
>> aTest = testIt
Properties:
a: 1
b: 2
c: []
>> aTest.addup
Properties:
a: 1
b: 2
c: 3
>> aTest
Properties:
a: 1
b: 2
c: []
The property c has not been created. Instead, I need to use the syntax:
>> aTest = aTest.addup
>> aTest
Properties:
a: 1
b: 2
c: 3
Can anyone explain to me why this is necessary?
Matlab supports two types of classes: handle classes, and value classes.
Value classes operate similar to structures and other Matlab variables, in that they are passed by value. Thus, if you want to modify an instance of a value class within a function, you need to return and overwrite the instance in the calling workspace.
Handle classes, on the other hand, are passed by reference. If you change the value of a property anywhere, the class is updated in all workspaces. For example, you can have a reference to the object in the base workspace, and one inside a GUI, and if you modify one, the other changes its value accordingly.
If you change your code to classdef testIt < handle, the objects will behave exactly the way you expect.
Also: Have a look at the documentation
add to the class definition:
classdef testIt < handle
I have created my own class to act like an enumerated type. I haver overridden the method disp() so when a variable containing that type is displayed in the Command Window it shows something meaningful (specifically, the name of that enumerated value.)
classdef MyEnumeratedType
properties(Constant)
ENUMVAL1 = MyEnumeratedType(1, 'ENUMVAl1');
ENUMVAL2 = MyEnumeratedType(2, 'ENUMVAL2');
ENUMVAL3 = MyEnumeratedType(3, 'ENUMVAL3');
end
properties(Access=private)
ordinal
name
end
methods(Access=private)
function this = MyEnumeratedType(ord, name)
this.ordinal = ord;
this.name = name;
end
end
methods
function disp( this )
disp(this.name);
end
end
end
So when I assign it to a variable in the command window, I get the desired output:
>> x = MyEnumeratedType.ENUMVAL2
x =
ENUMVAL2
So far so good. BUT when I assign a value of type MyEnumeratedType to the field of a structure, the display of that structure doesn't display the value, but only tells me that I have a value of type MyEnumeratedType.
>> mystruct.field1 = 42
mystruct =
field1: 42
>> mystruct.field2 = MyEnumeratedType.ENUMVAL3
mystruct =
field1: 42
field2: [1x1 MyEnumeratedType]
How do I get the value of field2 to show up like it does for the double value in field1?
The disp method for structures shows the contents of numeric and cell arrays, if they can be written in a row, and the class/size info otherwise:
s = struct('a',1,'b',[1 2 3],'c',{{1}},'d',magic(3),'e',[1;2])
s =
a: 1
b: [1 2 3]
c: {[1]}
d: [3x3 double]
e: [2x1 double]
Consequently, to have the value of your enum displayed, you need to overload disp for structures. To do this, you create an #struct directory on your path, and create your own disp method that hopefully reproduces faithfully what Matlab does, but with an exception for your particular class. In short: it's possible, but I'd rather not be the one doing that.
This submission http://www.mathworks.com/matlabcentral/fileexchange/48637 attempts to recreate the disp function pretty well. So, you could use this for the disp.m you put in the #struct folder.