【问题标题】:How to create an 'closure function' in Matlab as in python and js?如何像在 python 和 js 中一样在 Matlab 中创建“闭包函数”?
【发布时间】:2014-05-02 17:27:02
【问题描述】:

背景

在 Python 和 JS 中,我们有闭包,它返回一个带有一些预定义变量的函数。例如

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

我们在 Matlab 中有类似的东西吗?

这里我有一个回调函数

function show(object, eventdata)

要设置为我的 GUI 的回调

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

但是,我想为这个show 函数添加一些额外的参数。

目前我正在使用全局变量来做到这一点。但我认为如果我们有一个“闭包函数”会很优雅。

关于匿名函数

是的,我们在 Matlab 中确实有匿名函数。但是在我看来支持60行程序太简单了。

【问题讨论】:

  • 我不确定我是否完全理解您的问题,但如果这有助于在更有知识的人出现之前,我会提出建议。您可以将附加参数添加到回调函数function callbackFunction(object,eventdata,variable)。您可以添加多个我没有尝试过的额外参数,我使用我的 GUI 使用的所有变量创建一个结构,并将其用作第三个输入参数。希望这是相关的!

标签: matlab function closures


【解决方案1】:

曾经有一个由谢尔盖·西马科夫(Sergey Simakov)在互联网上流传的文件。它非常简洁,不是特别具有描述性,但涵盖了基础。根据我的经验,这是 matlab GUI 上最权威的文本。我怀疑它仍然是......

你正在解决两个/三个问题:

闭包问题

嵌套函数解决了这个问题。

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

注意事项:

  1. 不能简单地返回计数器!它必须包含在 varargout 中或分配给其他一些输出变量。

用法

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

封闭范围+状态问题

没有丑陋的大括号,担心范围,字符串的单元格数组。如果您需要访问 SELF 下的某些内容,则不再是 FINDOBJ、USERDATA、SET/GETAPPDATA、GLOBAL 废话。

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

注意事项:

  1. 使用上面闭包问题中列出的函数

用法:

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

您可能更喜欢:

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

【讨论】:

    【解决方案2】:

    由于我的评论看起来很有帮助,我将解释我对 struct 事物的意思。我认为您调用回调函数的方式略有不同,因此可能效果不佳)。

    如果你有一堆不同的回调函数,以及一堆你想传递给它们的变量,那么为每个函数都有一个大的输入参数列表是很烦人的并且很难。相反,我会这样做:

    先创建一堆UI组件,但不指定它们的回调函数,例如

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

    制作完所有组件后,创建一个将要使用的变量结构

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

    然后更新 UI 组件以包含回调函数

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

    现在创建实际函数

    function radio1_callback(object,eventData,vars)
    

    所以这没什么花哨的,只是一种比使用多个参数更简洁的方法。

    【讨论】:

      【解决方案3】:

      受到this question 和@David 评论的启发,我想出了解决方案。 (也许这是@David 的答案,但没有明确解释。所以让我将他的评论扩展到答案。)

      实际上有一种方法可以为回调添加额外的参数。

      只需将参数添加到参数列表的末尾

      function show(object, eventdata, extra)
      

      并像这样设置回调:

      func = @show;
      

      set(gcf, 'WindowButtonMotionFcn', func);

      参考

      Mathworks

      传递额外的输入参数

      你可以定义回调函数来接受额外的输入 通过将参数添加到函数定义中:

      function myCallback(src,eventdata,arg1,arg2)
      

      当使用额外的 回调函数的参数,您必须设置 属性到元胞数组(即,包含函数句柄和 花括号中的参数):

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

      【讨论】:

      • 酷。我添加了一个答案来解释我的意思,但我认为你在这里有完整的解决方案。
      • 几乎一样。实际上回调可以得到几个额外的参数,所以不用打扰结构。我说得对吗?
      • 是的。使用结构的唯一原因是,如果您更改代码,则不必更新一大堆函数中的一大堆变量。我在大约 10 个回调函数中的每一个中使用了大约 15 个变量,并将它们放在一个结构中只是更容易管理。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-05-27
      • 1970-01-01
      • 2012-02-20
      • 1970-01-01
      • 1970-01-01
      • 2017-12-20
      • 2015-03-07
      相关资源
      最近更新 更多