SolessChong
SolessChong

Reputation: 3407

How to create an 'closure function' in Matlab as in python and js?

Background

In Python and JS we have closures, which returns a function with some of the variables pre-defined. e.g.

def make_printer(msg):
    def printer():
        print msg
    return printer

Do we have similar stuff in Matlab?

Here I have a callback function

function show(object, eventdata)

that is to be set to the callback of my GUI

func = @show;
set(gcf, 'WindowButtonMotionFcn', func);

However, I want to add some additional parameters to this show function.

Currently I'm using global variables to do that. But I think it would be elegant if we have a 'closures function'.

About anonymous function

Yes we do have anonymous function in Matlab. However it seems to me that it is too simple to support 60-line procedures.

Upvotes: 2

Views: 1150

Answers (3)

Carel
Carel

Reputation: 3397

There was once a document floating about the internet by Sergey Simakov. It was very terse , not especially descriptive but covered the bases. This was in my experience the most authoritive text on matlab GUI's. I suspect it still is ...

You're solving two/three problems :

Closure Problem

Nested functions resolve this problem.

function iterator = count(initial)
 % Initialize
 if ~exist('initial','var')
  counter = 0
 else
  counter = initial
 end
 function varargout = next() % [1] 
  % Increment
  counter = counter + 1
  varargout = {counter}      % [1] 
 end
 iterator = @next
end

Note(s) :

  1. One may not simply return counter ! It must be wrapped in varargout or assigned to some other output variable.

Usage

counter = count(4) % Instantiate
number = counter() % Assignment
number = 
  5

Closed Scope + State problem

No ugly braces, worrying about scope, cell arrays of strings. If you need access to something it's under SELF no more FINDOBJ, USERDATA, SET/GETAPPDATA, GLOBAL nonsense.

classdef Figure < handle

 properties 
  parent@double
  button@double
  label@double
  counter@function_handle
 end

 methods 
  function self = Figure(initial)
   self.counter = count(initial) % [1]
   self.parent  = figure('Position',[200,200,300,100])
   self.button  = uicontrol('String','Push',          'Callback', @self.up, 'Style', 'pushbutton', 'Units', 'normalized', 'Position', [0.05, 0.05, 0.9, 0.4])
   self.label   = uicontrol('String', self.counter(),                       'Style', 'text',       'Units', 'normalized', 'Position', [0.05, 0.55, 0.9, 0.4])
  end

  function up(self,comp,data)
   set(self.label,'String',self.counter())
  end
 end
end

Note(s):

  1. One uses the function listed in the Closure problem above

Usage :

f = Figure(4)                  % Instantiate
number = get(f.label,'String') % Assign
number = 
  5

You may prefer :

f.label.get('String')          % Fails (f.label is double not handle, go figure)
h = handle(f.label)            % Convert Double to handle
number = h.get('String')       % Works 
number = 
  5

Upvotes: 2

David
David

Reputation: 8459

Since it looks like my comment was helpful, I'll explain what I mean about the struct thing. I think the way you are calling the callback functions is slightly different, so this may not work so well).

If you have a bunch of different callback functions, and a bunch of variables you want to pass to them, it is annoying and difficult to have a big list of input arguments for each function. Instead, I do something like this:

First create a bunch of UI components , but don't specify their callback functions, e.g

radio1_handle=uicontrol(panel1_handle,'Style','radiobutton',...
    'Min',0,'Max',1,'Value',0,'Units','normalized','Position',[.8 .8 .2 .25]);

once you have made all the components, create a struct of variables you will be using

vars=struct('varName1',var1,'varName2',var2);

then update the UI components to include the callback functions

set(radio1_handle,'Callback',{@radio1_callback,vars});

now create the actual functions

function radio1_callback(object,eventData,vars)

So it's nothing fancy, just a potentially neater way than using multiple arguments.

Upvotes: 1

SolessChong
SolessChong

Reputation: 3407

Inspired by this question and @David's comment, I came up with the solution. (Maybe this is @David's answer, but not explicitly explained. So let me extend his comment into an answer.)

There is actually a way to add extra parameters to callbacks.

Just add the parameters to the end of the parameter list

function show(object, eventdata, extra)

and set the callback like this:

func = @show;

set(gcf, 'WindowButtonMotionFcn', func);

Reference

Mathworks

Passing Additional Input Arguments

You can define the callback function to accept additional input arguments by adding them to the function definition:

function myCallback(src,eventdata,arg1,arg2)

When using additional arguments for the callback function, you must set the value of the property to a cell array (i.e., enclose the function handle and arguments in curly braces):

figure('WindowButtonDownFcn',{@myCallback,arg1,arg2})

Upvotes: 1

Related Questions