6

I'm grouping a set of anonymous functions into a structure and some variables within that structure. Is there a way to refer 'self', i.e, own structure? What I'd like to accomplish is to have a function returns some values based on the member variables. For simplicity, say I have a struct a, where

a.value_1 = 3;
a.value_2 = 2;
a.sum = @()(self.value_1 + self.value_2)

Is there something like that possible in MATLAB?

Amro
  • 123,847
  • 25
  • 243
  • 454
radarhead
  • 668
  • 3
  • 13

3 Answers3

10

Before objected-oriented programming was introduced in MATLAB (including both classdef-style and the obsolete @-directory style classes), one could create lightweight objects using closures and nested functions (lacking inheritance of course). This concept also exists in other languages.

Here is an example:

function s = mystruct()
    s = struct('value_1',[], 'value_2',2, 'sum',@mysum);
    s.value_1 = 3;

    function out = mysum()
        out = s.value_1 + s.value_2;
    end
end

Which is used as:

>> s = mystruct()
s = 
    value_1: 3
    value_2: 2
        sum: @mystruct/mysum
>> s.value_1 = 10;     % NOTE: this wont do what you expect!
>> s.sum()
ans =
     5

Note that variables are immediately captured when creating a closure (functions have their own private copy if you will). So if you change one of the exposed fields from the returned structure, it will not be reflected in the enclosed state (think of them as read-only properties).

One solution is to provide accessor methods:

function obj = mystruct()
    % think of those as private properties
    value_1 = 3;
    value_2 = 2;

    % returned object (this or self)
    obj = struct();

    % public accessors for properties
    obj.value_1 = @accessValue1;
    function out = accessValue1(in)
        if nargin > 0, value_1 = in; end
        out = value_1;
    end
    obj.value_2 = @accessValue2;
    function out = accessValue2(in)
        if nargin > 0, value_2 = in; end
        out = value_2;
    end

    % member method
    obj.sum = @mysum;
    function out = mysum()
        out = value_1 + value_2;
    end
end

So now we could say:

>> s = mystruct()
s = 
    value_1: @mystruct/accessValue1
    value_2: @mystruct/accessValue1
        sum: @mystruct/mysum
>> x = s.value_1();   % get
>> s.value_1(10);     % set
>> s.sum()
ans =
    12

Which is starting to look like the current recommended approach to create classes:

classdef mystruct < handle
    properties
        value_1 = 3;
        value_2 = 2;
    end
    methods
        function out = sum(obj)
            out = obj.value_1 + obj.value_2;
        end
    end
end

Used in a similar manner:

>> s = mystruct()
s = 
  mystruct with properties:

    value_1: 3
    value_2: 2
>> s.value_1 = 10;
>> s.sum
ans =
    12

We could also define get/set access methods as before..

Amro
  • 123,847
  • 25
  • 243
  • 454
2

This seems to work but I think you should rather create a class than a struct to do this:

a.value_1 = 3;
a.value_2 = 2;

a.sum = @(x)(x.value_1 + x.value_2)

a.sum(a)
Dan
  • 45,079
  • 17
  • 88
  • 157
  • 1
    was going to add the same exact answer! – Lokesh A. R. Aug 12 '13 at 14:21
  • 1
    This only works for a very specific use case - it will [totally screw](http://stackoverflow.com/a/18189781/2278029) up in others if one doesn't understand what it's doing. – horchler Aug 12 '13 at 14:44
  • @horchler In what cases will it screw up? This does work and I did suggest to rather use a class so I don't really understand the downovte? – Dan Aug 12 '13 at 14:59
  • You're correct - your `sum` function is different from the OP's and the one I used and I see now that you're passing in `a` (`x`) as an argument. It's a form of what I proposed [here](http://stackoverflow.com/a/18111820/2278029). – horchler Aug 12 '13 at 15:07
0

With this change

a.value_1 = 3;
a.value_2 = 2;
a.sum = @()(a.value_1 + a.value_2)

Then a.sum() returns 5. But what happens when you change one of the values, later on, e.g., set a.value_1 = 5? Now a.sum() returns ... still 5. The parameters passed into the anonymous function are evaluated upon instantiation. If you want the second behavior to work properly you need to use a class. See my answer to this question for more. The only reason to use a function handle like you have is to avoid evaluating and storing a large output of a function until it's needed.

Community
  • 1
  • 1
horchler
  • 18,384
  • 4
  • 37
  • 73
  • Thanks for the response. A class is what I was looking for but didn't realize it had been available in MATLAB. – radarhead Aug 12 '13 at 14:47