代码之家  ›  专栏  ›  技术社区  ›  flawr

如何编写输出参数数目可变的匿名函数?

  •  6
  • flawr  · 技术社区  · 8 年前

    使用 deal 我们可以编写具有多个输出参数的匿名函数,例如

    minmax = @(x)deal(min(x),max(x));
    [u,v] = minmax([1,2,3,4]); % outputs u = 1, v = 4
    

    但是如果你想提供一个函数,它的梯度是最优化函数 fminunc 这不起作用。 功能 fminunc公司 有时使用一个输出参数调用输入函数,有时使用两个输出参数调用输入函数。 (编辑:这不是真的,您只需指定是否确实要使用渐变,例如。 optimset('SpecifyObjectiveGradient',true) . 然后在一次调用中,它总是要求相同数量的参数。)

    我们必须提供

    function [f,g] = myFun(x)
     f = x^2; % function
     g = 2*x; % gradient
    

    可以用一个 两个输出参数。

    那么,有没有一种方法可以在不使用 function 关键字?

    2 回复  |  直到 8 年前
        1
  •  9
  •   flawr    8 年前

    是的,这涉及到 this question 关于递归匿名函数。首先我们定义一个助手函数

    helper = @(c,n)deal(c{1:n});
    

    它接受一个单元格数组 c 可能的输出以及整数 n 这说明我们需要多少产出。要编写实际的函数,只需定义单元数组并传递 nargout (预期输出参数的数目)到 helper :

    myFun = @(x)helper({x^2,2*x,2},nargout);
    

    现在,在调用 fminunc :

    x = fminunc(myFun,1);
    
        2
  •  5
  •   Dev-iL    8 年前

    这个 OP's solution 很好,因为它在许多情况下都简洁而有用。

    然而,它有一个主要缺点,即它的可扩展性不如其他方式。之所以提出此声明,是因为所有功能( {x^2,2*x,2} )无论是否需要作为输出,都会对其进行评估,这会在请求的输出少于3个时导致“浪费”计算时间和内存消耗。

    在这个问题的例子中,这不是一个问题,因为函数及其导数很容易计算,输入 x 是一个 标量 ,但在不同的情况下,这可能是一个非常现实的问题。

    我提供了一个修改后的版本,虽然更加丑陋,但它避免了上述问题,并且更为通用:

    funcs_to_apply = {@(x)x.^2, @(x)2*x, @(x)2};
    unpacker = @(x)deal(x{:});
    myFun = @(x)unpacker(cellfun(@(c)feval(c,x),...
                                 funcs_to_apply(1:evalin('caller','nargout')),...
                                 'UniformOutput',false)...
                        );
    

    笔记:

    1. 我使用的其他功能包括 cellfun , evalin feval .
    2. 这个 'UniformOutput' 只添加了参数,以便 cellfun公司 是一个单元格(可以“解包”到 comma-separated list ; 我们可以把它包起来 num2cell 而是)。
    3. 这个 埃瓦林 需要技巧,因为在 myFun 范围我们不知道请求了多少输出 unpacker .
    4. 虽然 eval 以各种形式(此处: 埃瓦林 )通常不鼓励这样做,在这种情况下,我们确切地知道呼叫者是谁,这是一种安全的操作。
    推荐文章