Loren on the Art of MATLAB

Introduction to Functional Programming with Anonymous Functions, Part 1 24

Posted by Loren Shure,

Tucker McClure is an Application Engineer with The MathWorks. He spends his time helping our customers accelerate their work with the right tools and problem-solving techniques. Today, he'll be discussing how "functional programming" can help create brief and powerful MATLAB code.

Contents

The Goal

I use a lot of anonymous functions. They're nice and compact and almost invisible in their simplicity. Plus, if I can write an anonymous function to do something, I don't need to save the function in a file, and that can save me from file clutter on larger projects and from having to send someone a dozen files instead of sending one clean script. However, it seems at first glance like anonymous functions must necessarily be simple. No if... else, while, for, or any other keywords can be used. So how could we possibly write sophisticated programs in anonymous functions? We'll see, and it will involve some ideas from functional programming.

The goal of this introduction is to demonstrate how a few of these techniques can change the way we work in MATLAB, allowing greater brevity and simultaneously increasing readability. There are three parts. In this first part, we'll present creating functions of functions and treating functions as variables (in MATLAB, that means function handles), and from there, we'll move on to implementing conditional statements (like if... else) in anonymous functions. In the next part, we'll add recursion and executing multiple statements inside an anonymous function. In the final part, we'll develop a loop function. But first, if "function handle" or "anonymous function" is new to you, go check out Loren’s great introductions to those ideas in her previous posts!

Minimum and Maximum Example

Let's say we want to write a function to find the minimum and maximum of a set of numbers and store the results in an array. Here's a first pass:

min_and_max = @(x) [min(x), max(x)];
min_and_max([3 4 1 6 2])
ans =
     1     6

Our min_and_max function takes in an array that we'll call x, finds the minimum and maximum, and stores the two results in an output array. Clear? Good. But now let's make it more difficult. The min and max functions both return two outputs if desired (both the minimum or maximum and the index at which they occur in the input array). Our simple min_and_max function can't get those secondary outputs! How can we access them? Consider this odd-looking line.

[extrema, indices] = cellfun(@(f) f([3 4 1 6 2]), {@min, @max})
extrema =
     1     6
indices =
     3     4

Well, that clearly worked. The minimum, 1, occurs at index 3. The maximum, 6, occurs at index 4, but what is this line actually doing? First, recall how cellfun behaves. The first argument is a function handle. The second argument is a cell array of whatever. Each element of the cell array is given as an argument to the provided function handle. Most of the time, that cell array is full of data, and each piece of data is passed to the function. However, we could just as easily put function handles in the cell array. Then the first function (@(f) f(...)) acts on all the other functions. So, first @min is passed in for f and the outputs from min([3 4 1 6 2]) are stored. Then, @max is passed in for f, and its outputs are stored.

Ok, now that we're working with functions of functions, let's remove that hard-coded [3 4 1 6 2] and write a new min_and_max function by simply adding a @(x) out front and changing [3 4 1 6 2] to x.

min_and_max = @(x) cellfun(@(f) f(x), {@min, @max});

We can now use min_and_max for just the extrema, like before, but we can also get the indices too.

y = randi(10, 1, 10)
just_values        = min_and_max(y)
[~, just_indices]  = min_and_max(y)
[extrema, indices] = min_and_max(y)
y =
     2    10    10     5     9     2     5    10     8    10
just_values =
     2    10
just_indices =
     1     2
extrema =
     2    10
indices =
     1     2

That might have looked a little funny, but it's pretty easy to think about, right? Now let's make it look a little nicer too.

Map

Above, we're mapping each function to our input x. More generally, we might write a "map" function to map a series of functions to the input values. We'll make val a cell array so we can also send multiple inputs to multiple functions all at once. This is like what we had before, but rearranged a bit.

map = @(val, fcns) cellfun(@(f) f(val{:}), fcns);

Look how simple this makes min_and_max (below), while still accessing both outputs. Not only is it shorter to write than any other versions so far, it's easier to read, with hardly anything but a single occurrence of each variable or function name. "Map x to the min and max functions". No problem.

