如何编写一个具有可变输出参数数量的匿名函数?

6

通过使用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 关键字的情况下内联执行相同的操作呢?


5
附注:与您的 minmax 类似的功能最近(R2017a)被添加到 MATLAB 中,名称为 bounds - Dev-iL
2个回答

9

有的,它涉及到一种在这个问题中使用的技术——递归匿名函数。首先,我们定义一个辅助函数。

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);

5

OP的解决方案很简洁,而且在许多情况下非常有用。

然而,它有一个主要缺点,即它不如其他可能性可扩展。这是因为所有函数({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. 我使用的附加函数包括cellfunevalinfeval
  2. 'UniformOutput'参数仅用于使cellfun的输出为单元格(并且可以“解压”为逗号分隔列表;我们也可以将其包装在num2cell中)。
  3. 由于在myFun作用域中我们不知道从unpacker请求了多少个输出,因此需要使用evalin技巧。
  4. 虽然各种形式的eval(这里是evalin)通常不被鼓励,但在这种情况下,我们确切地知道调用者是谁,并且这是一个安全的操作。

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