Search code examples
matlaboutputvariadic-functionsanonymous-functionfunction-handle

How to write an anonymous function with a variable number of output arguments?


Using deal we can write anonymous functions that have multiple output arguments, like for example

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

But if you want to provide a function with its gradient to the optimization function fminunc this does not work. The function fminunc calls the input function sometimes with one and sometimes with two output arguments. (EDIT: This is not true, you just have to specify whether you actually want to use the gradient or not, using e.g. optimset('SpecifyObjectiveGradient',true). Then within one call it always asks for the same number of arguments.)

We have to provide something like

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

which can be called with one or two output arguments.

So is there a way to do the same inline without using the function keyword?


Solution

  • The OP's solution is good in that it's concise and useful in many cases.

    However, it has one main shortcoming, in that it's less scalable than otherwise possible. This claim is made because all functions ({x^2,2*x,2}) are evaluated, regardless of whether they're needed as outputs or not - which results in "wasted" computation time and memory consumption when less than 3 outputs are requested.

    In the example of this question this is not an issue because the function and its derivatives are very easy to compute and the input x is a scalar, but under different circumstances, this can be a very real issue.

    I'm providing a modified version, which although uglier, avoids the aforementioned problem and is somewhat more general:

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

    Notes:

    1. The additional functions I use are cellfun, evalin and feval.
    2. The 'UniformOutput' argument was only added so that the output of cellfun is a cell (and can be "unpacked" to a comma-separated list; we could've wrapped it in num2cell instead).
    3. The evalin trick is required since in the myFun scope we don't know how many outputs were requested from unpacker.
    4. While eval in its various forms (here: evalin) is usually discouraged, in this case we know exactly who the caller is and that this is a safe operation.