如何在MATLAB匿名函数中执行多个语句?

44

我想要做类似这样的事情:

>> foo = @() functionCall1() functionCall2()

所以当我说:

>> foo()

它将执行functionCall1(),然后执行functionCall2()。(我觉得我需要类似于C,operator的东西)
编辑: functionCall1functionCall2不一定是返回值的函数。

functionCall1和functionCall2是否需要接受输入值?如果不需要,我下面给出的解决方案应该可以工作。如果它们需要接受值,则我的解决方案仍然可以工作,但需要进行一些修改。 - gnovice
我更新了我的答案,并提供了一个传递输入参数的示例,以防您需要它。 - gnovice
5个回答

47

尝试仅通过命令行完成所有操作而不将函数保存在m文件中可能是一个复杂而混乱的努力,但这里是我想出的其中一种方法...

首先,创建你的匿名函数并将它们的句柄(handle)放在一个cell数组中:

fcn1 = @() ...;
fcn2 = @() ...;
fcn3 = @() ...;
fcnArray = {fcn1 fcn2 fcn3};

如果您已经定义了函数(例如在M文件中),则可以将函数句柄放入单元数组中,如下所示:

fcnArray = {@fcn1 @fcn2 @fcn3};

你可以创建一个新的匿名函数,使用内置函数cellfunfeval,调用数组中的每个函数:

foo = @() cellfun(@feval,fcnArray);

虽然看起来有点奇怪,但它能够运作。

编辑:如果fcnArray中的函数需要使用输入参数进行调用,则首先必须确保数组中的所有函数都需要相同数量的输入。在这种情况下,以下示例演示了如何为每个函数分别提供一个输入参数:

foo = @(x) cellfun(@feval,fcnArray,x);
inArgs = {1 'a' [1 2 3]};
foo(inArgs);  %# Passes 1 to fcn1, 'a' to fcn2, and [1 2 3] to fcn3


警告: cellfun 的文档说明不保证计算输出元素的顺序,不能依赖此顺序。这意味着不能保证fcn1fcn2fcn3之前被评估。如果顺序很重要,则不应使用上面的解决方案。


太棒了!我一直在尝试找出类似的东西。 - Jason S
1
有趣!当然,您可以直接声明fcnArray而不是作为单独的变量,因此类似于: myfun = @() cellfun(@feval,{@() xlim([1 5]), @() xlabel('seconds')}); 现在,调用myfun将在图形中设置xlim和xlabel。 - Tom Anderson

14
Matlab中的匿名函数语法(像其他一些语言一样)只允许单个表达式。此外,它具有不同的变量绑定语义(不在参数列表中的变量在函数创建时词法上绑定其值,而不是绑定引用)。这种简单性使Mathworks可以在脚本中使用它们时在幕后进行一些优化,并避免许多混乱的作用域和对象生命周期问题。
如果您在函数(而非脚本)中定义此匿名函数,则可以创建命名内部函数。内部函数具有正常的词法引用绑定并允许任意数量的语句。
function F = createfcn(a,...)
  F = @myfunc;
  function b = myfunc(...)
    a = a+1; 
    b = a; 
  end
end
有时候你可以使用gnovice的建议来达到目的。但是要小心使用eval...它非常低效(绕过了JIT),而且Matlab的优化器可能会在eval表达式内部使用外部范围中的变量和函数时混淆它们。此外,使用eval编写的代码也很难调试或扩展。

7
这里有一种方法可以保证执行顺序,并且(在结尾提到的修改下)允许向不同的函数传递不同的参数。
call1 = @(a,b) a();
call12 = @(a,b) call1(b,call1(a,b));

关键在于call1可以调用它的第一个参数并忽略第二个参数。而call12则会先执行第一个参数,再执行第二个参数,返回第二个参数的值。这是因为在函数求值前,必须先对其参数进行求值。创建你的例子时,你需要编写:

foo = @() call12(functionCall1, functionCall2);

测试代码

这是我使用的测试代码:

>> print1=@()fprintf('1\n');
>> print2=@()fprintf('2\n');
>> call12(print1,print2)
1
2

调用更多函数

要调用3个函数,您可以编写:

call1(print3, call1(print2, call1(print1,print2)));

4个功能:

call1(print4, call1(print3, call1(print2, call1(print1,print2))));

如果需要更多功能,继续嵌套模式。

传递参数

如果您需要传递参数,可以编写一个接受参数的call1版本,然后对call12进行显而易见的修改。

call1arg1 = @(a,arg_a,b) a(arg_a);
call12arg1 = @(a, arg_a, b, arg_b) call1arg1(b, arg_b, call1arg1(a, arg_a, b))

您还可以创建多个参数版本的call1,并根据需要进行混合和匹配。


5

使用curly函数可以创建逗号分隔列表。

curly = @(x, varargin) x{varargin{:}};
f=@(x)curly({exp(x),log(x)})
[a,b]=f(2)

1
如果 functionCall1()functionCall2() 返回值可以拼接,那么您可以这样做: >> foo = @() [functionCall1(), functionCall2()] 或者 >> foo = @() [functionCall1(); functionCall2()] 副作用是 foo() 将返回 functionCall1()functionCall2() 的拼接。
我不知道是否保证了 functionCall1()functionCall2() 的执行顺序。

是的,不幸的是我正在处理不返回值的函数。 - Daniel LeCheminant

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接