x = [3 4 1 6 2];
[extrema, indices] = map({x}, {@min, @max})
extrema =
     1     6
indices =
     3     4

Let's try multiple inputs:

map({1, 2}, {@plus, @minus, @times})
ans =
     3    -1     2

What if outputs are different sizes? We'll write mapc (as in MAP with Cell array outputs) to handle this; all it needs is an extra argument to cellfun to say that our output isn't uniform in size.

mapc = @(val, fcns) cellfun(@(f) f(val{:}), fcns, 'UniformOutput', false);

Send pi to multiple functions that return differently-sized arrays. The first output is a scalar, the second is a scalar, and the third is a string.

mapc({pi}, {@(x) 2 * x, ...                     % Multiply by 2
            @cos, ...                           % Find cosine
            @(x) sprintf('x is %.5f...', x)})   % Return a string
ans = 
    [6.2832]    [-1]    'x is 3.14159...'

That takes care of map, which we can now use anywhere to send a set of inputs to numerous functions and collect their multiple outputs with brief and easy-to-read code.

By the way, writing these functions that operate on other functions is part of the "functional programming" style, and we're just scratching the surface. Let's go a little deeper and see how we can write a function to choose which function to apply from a list of functions.

Inline Conditionals

Sometimes an anonymous function might need a condition, like if...else. However, normal MATLAB syntax doesn't allow program flow statements like these in anonymous functions. Hope it not lost. We can implement an "inline if" in a single line:

iif = @(varargin) varargin{2 * find([varargin{1:2:end}], 1, 'first')}();

Alright, that looks decidedly strange, so before we discuss how it works, take a look at how easy it is to use:

   [out1, out2, ...] = iif( if this,      then run this, ...
                            else if this, then run this, ...
                            ...
                            else,         then run this );

All the "if this" conditions should evaluate to true or false. The "then run this" action next to the first true condition is executed. None of the other actions are executed! We could use this to make, for example, a safe normalization function to do the following:

  • If not all values of x are finite, throw an error.
  • Else if all values of x are equal to 0, return zeros.
  • Else, return x/norm(x).

This is implemented below. Note the @() out in front of the actions. This means, "don't do this action, but refer to this action". That is, we're passing pieces of code to the iif function as arguments. In this way, we aren't actually doing all three things; we'll only call the action for the single case we need.

normalize = @(x) iif( ~all(isfinite(x)), @() error('Must be finite!'), ...
                      all(x == 0),       @() zeros(size(x)), ...
                      true,              @() x/norm(x) );

Test the nominal condition.

normalize([1 1 0])
ans =
      0.70711      0.70711            0

Test the error condition with non-finite inputs.

try normalize([0 inf 2]), catch err, disp(err.message); end
Must be finite!

Test the all-zeros condition.

normalize([0 0 0])
ans =
     0     0     0

Easy to use, right? We've implemented if... else behavior without needing an actual if or else anywhere! So now it's time to see how this thing works.

