6
votes

How do I do successive function application in MATLAB with anonymous functions? Something like the following:

g = @(x) @(y) x+y;
g(1)(2)

However MATLAB gives an error at line 2: ()-indexing must appear last in an index expression.

But the following works:

g = @(x) @(y) x+y;
f = g(1);
f(2)

The script above outputs ans=3.

I am not very familiar with MATLAB but I think the ability to manipulate on the function level makes programming much easier. For example, when I need to compute projections of functions onto some subspace of L^2, the projection operator and normalization etc. all output functions that take additional arguments to evaluate to a numerical answer.

1
I don't think what you are describing is actually currying. Do want to know how to evaluate g(1)(2) or do you want to know how to do currying in MATLAB? (Which is generating a function X -> (Y -> Z) from a function (X x Y) -> Z) - knedlsepp
@knedlsepp I would like to know the former, i.e. how to evaluate g(1)(2). Thanks! - David Yao

1 Answers

6
votes

MATLAB doesn't support single expression calls like y = g(1)(2) to function handles returned by functions. You can however work your way around this limitation by using temporary variables:

g1 = g(1); 
y = g1(2);

As an alternative you could build your own function to wrap around this functionality.

A recursive approach could be:

function f = fevalIterated(f, varargin)
if ~isempty(varargin)
    f = fevalIterated(f(varargin{1}), varargin{2:end});
end

Instead of y = g(1)(2) you would call y = fevalIterated(g, 1, 2).

The iterative approach to do this might be faster:

function f = fevalIterated(f, varargin)
for i = 1:numel(varargin)
    f = f(varargin{i});
end

As you were asking about the concept of currying in MATLAB, which is really similar to this:

Un-currying

Un-currying would mean to convert a function @(x) @(y) @(z) x+y+z to a function @(x,y,z) x+y+z. This is a very similar concept and you can thus reuse the functionality of fevalIterated to build a function uncurry that can be used like this:

g = uncurry(@(x) @(y) @(z) x+y+z);
y = g(1,2,3)

The function uncurry would be defined as:

function uncurried = uncurry(f)
uncurried = @(varargin) fevalIterated(f, varargin{:});

Currying

To curry a function @(x,y,z) x+y+z would mean to convert it to @(x) @(y) @(z) x+y+z.

Here is a recursive implementation of curry:

function f = curry(f,N)
if N>1
    f = @(first) curry(@(varargin)f(first,varargin{:}), N-1);
end

A (faster) iterative implementation would look like this:

function f = curry(f,N)
for i = 1:N-1
    f = @(varargin) @(last) f(varargin{:}, last);
end

You can call both via f = curry(@(x,y,z) x+y+z, 3).

Caveat

Although you can do all this in MATLAB, you might suffer from a noticable performance drop if you overdo the whole function handle calling thingy.

f = @(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15) ...
     (x1+x2+x3+x4+x5+x6+x7+x8+x9+x10+x11+x12+x13+x14+x15);
%%// Currying vs Comma separated list expansion
%// Comma separated list expansion
tic;
[C{1:15}] = deal(12345);
f(C{:});
toc;
%// Elapsed time is 0.000146 seconds.

%// Currying
g = curry(f,15);
tic;
for i = 1:15
    g = g(12345);
end
toc;
%// Elapsed time is 0.015679 seconds.