Search code examples
matlabfunctionhandle

Defining a generic function for iterated function composition


Let us define any function handle foo:

foo = @(x) x*2

I am trying to write a general function defFun that generates the n-th functional power of the function foo, i.e. n iterative calls of foo, in a way it can be stored in another handle function boo, like this:

boo = defFun(foo,n)

For example,

foo = @(x) x^2;  
boo = defFun(foo,3);

boo(3) will give 6561 [== foo(foo(foo(3)))] and boo(2) will give 256 [== foo(foo(foo(2)))].

I tried this code to write defFun but these handles are tricky to deal with. Any ideas?

function boo = defFun(foo,n)
   h = foo;
   for i=2:n
      h = h(h);
   end
   boo = h
end

Solution

  • My code pretty much resembles your original one. I took the liberty to rename a few of the variables.

    Direct approach:

    For single input and single output argument you could use the direct approach resembling your code:

    function ftoN = fIterate(f, N)
        ftoN = f;
        for i = 2:N
            ftoN = @(x) f(ftoN(x)); 
        end
    end
    

    Indirect approach: (possibly faster)

    This one will be a lot faster and it will also work for multiple (but same number of) inputs and outputs.

    function ftoN = fIterate(f, N)
        ftoN = @(varargin)   fIterateLocal(f, N, varargin{:});
        function varargout = fIterateLocal(f, N, varargin)
            varargout = varargin;
            for i = 1:N
                [varargout{1:nargin-2}] = f(varargout{:});
            end
        end
    end
    

    Example:

    Both approaches should work with this:

    square = @(x) x^2;
    square(2)
    >> ans = 4
    
    squaresquaresquare = fIterate(square, 3)
    squaresquaresquare(3)
    >> ans = 6561
    

    Aftermath:

    The direct approach will be rather slow and also limited by the Maximum recursion limit of MATLAB.

    timeit(@()feval(fIterate(@(X)X+1,400),0))
    ans = 1.2160
    

    The indirect approach will give you a lot more speed and flexibility:

    timeit(@()feval(fIterate(@(X)X+1,400),0))
    ans = 0.0072