First, the iif function takes any number of arguments, thanks to varargin. These arguments will be condition 1 (true or false), action 1 (a function), condition 2, action 2, etc. First, the iif function selects all of the conditions (that's the odd numbered items in varargin) via [varargin{1:2:end}]. For our safe norm, this returns:

   [~all(isfinite(x)), all(x == 0), true]

Next, it finds the index of the first true value in those conditions with find(..., 1, 'first'). E.g., if ~all(isfinite(x)) was false, but all(x == 0) was true, the index would be 2.

The actions to perform are the even-numbered items of varargin, so we just multiply that index by 2 to get the index of the action to perform. Finally, we execute the action by appending () on the end, as in

   varargin{...}()

Did you catch what was happening there? We're passing little pieces of code as inputs to the iif function. Functions as arguments. See why this is called "functional" programming? I'll admit it looks weird at first, but once you've seen it, the pattern is hard to forget.

Ok, that's it for today. Here are the functions we developed today.

iif  = @(varargin) varargin{2*find([varargin{1:2:end}], 1, 'first')}();
map  = @(val, fcns) cellfun(@(f) f(val{:}), fcns);
mapc = @(val, fcns) cellfun(@(f) f(val{:}), fcns, 'UniformOutput', false);

These can also be found here, implemented as regular MATLAB functions that can be kept on the path.

To Be Continued

In the next installment, we'll build on these to enable recursion and to make anonymous functions that execute multiple statements. In the mean time, there are multiple ways to accomplish an inline if. For instance, I've seen a very brief ternary operator in our forums in the past. I'm curious what other implementations people might suggest for this type of behavior, so please post suggestions here.


Get the MATLAB code

Published with MATLAB® R2012b

24 CommentsOldest to Newest

I’ve had problems in the past with anonymous functions using huge amounts of memory and am thus very sceptical about them. My understanding is that this was caused by the fact that anonymous functions capture and store the entire workspace in which they’re created. Can you comment on this (Loren, Tucker, or anyone else)?

Tucker, that was fascinating. There is an ironic side to this article, though – the map and iif functions are so darned useful that I will want to use them often. To avoid repeating the code, I’ll have to make function files!

Hi Chris,

I hear you. I’d offer a few things.

1. Because of MATLAB’s “lazy” copy, the data inside an anonymous function doesn’t actually have to be copied anywhere in some cases. For instance, the anonymous function below does *not* contain a copy of x until you later modify x:

>> x = randn(5000); % 5000*5000*8B = 191 MB
>> f = @() eig(x); % This does NOT require storing x twice!
… % Later on….
>> x = randn(1000); % Ok, now f “stores” our original x.

2. The *entire* workspace isn’t captured in the anonymous function, but the part that is captured is sometimes larger than is necessary. That it captures the data is actually really helpful, because it means anonymous functions have their own scope, but you’re also correct that it does take some extra memory.

3. My suggestion: if anonymous functions are a helpful way for you to think about solving your problem, give them a shot! If you find that you’re memory constrained, first make sure your algorithm is efficient (algorithm before implementation!), then consider turning your anonymous function into a file. It takes almost no time to do this, so there’s really no loss in trying the anonymous function in the first place.

Thanks for commenting! Thanks also to Steve for jumping in.

It seems to me that cellfun and arrayfun could both be special cases of map, with the syntax:

varargout = map(funcs,vals,name,value)

where name and value could allow for non-uniform output, as in the existing cellfun and arrayfun.

Andrew,

Great comments. Yes, cellfun and arrayfun are special cases of mapping. Of course, since we *used* cellfun to build our map function, it would be weird to then use map to implement cellfun. :) Indeed, mapping values to functions or functions to values are really the same thing because we can think of functions *as* values. Thanks for pointing this out!

- Tucker

There is a subtle difference when using your “iif” function vs. regular if/else statements: short-circuiting behavior.

If/else will only execute the second condition if the first one evaluated to false. Your function ‘iif’ (mainly the call to FIND) will always execute all conditions.

Consider the example below:

function x = a(x)
disp(‘inside a’)
end
function x = b(x)
disp(‘inside a’)
end

Now compare:

if a(true), 1, elseif b(false), 2, end

against

find([a(true) b(false)], 1, ‘first’)

@Chris:
you can get a look at the captured scope of a function handle:


>> X = randn(1000);
>> f = @() eig(X);


>> fhinfo = functions(f)
fhinfo =
function: '@()eig(X)'
type: 'anonymous'
file: ''
workspace: {[1x1 struct]}


>> fhinfo.workspace{1}
ans =
X: [1000x1000 double]

I would be curious to know what (if any) drawbacks there are in terms of performance to such techniques. This seems like a different programming paradigm, and as such might not optimize as well in the JIT as more conventional techniques.

For example, a function I often use when doing regular expressions is:

t = regexp(string, pattern, ‘tokens’);
extract = @(t, ind) cellfun(@(c), c{ind}, t, ‘uniformoutput’, false);

