recipes : programming : Writing better code : Repeatedly calling a function

Problem

Is there an elegant way to run the same function repeatedly on different data?

Solution

There are several ways of running the same function on different sets of data. Which you choose is, to a large extent, a matter of style. Probably the first approach people use is to put the analyses to be repeated into the body of a loop.

function myAnalyzer(data)

for ii=1:length(data)
   % Data analyses go here
end

This is fine for simple analyses but if a lot of code has to go into that loop (possibly including sub-loops) then you can end up with a function that looks rather messy. The obvious way to make things neater is put the analyses into one or more separate functions. This could either either be a sub-function in the same file or an independent function in a different file. Here, for example, we define helperFunction within the .m file that our analyses are run:

function out=myAnalyzer(data)
for ii=1:length(data)
   out(ii)=helperFunction(data(ii)); %call to internal function
end

function myOutput=helperFunction(data)
% Data analyses go here

The loop is now more readable, since we have off-loaded all the analyses to helperFunction. However, perhaps it still irks you (it does me) that to simplify stuff we've had to add an "extra" function with a silly name. There is an alternative! We can get myAnalyzer function to recursively call itself:

function out=myAnalyzer(data)

if length(data)>1
  for ii=1:length(data)
      out(ii)=myAnalyzer(data(ii)); %Recursive calll
  end
return %Got to have this here!
end

% Data analyses go here

This may be confusing at first... Say a large structure (data) is fed into the myAnalyser function. If length(data) is greater than 1, the if statement is satisfied and the for loop is entered. Within this loop, myAnalyzer calls itself. However, on each pass through the loop it feeds itself just one element of the structure. So when the function calls itself, length(data) equals 1 and the if statement isn't satisfied. Instead the function proceeds to analyse data, returning the results in the variable out. It does this repeatedly until it has gone through all elements of data. Once it has done this, it proceeds to the return statment and exits. Without the return there will be an error once the function has completed the for loop. Can you guess what sort of error might occur?

Discussion

Recursion of course has many other uses. I found one related example in Prata's C++ Primer Plus. In this case we use a recursive sub-function call to create this pattern:

|                                                               |
|                               |                               |
|               |               |               |               |
|       |       |       |       |       |       |       |       |
|   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

The function that builds this ruler is rather nice. The following is the ruler.m file that creates the pattern. The internal, subdivide, function does most of the work. There are comments, but you'll need to think through it in order to understand how it works.

function ruler

len=65; %The ruler length
divs=6; %The number of sub-division levels

%Build the first ruler line
r=repmat(' ',1,len);
r(1)='|'; r(end)='|';

%Display the ruler on-screen
fprintf('%s\n',r)
for ii=1:divs
	tmp=subdivide(r,1,len,ii);
	fprintf('%s\n',tmp)
end


function r=subdivide(r,low,high,level);
if level==0
	return %Stop once the required number of division levels have been made
end

%Find the mid-point
midpoint=(high+low)/2;
r(midpoint)='|';

%Divide each half of the ruler into two
r=subdivide(r,low,midpoint,level-1);
r=subdivide(r,midpoint,high,level-1);