As I do not seem to be able to edit my old question (Matlab - Function taking no arguments but not static), here it is again:
I am trying to implement the following:
classdef asset
properties
name
values
end
methods
function AS = asset(name, values)
AS.name = name;
AS.values = values;
end
function out = somefunction1
ret = somefunction2(asset.values);
out = mean(ret);
return
end
function rets = somefunction2(vals)
n = length(vals);
rets = zeros(1,n-1);
for i=1:(n-1)
rets(i) = vals(i)/vals(i+1);
end
return
end
end
end
But I am getting the error that somefunction1 should be static. But if it's static then it can't access the properties anymore. How would I resolve this issue?
Basically I want to be able to write something like this:
AS = asset('testname',[1 2 3 4 5]);
output = AS.somefunction1();
as opposed to writing
AS = asset('testname',[1 2 3 4 5]);
output = AS.somefunction1(AS);
For accessing the properties of an object in a method, you need to pass that object as argument to the method. If you don't need a specific object in order to perform a function, then make it static (belongs to the class, but does not operate on a specific object).
So, compare the original code:
methods
% ...
function out = somefunction1
ret = somefunction2(asset.values);
out = mean(ret);
return
end;
function rets = somefunction2(vals)
n = length(vals);
rets = zeros(1,n-1);
for i=1:(n-1)
rets(i) = vals(i)/vals(i+1);
end
return
end
end
with the correct code:
methods
% ...
% this function needs an object to get the data from,
% so it's not static, and has the object as parameter.
function out = somefunction1(obj)
ret = asset.somefunction2(obj.values);
out = mean(ret);
end;
end;
methods(Static)
% this function doesn't depend on a specific object,
% so it's static.
function rets = somefunction2(vals)
n = length(vals);
rets = zeros(1,n-1);
for i=1:(n-1)
rets(i) = vals(i)/vals(i+1);
end;
end;
end;
To call the method, you'd write indeed (please test):
AS = asset('testname',[1 2 3 4 5]);
output = AS.somefunction1();
because, in MATLAB, this is 99.99% of cases equivalent to:
AS = asset('testname',[1 2 3 4 5]);
output = somefunction1(AS);
The differences appear when you're overriding subsref for the class, or when the object passed to the method is not the first in the argument list (but these are cases that you should not concern with for now, until you clarify the MATLAB class semantics).
Related
I work within a project which has several classes which define properties that use essentially the same set method. To make the code more readable, I want to implement a commonSetter method. The overall goal is to include this commonSetter method in the superclass, so that all the classes could use it.
The question was already posted here, but unfortunately, the answer is not working. I changed the code to the following, but get the error: Maximum recursion limit of 500 reached.
classdef MyClass
properties
A
B
end
methods
function mc = MyClass(a,b) % Constructor
mc.A = a;
mc.B = b;
end
function mc = set.A(mc, value) % setter for A
mc = mc.commonSetter(value, 'A');
end
function mc = set.B(mc, value) % setter for B
mc = mc.commonSetter(value, 'B');
end
end
methods(Access = protected)
function mc = commonSetter(mc, value, property)
% do some stuff
disp('Made it into the commonSetter!')
mc.(property) = value;
end
end
end
So far I know that there is an infinite loop where mc.(property) = value; calls set.A (or set.B), which in turn calls commonSetter.
In my post # MathWorks the following was suggested:
To break that loop I guess you should look at builtin() and subsasgn(). Maybe Overriding subsref and subsasgn - effect on private properties can be of some help.
Currently, I have troubles realizing the suggestions and additionally not very comfortable to overwrite subsasgn() as I'm not sure how it will affect the overall project. I would like to know, if someone has other ideas or knows how to overwrite subsasgn() safely.
To solve the recursion error, you could just let the commonSetter method output the new value instead of the object.
classdef MyClass
properties
A
B
end
methods
function mc = MyClass(a, b)% Constructor
mc.A = a;
mc.B = b;
end
function mc = set.A(mc, value)% setter for A
mc.A = mc.commonSetter(value, 'A'); % update mc.A
end
function mc = set.B(mc, value)% setter for B
mc.B = mc.commonSetter(value, 'B');
end
end
methods (Access = protected)
function new_value = commonSetter(mc, value, property) % only return the new value
% do some stuff
disp('Made it into the commonSetter!')
if value > 5
new_value = -10;
else
new_value = value;
end
end
end
end
I'm trying to solve differential equation using the ode45 function. Consider the following code,
[t1,X2] = ode45(#(t,x)fun(t,x,C1,C2,C3,C4),t0,X01);
where parameters C1, C2, C3 and C4 are column vectors, which should be available to the function that ode45 is referring to (fun.m). I want the values to change after every iteration, so for example, at the beginning the entry of C1 I want in is C1(1), in the next iteration it's C1(2), etc.
How can I implement that?
You may have noticed that the official docs are not too helpful in this scenario (as they pretty much force you to use global variables - which is doable, but discouraged). Instead, I'll show you how this can be done with classes and function handles. Consider the following:
classdef SimpleQueue < handle
%SIMPLEQUEUE A simple FIFO data structure.
properties (Access = private)
data
position
end
methods (Access = public)
function obj = SimpleQueue(inputData)
%SIMPLEQUEUE Construct an instance of this class
obj.data = inputData;
rewind(obj);
end % constructor
function out = pop(obj, howMany)
%POP return the next howMany elements.
if nargin < 2
howMany = 1; % default amount of values to return
end
finalPosition = obj.position + howMany;
if finalPosition > numel(obj.data)
error('Too many elements requested!');
end
out = obj.data(obj.position + 1 : obj.position + howMany);
obj.position = finalPosition;
end % pop
function [] = rewind(obj)
%REWIND restarts the element tracking
% Subsequent calls to pop() shall return elements from the beginning.
obj.position = 0;
end % rewind
end % methods
end % classdef
How to use this? Simple:
C1q = SimpleQueue(C1);
C2q = SimpleQueue(C2);
C3q = SimpleQueue(C3);
C4q = SimpleQueue(C4);
[t1,X2] = ode45(#(t,x)fun(t,x,#C1q.pop,#C2q.pop,#C3q.pop,#C4q.pop),t0,X01);
As you can see, inside fun we use C1q() instead of C1.
Minimalistic Example:
classdef MyClass
properties
arr
handArr
end
properties(Dependent)
rowAcc
colAcc
end
methods
function obj = MyClass(arr, handRow, handCol)
obj.arr = arr;
obj.handArr{1} = handRow;
if ~isequal(handRow, handCol)
obj.handArr{2} = handCol;
end
end
function r = get.rowAcc(obj)
r = obj.handArr{1}(obj.arr);
end
function c = get.colAcc(obj)
c = obj.handArr{end}(obj.arr);
end
end
end
Now assume I pass equal functions to the constructor, I want the row and col access would also be the same:
f=#(x)#(y) y;
x=MyClass(1, f, f);
isequal(x.rowAcc, x.colAcc) //should be 1
Is this possible?
I have a good reason for this 'insane' requirement:
I have several algorithms which run with 100+ MBs of input and takes those two functions as input, and when they are equal they can be optimized very efficiently; to call the algorithms I need to make transformations to the input functions which are encapsulated inside this class. I can't change the algorithms (not my code) and they use isequal on they're own functions to dispatch.
Two variables pointing to the same anonymous function are considered to be equal
f = #(x)x;
g = f;
isequal(f, g)
% 1
However, if you define the anonymous functions at different times, then they are not considered to be equal because the internal workspaces of the two functions could differ.
f = #(x)x;
g = #(x)x;
isequal(f, g)
% 0
In order to have your property return equal handles, you could have some "shadow" property (accessors_) which caches the accessors and you update these cached values whenever the arr property is changed.
classdef MyClass
properties
arr
handArr
end
properties (Access = 'protected')
accessors_ % An array of accessor functions for rows & columns
end
properties (Dependent)
rowAcc
colAcc
end
methods
function set.arr(obj, value)
% Set the value
obj.arr = value;
% Update the accessors_
self.accessors_ = obj.handArr{1}(obj.arr);
% Only assign another accessor if we have a second one
if numel(obj.handArr) > 1
self.accessors_(2) = obj.handArr{2}(obj.arr);
end
end
function res = get.rowAcc(obj)
res = obj.accessors_(1);
end
function res = get.colAcc(obj)
% If only one was stored, this will return a duplicate of it
res = obj.accessors_(end);
end
end
end
This also has the added benefit that you aren't creating function handles every time that colAcc and rowAcc are retrieved.
I have a class that encapsulates access to an array in a wierd way;
The class constructor takes a function handle which is some kind of transformation of indexes before passing them to the array
classdef MyClass
properties
arr
accessHandle
end
methods
function obj = MyClass(array, trans)
obj.arr = array;
obj.accessHandle = #(i) obj.arr(trans(i))
end
end
The problem is the anonymous function copies the array into it's own workspace and if we change the array, it doesn't change in the function.
Essentially what is needed is to pass to the anonymous function the 'this' pointer/reference like in Java/C++/etc
The simple solution is to create a handle to the array and pass it along to the function:
classdef MyClass
properties
arr
accessHandle
end
methods
function obj = MyClass(array, trans)
obj.arr = array;
tmp = PropertyReference(obj, 'arr'); %See http://stackoverflow.com/questions/7085588/matlab-create-reference-handle-to-variable
%for the definition
obj.accessHandle = #(i) tmp(trans(i));
end
end
end
The problem now is when I pass an instance of the class to a function, the reference passed still refers to the object outside the function:
function foo(ins)
ins.arr = [1 2];
disp(ins.accessHandle(1));
end
cl = MyClass([0 3], #(x) x);
foo(cl) //output 0 instead of 1
disp(ins.accessHandle(1)) //output 0
EDIT: The class should be a value class, the semantics are that when a copy of the class is made, the accessHandle field changes the array handle it uses.
How can I achieve the right semantics ?
Currently, your class is a value class (MATLAB's default). If you want to be able to pass objects around by reference (changes will be reflected in the original object), you'll to make it a handle class by subclassing handle
classdef MyClass < handle
properties
arr
accessHandle
end
methods
function obj = MyClass(array, trans)
obj.arr = array;
obj.accessHandle = #(i) obj.arr(trans(i))
end
end
end
And then you can use it the way you expect
m = MyClass([1 2 3], #(x)x);
m.accessHandle(2)
% 2
m.arr(2) = 5;
m.accessHandle(2)
% 5
More information about the difference between the two can be found here.
Edit
If you need MyClass to be a value class, then you could store the trans value as a property and then have a normal method to access the value
classdef MyClass
properties
arr
trans
end
methods
function obj = MyClass(array, trans)
obj.arr = array;
obj.trans = trans;
end
function res = access(obj, k)
res = obj.arr(obj.trans(k));
end
end
end
Or if you want, you could make accessHandle a dependent property that returns a function handle.
classdef MyClass
properties
arr
trans
end
properties (Dependent)
accessHandle
end
methods
function obj = MyClass(array, trans)
obj.arr = array;
obj.trans = trans;
end
function res = get.accessHandle(obj)
res = #(i)obj.arr(obj.trans(i));
end
end
end
I'd like to iterate over Matlab classes. Is this possible?
My application has several subclasses based on different structures and algorithms, but each subclass implements the same (class and instance) methods. So it is natural for me to have test-cases which have the subclass as a parameter. In Ruby, for example, this is would be easy
[ClassA, ClassB].each do |cls|
if cls.some_class_method? then
instance = cls.new
:
end
end
but I don't seem to be able to find a way to do this in Matlab, there don't seem to be "class handles".
Is there a way?
[edit]
Following #zeeMonkeez's solution I ended up with the following
function varargout = call_method(class_name, method_name, varargin)
qualified_name = sprintf('%s.%s', class_name, method_name);
[varargout{1:nargout}] = feval(qualified_name, varargin{:});
end
which I can call in my test-case like
class_name = 'MVPolyCube';
:
[x, y, z] = call_method(class_name, 'variables');
which solves my problem and DRYs up my test suite. Thanks all!
If you don't mind using feval, you could do something like the code below.
Basically, we test if a static method of a given name exists for a class name (using meta.class). Then we get the return value of said method using feval. Based on that, we instantiate an object or not (again using feval).
A.m:
classdef A
methods
function whoami(this)
fprintf('I am an A\n');
end
end
methods(Static)
function out = a
out = true;
fprintf('A.a returns %i\n', out);
end
end
end
B.m:
classdef B
methods
function whoami(this)
fprintf('I am a B\n');
end
end
methods(Static)
function out = a
out = false;
fprintf('B.a returns %i\n', out);
end
function out = b
out = true;
fprintf('B.b returns %i\n', out);
end
end
end
has_static_method.m, will be used to test if a class has a static function:
function res = has_static_method(class_name, method_name)
mc = meta.class.fromName(class_name);
ml = mc.MethodList;
ml = ml([mc.MethodList.Static]);
res = any(strcmp(method_name, {ml.Name}));
end
test.m:
classes = {'A', 'B'};
for i_c = 1:numel(classes)
klass_name = classes{i_c};
static_function_name = 'a';
if has_static_method(klass_name, static_function_name) && feval(sprintf('%s.%s', klass_name, static_function_name))
an_object = feval(klass_name);
an_object.whoami
end
static_function_name = 'b';
if has_static_method(klass_name, static_function_name) && feval(sprintf('%s.%s', klass_name, static_function_name))
an_object = feval(klass_name);
an_object.whoami
end
end