The “extract” anonymous function is very useful to extract the the data from different token matches from the regexp command, but I find that when running very large pattern matches, most of the execution time is spent in that “extract” function. In fact, when performance is a concern, I am forced to use a for-loop and forgo the convenience of that function.

Have you tried to use your functional programming techniques on large data sets?

Great point about short-circuiting behavior, Amro. I use this all the time, and in fact the version of iif that’s included with the file exchange entry actually does the short-circuiting as you’d expect (by allowing the conditions to be either numbers *or* function handles). If they’re function handles, they’re evaluated only until a true is found. I thought it was a bit too much to try to add in to the anonymous version of iif!

Consider how short-circuit behavior might be useful. Below we’ll test that a figure exists and is visible before doing something. If it doesn’t exist, ishandle(h) will return false, so ~ishandle(h) will return true. The || then sees it’s already found something true and returns true, without ever having run the get(h, ‘Visible’) part.

if ~ishandle(h) || strcmp(get(h, ‘Visible’), ‘off’)
error(‘I need a visible figure.’);
else
% do something
end

Using short-circuiting behavior therefore helps organize code logically and efficiently.

Again, the version of “iif” in the File Exchange entry includes short-circuiting behavior, while our anonymous version does not.

Hi Sean,

I’ve used these with large-ish data and, at least for the way I was using them, haven’t found significant differences between my anonymous functions and subfunctions, nested functions, or normal functions. On the other hand, I’m sure there are some differences somewhere, considering they are likely handled differently by the engine. Also, like Steve mentioned above, we’re always working to find ways to make execution time faster for various constructs. So I suppose the best approach if speed is becoming a problem is to consider how functions are using their variables (e.g., are we storing a lot in cell arrays? is that appropriate? would a normal array be better?) and to test a few implementations. It only takes a moment, and a faster version can quickly pay off in run-time.

- Tucker

The programming style advocated is interesting, but I’d argue it has limited efficiency. While it will definitely speed up writing the code, what is the impact on future use?

Anonymous functions are powerful, but tend to encourage a brief coding style with short variable names, limited spacing and no comments. This style is ok for bashing out initial code, but awful for efficient long term maintenance.

I was taught coding standards based on ADA83 that encouraged long names, lots of white space and heavy commenting. Since then I’ve become proficient in Matlab but maintain my original coding style. I frequently receive comments on how nice my code is when I share it, and this then leads to me being regarded as the team’s Matlab expert. However, many of my colleagues are actually just as skilled, but their insistence on calling every variable x, cramming multiples statements on one line and not commenting means no one can understand their code.

As such, I’d much rather receive a cluster of files containing well written functions than code full of incomprehensible anonymous functions.

I’m glad someone mentioned readability. Thanks, Graeme. In fact, since functional programming can be so concise, it’s often known as being “unreadable”. However, I’ve seen excellent programming in many styles, including functional, imperative, and object-oriented, and I’ve learned that the degree to which code is readable is a function of the author’s care. Anonymous functions in particular bring a few potential readability *enhancements* to the table even, including the fact that their implementation is immediately available to be read (not hidden in another file) and that it reduces the need for mere “wrapper” functions to be implemented in tons of files. I often need to have a single function wrapped in three or four different ways for different purposes. The ability to do so without extra and trivial files floating around is a boon to maintaining the overall project. Care must be taken, and Graeme’s absolutely correct that very powerful code could allow an unwary coder to create something unmaintainable. Whitespace, consistent naming, and luxurious comments are always recommended, and these can help create fantastic-looking code in any style.

While on the subject, I would also recommend Google’s C++ coding standards. They’ve always seemed sensible and very easy, and adapting them to MATLAB is fairly direct.

With regard to anonymous functions capturing too much in the workspace, there is a workaround for the case where you do not need any workspace variables: Use str2func to create the anonymous function. It creates the anonymous function with no saved workspace, at the expense of the editor thinking the code is a string (no bracket matching, autocompletion, or parameter tooltips).

Arthur,

