classdef Dog < handle
properties
data;
end
methods
function self = Dog(varargin)
disp("Dog()")
end
function out = new(~)
out = Dog();
out('bark') = 1;
end
end
end
d=Dog(); why does d.new() print below?
Dog()
Dog()
Dog()
It also bypasses subsref if I overload it. Entering d in console gives
ans =
1×114 Dog array with properties:
data
and isn't reproduced with d('bark') = 1.
The behavior changes if I get rid of varargin, instead there's an error. Also why 1x114?
Here is what's happening when calling d.new() (after d = Dog()):
out = Dog(); is invoked. This constructor call triggers the first output of "Dog()"
out('bark') = 1; is invoked, which triggers the following:
The characters of 'bark' are interpreted as values [98 97 114 107 ]. Consequently, Matlab resizes out to a size of 114.
To construct a Dog object to fill positions [98 97 114 107 ] in out, Matlab calls the constructor again, this time with the input argument varargin equal to { 1 }. This triggers the second output of "Dog()".
To construct a Dog object to fill the other positions in out, Matlab calls the constructor again, this time with no input argument. This triggers the third output of "Dog()".
Furthermore, subsref is not bypassed, but simply not called, as you do not refer to the object in a reading manner.
And when you remove varargin, you get the error, because the constructor cannot be called anymore with one input argument, as explained in the second bullet point under 2. above.
As I understand it, within class methods, indexing expressions always use the built-in subsasgn and subsref, not the overloaded (customized) one you might write yourself. This is so that overloading these functions doesn't remove the ability of class methods to access object properties.
So, within your class method, out(i) accesses the ith object in the array out. This is not out.data(i), out is really an array of objects of class Dog. Dog() creates a 1x1 array, which you would normally think of as an object, but it really is an array. Everything in MATLAB is an array!
Now for out('bark') = 1. 'bark', as explained by user16372530's anwser, is a 4-element array, with numeric values [98 97 114 107]. Thus out, which starts off as a 1x1 array of your custom class, is resized to an array of 114 elements, and to 4 of those elements you assign 1. Here, MATLAB needs to create an empty element to fill out the 113 new elements, so calls Dog() once and copies the result to each of those elements. Then it needs to convert 1 to your class, so calls Dog(1) to do so, and copies to result to the 4 indexed elements. Actually, as indicated by
user16372530 in a comment below, first 1 is converted and then the empty object is created.
If you want your class method to use the overloaded indexing operator, you need to explicitly call subsasgn or subsref, which is ugly and hard to read. There really is no way, as far as I understand it, to use class objects inside the class methods as you actually intend your class to be used. The same code behaves differently inside a class method and outside of it.
MATLAB OOP has some really interesting features, but it also has a lot of weird and inconvenient things. I guess they did their best designing a custom class system within the previously existing MATLAB array syntax.
Related
I tried to change an attribute value of a class by invoking one of its member function:
p1 = tank();
p1.checkOri(p1);
And in the class definition, I have that static method:
classdef tank
properties
value
...
end
methods
...
methods (Static)
function obj = checkOri(obj)
if (CONDITION) %the thing I want it to do
obj.value = EXPRESSION;
...
Yet this checkOri method is not working. But if I write this method in the main file, or to say altering the value of p1-the instance of class tank-it works perfectly:
p1 = tank();
p1.checkOri(p1);
if (CONDITION) %the thing I want it to do
p1.value = EXPRESSION;
It works perfectly.
I wonder what caused this. From my experience with other programming languages, invoking method should have worked, is it because of some tricks with Matlab syntax or static method? How could I fix it so that this method would work?
So, as #Navan in the comment said, handle class could be a solution.
It appears Matlab has a similar parameter concept with Java and C++, arguments modified in a function/method only remains that modification inside the function/method.
For this class, I simply added < handle in the head of class definition and it worked:
classdef tank < handle
properties
...
But I am not sure if that is the only solution, there might be better ways to do this. So I'll leave that question open, you are more than welcomed to post your opinion:D
In MATLAB, the call
p1.checkOri();
is equivalent to
checkOri(p1);
In both cases, the class method checkOri is called for the class of object p1, passing p1 as first argument to the function by value.
Because p1 is passed by value, any changes made to it inside the function are not seen by the object in the calling workspace. Therefore, one typically does
p1 = checkOri(p1);
This way, the object that was passed by value and modified inside the function is passed back out and assigned to the variable that held the original object.
If the method is written as follows:
function obj = checkOri(obj)
%...
end
then MATLAB will optimize the above function call such that no copy of the object is actually made. Note that both in the function declaration and in the function call, the input and output variable is the same.
As already discovered by OP, the above does not hold for handle classes, classes that inherit from handle. These classes act as if they are always passed by reference, and any change made to them in any workspace will be reflected in all other copies in other workspaces.
Also assigning to a member variable does not follow the above, such that
p1.value = 0;
modifies object p1.
For more information on the difference between value classes and handle classes see this other question.
I am facing a strange behaviour in MATLAB when declaring an object variable as global. Where all global doubles I define seem to be recognizable by functions, it seems not possible to do the same with objects, or structs of objects that are defined from a class. This is what I have done.
Class definition:
classdef Class < handle
properties
property
end
methods
function obj = Constructor(value)
if nargin == 1
obj.property = value;
end
end
end
end
Construction of objects array:
% Define objects array as global variable to be accessible in functions
global objectsArray variableA
% define value of variable A, to be accessible in functions
variableA = 123;
% Values to be assigned to objects' property field
values = [1,2,3];
% initialise object array using constructor
if isempty(objectsArray)
objectsArray(3,1) = Constructor();
end
% define objects' property field
for k = 1:3
objectsArray(k) = Constructor( values(k) );
end
Now, I'd like a function to access both the objects array, and variable A, both defined above, and both declared as global variables. However, if I build a function as follows, only variable A is accessible, while the objects array is seen as an empty variable.
function varargout = doSomething (varargin)
global objectsArray variableA
aaa = variableA;
bbb = objcetsArray(1).property; <--- code crashes here
end
The code crashes at executing the instruction defining bbb. Debug mode reveals that objectsArray is empty, and that variableA is equal to its value, 123. Ending debug mode, exiting therefore the function's execution, the objectsArray is defined as I'd expect, as a structure of 3 objects.
My question is, why does this happen? Why does it seem possible to recall global variables, but not if they are defined as objects?
After some research, I realised the problem was not the object itself, but the concept of global variable and how MATLAB works with it.
The same issue materializes when trying to make a struct global.
An answer to my question is explained in this thread by the user Steven Lord:
The problem here is not caused by global, at least not directly. When
you define a variable to be global, it starts out as an empty
(0-by-0) double array. So this line:
global array_Structure
sets array_Structure to be a global array containing [].
Now when you try to assign a struct array (data_Struct) into an
element of the array_Structure double variable, since the variable
has to be all one type, MATLAB has to convert one of the variables
into the other's type. In this case, since you're trying to put the
struct into the double array, it tries to convert the struct into a
double array. For some pairs of the built-in data types, MATLAB knows
how to convert between the two data types implicitly (for instance,
double to single) -- but struct and double is not one of those pairs
of types, so MATLAB says that converting a struct into a double is
not possible. It doesn't know how to do it.
i have a class called sampleClass with a cell array member variable called list.
Now i am trying to add elements to the internal list. For this purpose, i created the method addToList. The method simply tries to add elements to the list.
classdef sampleClass
properties
list = {}
end
methods
function addToList(obj)
obj.list{end+1}='test';
obj.list
end
end
end
Now the problem is, that the elements are not stored in the list.
Inside the function addToList the following output signalizes that the element is stored in the list:
>> samp = sampleClass();
>> samp.addToList
ans =
'test'
But after leaving the function, the element is no longer accessible:
>> samp.list
ans =
{}
Has anybody an idea what's going wrong? I am using Matlab 7.11.0 (R2010b).
Thanks for your help.
Julien
That's because you forgot to inherit from the handle class:
classdef sampleClass < handle %// Change here!
properties
list = {}
end
methods
function addToList(obj)
obj.list{end+1}='test';
obj.list
end
end
end
If you want to retain the changes made when modifying an instance of your class, you must inherit from the handle class. This can be done by taking your class definition (first line of your code), and doing < handle. If you don't, then any changes you make will not be saved. To show that this works, this is what I get in MATLAB:
>> samp = sampleClass();
>> samp.addToList
ans =
'test'
>> samp.list
ans =
'test'
To be more verbose, here's what the MATLAB docs say about handle:
The handle class is the superclass for all classes that follow handle semantics. A handle is a reference to an object. If you copy an object's handle, MATLAB® copies only the handle and both the original and copy refer to the same object data. If a function modifies a handle object passed as an input argument, the modification affects the original input object.
As such, you need to inherit from the handle class so that you have a reference back to the object you created. Any changes you make to the instance of your class will be remembered if you inherit from handle. If you don't, no changes you make get registered.
i am an engineering student enrolled in computer programming trying to understand a practice assignment for an upcoming lab and was wondering if someone could help me with this step of my program, Step: using The init method for the class takes the first formal parameter self and a list of [x, y] pairs v and stores the list as a class instance variable
It sounds like you are using Python, but next time you post a question, make sure you specify that and tag your question as such. You are looking for something like the following code:
class MyClassName(object):
def __init__(self, pairs):
self.pairs = pairs
Let's look at this line by line:
class MyClassName(object):
The first line declares a class called MyClassName. It extends object, which is not super important to understand right now, but is basically saying that MyClassName is a particular type of object.
def __init__(self, pairs):
The second line creates a function called __init__ which will be called when you instantiate an object of type MyClassName. This line also declares what parameters it takes. It sounds like you already know that the first argument has to be self, and the second parameter, pairs, is the list of [x,y] pairs. In python, we don't need to specify what type these parameters are, so we need only to name them (Some languages would require us to specify that pairs is going to be a list of pairs).
self.pairs = pairs
Now all we have to do is set the instance variable. Inside a class, self refers to this particular instance of the object. In other words, every time we create a variable of type MyClassName, the self keyword will refer to that particular instance of the object, rather than to all instances of MyClassName. So in this case, self.pairs refers to the variable pairs in this particular instance of MyClassName. On the other hand, pairs simply refers to the argument passed into the function __init__.
So, to put all this together, we have defined a class called MyClassName, then defined the __init__ function, and in it, we set the instance variable self.pairs to be equal to the pairs variable passed into __init__.
Last, I'll give a quick example of how to instantiate MyClassName:
my_list = [(1,1),(2,4),(3,9),(4,16)]
my_instance = MyClassName(my_list)
Good luck!
[Edit] Also, I agree with the first comment on your question. You need to be more clear and verbose in exactly what you are trying to accomplish and not leave it up to guess work. In this case, I think I could tell what you were trying to do, but it may not always be clear.
I am having a go at matlab object orientated programming. I have a class with properties. The properties are computed if they are NaN otherwise the property is returned.
So I have this section:
properties
some_property = NaN;
end
and a property is computed like this:
function some_property = get_some_property(obj)
if(isnan(obj.some_property))
% do some expensive computation
obj.some_property = ...;
end
some_property = obj.some_property;
A get_some_property method can be used by another get_some_property method. The problem is that between method calls properties are set back to NaN. This causes the error:
Maximum recursion limit ...
Any ideas? Thanks.
Your problem is that your class is not derived from handle, so it's passed around as a value. This means that when you call get_some_property, Matlab faithfully duplicates obj, giving a copy to the function get_some_property and keeping that separate from the copy of the code that called get_some_property. So, when you change some_property in the function get_some_property, it changes the property on the value of obj in the function, but not the property of obj in the code that called it.
There are two solutions to your problem. The best solution (IMO) is to derive your class from handle. Classes derived from handle are passed as references, so when you change the property on obj, it will change the property everywhere.
However, if you're unwilling to do this, then you can return obj from get_some_property:
function [some_property,obj] = get_some_property(obj)
And then call get_some_property in this way:
[property,obj] = get_some_property(obj);
This updates the value of obj in the calling namespace. It's both awkward and clunky; I would derive your classes from handle unless there's a good reason to avoid it.