kjo
kjo

Reputation: 35311

Does MATLAB support "callable" (i.e. function-like) classes?

Is it possible to define a MATLAB class such that the objects from this class can be called like any other function?

IOW, I'm asking whether one can write in MATLAB the equivalent of something like the following Python class:

# define the class FxnClass
class FxnClass(object):
    def __init__(self, template):
        self.template = template

    def __call__(self, x, y, z):
        print self.template % locals()

# create an instance of FxnClass
f = FxnClass('x is %(x)r; y is %(y)r; z is %(z)r')

# call the instance of FxnClass
f(3, 'two', False)

...
[OUTPUT]
x is 3; y is 'two'; z is False

Thanks!

Upvotes: 5

Views: 1061

Answers (4)

Amro
Amro

Reputation: 124563

One way is to override the feval function for your class:

classdef FxnClass < handle
    properties
        template
    end
    methods
        function obj = FxnClass(t)
            obj.template = t;
        end

        function out = feval(obj, varargin)
            out = sprintf(obj.template, varargin{:});
        end
    end
end

This would be used as:

>> f = FxnClass('x = %f, y = %s, z = %d');
>> feval(f, 3,'two',false)
ans =
x = 3.000000, y = two, z = 0

Now if you want to provide additional syntactic sugar, you could redefine the subsref function for your class as @Salain suggested. Add the following to the previous class definition:

classdef FxnClass < handle
    ...

    methods
        function out = subsref(obj, S)
            switch S(1).type
                case '.'
                    % call builtin subsref, so we dont break the dot notation
                    out = builtin('subsref', obj, S);
                case '()'
                    out = feval(obj, S.subs{:});
                case '{}'
                    error('Not a supported subscripted reference');
            end
        end
    end
end

Now you could simply write:

>> f = FxnClass('x = %f, y = %s, z = %d');
>> f(3,'two',false)
ans =
x = 3.000000, y = two, z = 0

Personally I don't particularly like overriding the subsref or subsasgn functions. They are used for too many cases, and its sometimes hard to get them write. For example all the following will eventually call the subsref method with different input:

f(..)
f.template
f.template(..)
f(..).template
f(..).template(..)

There is also the case of the end keyword which could appear in indexing, so you might have to also override it as well in some cases. Not to mention that objects can also be concatenated into arrays, which makes things even more complicated:

>> ff = [f,f];
>> ff(1)        % not what you expect!

That said, I think @Frank's suggestion to use nested functions with closures is more elegant in this case:

function f = FxnClass(t)
    f = @call;
    function out = call(varargin)
        out = sprintf(t, varargin{:});
    end
end

which is called as before:

>> f = FxnClass('x = %f, y = %s, z = %d');
>> f(3, 'two', false)

Upvotes: 4

Superbest
Superbest

Reputation: 26602

If you mean that you want a class to hold a method which you use like a normal function (eg. defined in an m-file), then yes, Matlab does support static methods.

A static method runs independently of any instances of that class, in fact, you don't even need to instantiate a class to use its static methods.

Matlab does not support static fields, however, so you would have to instantiate such a class first, and then set its fields before using the functions (which presumably make use of these fields, since you are asking this question).

Given the limitation with static members, you might be better off with closures, as described by Frank.

Upvotes: 1

Salain
Salain

Reputation: 752

I will be interested to see if this is possible without simply creating a java method in Matlab. I know you can do the following

classdef ExampleObject
    properties
        test;
    end
    methods
        function exampleObject = ExampleObject(inputTest)
            exampleObject.test=inputTest;
        end
        function f(exampleObject,funcInput)
            disp(funcInput+exampleObject.test);
        end
    end
end

>> e=ExampleObject(5);
>> f(e,10)
    15

But as far as my knowledge goes, if you tried to override the call function you'd run into a conflict with Matlab's parenthetical subscript reference subsref. You can find a reference here showing how to overwrite that, and you might be able to get it to do what you want...but it doesn't seem like good form to do so. Not sure how Matlab would handle a call to an object (as opposed to a function) without it getting confused with this.

Upvotes: 4

Frank
Frank

Reputation: 2788

I do not know, whether MATLAB directly supports what you want, but MATLAB does support first-class functions; closures might therefore provide a useable substitute, for instance:

function f = count_call(msg)
    calls = 0;

    function current_count()
        disp(strcat(msg, num2str(calls)));
        calls = calls + 1;
    end

    f = @current_count;
end

In this case, current_count closes over calls (and msg). That way you can express functions that depend on some internal state. You would use it this way:

g = count_call('number of calls: ') % returns a new function ("__init__")
g()                                 % "__call__"

Upvotes: 5

Related Questions