I’d be careful about converting strings into executable code like STR2FUNC. I don’t remember if MATLAB Compiler recognizes that pattern, but I don’t think it does. If it doesn’t you may run into the second and third problems on this page if you attempt to use STR2FUNC.

http://www.mathworks.com/help/compiler/limitations-about-what-may-be-compiled.html

If you know that the anonymous function you want to create doesn’t need any workspace variables, you could call a subfunction (with 0 inputs) that constructs the anonymous function and returns it. In this case, the whole (empty) workspace of the subfunction would be captured but that shouldn’t be a problem.

Hi. I’m still a rookie when it comes to Matlab, and I was trying to deepen my knowledge about anonymous functions. The code to implement conditions in anonymous functions seems pretty simple, but I’m having some doubts regarding the last ()… Is it possible for anyone to explain me why it is necessary?

iif = @(varargin) varargin{2 * find([varargin{1:2:end}], 1, ‘first’)}();

Thanks!

Hi Daniel,

If you’re a rookie, then you’re learning very fast! The part before the parentheses:

varargin{2 * find([varargin{1:2:end}], 1, ‘first’)}


returns a function handle, but doesn't evaluate the function. So how do you evaluate a function represented by a function handle? By putting () on the end. It's no different from x here:


x = @() fprintf('Hi.\n');
x();

Hope that helps.

I’m sorry, I can’t edit the last reply so I had to create a new one.

Why is is necessary to add the @() into the statements to be avaluated if the conditions are true?

I’ve tried the function without the @() and they work just aswell.

If anyone could clear this doubts I’d be very grateful.

Thanks.

Thanks for the answer Tucker McClure. I’ll try to look into function handles to fully understand your explanation.

Regarding my last answer, I’ve made some typos. I should have written
is it*
evaluated*
as well*

Thanks.

Hi Daniel,

Good question. If we write the following:

iif(rand < 0.5, fprintf('Heads\n'), ...
    true,       fprintf('Tails\n'));

then both the fprintf(‘Heads\n’) and fprintf(‘Tails\n’) statements are *executed*. Their results are passed in to iif. So you’ll see “Heads” and then “Tails” printed to the screen. *Then* iif runs. However, if we instead use @() and write:

iif(rand < 0.5, @() fprintf('Heads\n'), ...
    true,       @() fprintf('Tails\n'));

then what’s passed in to iif is a *handle* to a function that takes no arguments. When that function is executed, it prints something.

When you run the first version, you see that both “Heads” and “Tails” are printed. However, in the second, only one or the other is printed, because they aren’t executed until the iif function has decided which to execute.

If you don’t mind the extra execution, then it works either way. For instance, you wouldn’t see any difference in these:

iif(rand < 0.5, magic(3), ...
    true,       eye(3))

iif(rand < 0.5, @() magic(3), ...
    true,       @() eye(3))

because you don’t notice that magic(3) or eye(3) runs; you only notice what gets returned. I used fprintf in the example above so that you could see that both were being evaluated before being passed into iif.

Also, it’s worth noting that putting () on the end of a value, like x = 5; x(), just returns the value of x, so iif works when the inputs are function handles *or* values.

Great questions, Daniel. You should probably stop calling yourself a “rookie” now. :)

Hi all,

Consider this code:

strs = {‘all’,'of’,'my’,'strings’}
sprintf(‘%s__’,strs{:})

One way I would find the functional programming _really_ useful is if there were a way to take in a cell, and return that cell’s contents as multiple outputs. Some function like, say, “list()”, which would reproduce the same output as above given the code:

sprintf(‘%s__’,list({‘all’,'of’,'my’,'strings’}))

Very often I find myself needing to write a loop like:

strSets = {{‘all’,'of’,'my’,'strings’}, {‘other’,'strs’}}
outputs = cell(size(strSets));
for i = 1:length(strSets)
outputs{i} = sprintf(‘%s__’,strSets{i}{:})
end

Whereas I could accomplish the same thing more succinctly using:

outputs = cellfun(@(strs)sprintf(‘%s__’,list(strs)),strSets,’Un’,0);

Hi Sven,

I’m not sure I understand you exactly, but I believe your ‘list’ function is the same as using “curly(…, ‘:’)”, which is the same as “{:}” except that ‘curly’ can accept whatever’s returned by a function, whereas ‘{:}’ cannot. For example:

[en, es, de] = curly({‘hi’, ‘hola’, ‘hallo’}, ‘:’)

This unfolds ‘hi’ into ‘en’, ‘hola’, into ‘es’, and ‘hallo’ into ‘de’.

Does that do it?

Hi Tucker,

Using ‘curly’ is *almost* the same as the proposed ‘list’ function but there’s one important difference. ‘curly’ requires every output to explicitly supplied, whereas I would like to simply expand an output list to *as many outputs as are elements in the given cell*.

For example, consider the difference between curly() and the {:} operator below:

>> curly = @(x, varargin) x{varargin{:}};
>> cellOfStrs = {‘hi’, ‘hola’, ‘hallo’};
>> sprintf(‘%s_’, curly(cellOfStrs, ‘:’))
ans =
hi_
>> sprintf(‘%s_’, cellOfStrs{:})
ans =
hi_hola_hallo_

The particular example I’ve given (joining strings) happens to have a solution in the 2013a release (strjoin), however the ‘list’ function I’m talking about would have much greater use beyond this one example. It would be helpful for any function that takes an arbitrary number of inputs such as, for example, sub2ind:

Let’s just say you had some matrix of a given size and a set of subs references into that matrix.

matSize = [10 10 10];
numDims = length(matSize);
indsIJK = randi(10,5,numDims);
% The regular way (must explicitly know how many dimensions)
idxs = sub2ind(matSize, indsIJK(:,1), indsIJK(:,2), indsIJK(:,3))
% The flexible way (doesn’t matter how many dimensions but needs 2 lines)
indsCell = num2cell(indsIJK,1);
idxs = sub2ind(matSize, indsCell{:})
% The desired way (which could *also* go inside a cellfun() call if needed)
idxs = sub2ind(matSize, list(num2cell(indsIJK,1)))

The flexible way cannot go inside an anonymous function because it requires that extra line to *explicitly* make the indsCell in order to unpack it with {:} on the very next line.

The desired way would overcome this, wouldn’t you agree?

I have a feeling that what I’m looking for is actually not possible, but I think its behaviour can be very neatly summed up as “a functional form of the {:} operator”, so I very much look forward to having it available one day :)

Hi Sven,

Though it’s not intuitive, the answer is that you can (approximately) do this! It requires the ‘use’ function that I put in the File Exchange entry and that’s also defined below. Consider a short example, where we have a function, f, that returns some stuff we want to pass as individual arguments to another function, plot.

f = @() {1:10, (1:10).^2, ‘b.’};

What we’d like to do is this:

L = f();
plot(L{:});

The following snippet doesn’t quite work, because MATLAB will interpret functions called in “argument spots” as single-output:

plot(curly(f(), ‘:’)); % Only gets first argument, 1:10!

In this way, ‘curly’ is not exactly the same as ‘{:}’, as you pointed out. How can we use ‘{:}’? I provided the ‘use’ function for cases in which you want to “name” a function’s output from inside an anonymous function. All but the last argument to ‘use’ will be passed into the last argument, which is assumed to be a function handle taking all those arguments. It’s quicker just to show:

use(f(), @(L) plot(L{:})) % Name outputs of f as L and use!

With ‘use’, you could also create a variation of the ‘list’ function you describe.

list = @(f1, f2) use(f1(), @(L) f2(L{:}));
list(f, @plot) % List outputs from f as inputs to @plot.

Just for kicks, ‘use’ itself can be anonymous as well.

use = @(varargin) varargin{end}(varargin{1:end-1});

So it’s not exactly in the format you suggest, and as you point out, that format might not be possible, but I think this captures the nature of what you’re looking for. Yes? No?

These postings are the author's and don't necessarily represent the opinions of MathWorks.