Lecture 9

Chapter 10 - Advanced Functions

Synopsis
In Lecture 3 and Lecture 6, we have discussed how to define functions and subfunctions. In this lecture, we will introduce even more types of functions and the concept of function handles. In addition, we will cover additional topics from Chapter 10 - Advanced Functions including recursion.


Daily Quiz

Quiz 16


Anonymous Functions

When we have a task that needs to be repeated many times, we have see the utility of writing code to accomplish said task as a function. In this way, we can call the function as many times as necessary. But sometimes, we don't really need a whole function file to accomplish our task. Perhaps we have a one-liner that we want to repeat several times. We could write this as a separate file, but they our project has just expanded in size. Instead, MATLAB allows us to define one-line functions called anonymous functions. These are functions that are defined within our current script of function file. They take the following form:

1
fn = @(arguments) function body;

At first look, anonymous functions are a bit weird. Let's step through the different elements. It should be clear that we are making an assignment to the variable fn. In this case, fn is being assigned a function handle (ie. a pointer to the one-line function on the right). The @ symbol is the operator that return the handle to the contents on the right. In parentheses, we have the list of argument variables passed to the function. Lastly, the function body represents the code for the one-line function. We can get a better idea of how these functions work by walking through an example.

Suppose that we would like a piece of code that plots the profile of a grayscale image (ie. either the row sum or column sum of a matrix). We could do this with a function defined in a separate file, but it will be more convenient to use an anonymous function. Our function will be called plotprof and will accept two arguments--the image and the dimension to use for the profile.

1
2
% define a function to plot the profile of an image
plotprof = @(img, dim) plot(sum(img,dim));

Now that we have our function defined, let's look at how we call this function.

1
2
3
4
5
% load an image
I = imread('/path/to/image.jpg');

% plot row profile
plotprof(I, 1)

As you can see, we call anonymous functions in the same way we call built-in functions. The one difference is that we must always include the parentheses--even when the function has no arguments. In addition to making your code more efficient, anonymous functions help keep your code clean by reducing the number of repeated lines.

A key part of anonymous functions is retrieving the function handling using the @ operator. In addition to providing handles to defined functions, the @ operator can also return handles to built-in functions. This is particularly useful if we want to pass a function to another function.

Function Functions

A function that accepts a function as input is called a function function (too many functions?). When would this be useful? Well, it might make more sense if we consider passing a mathematical function (ex. f(x) = x+2 ). Perhaps we would like to plot a series of mathematical functions in a similar way. We can do this by defining a function function that accepts our mathematical expressions and creates a specific type of plot. Let's write a function function that plots an expression for the values x = 1 to x = 5.

1
2
3
4
function plot1to5(fn)
    x = 1:5;
    plot(x, fn(x))
end

We can call this function by passing it the handle to our expression of interest.

1
2
3
4
5
6
% plot sin function from x = 1 to x = 5
plot1to5(@sin)

% plot an anonymous function
myfunc = @(x) x+32;
plot1to5(myfunc)

Variable Number of Arguments

So far, we have only allowed our functions to accept and return a fixed number of variables. To add flexibility to our functions, we might like to allow for an unknown number of input or output arguments. Fortunately, MATLAB provides four key elements that allow for variable number of arguments: varargin, varargout, nargin, and nargout. As a placeholder for potential input and output arguments, we can use the cell arrays varargin and varargout, respectively. We use cell arrays because the type of the input arguments may not be the same. In order for us to know how many variables have been passed or should be returned, we use the functions nargin and nargout, respectively. These functions give you the number of output arguments and input arguments used in the function call. Let's look at an example to see how these elements work together to expand our functions. Suppose we'd like to write a matrix multiplication function that can accept one or two arguments. If one argument is provided, it will multiply that matrix by itself. If two arguments are provided, it will perform standard matrix multiplication. There are multiple methods for writing this function, but we will cover the preferred method here.

1
2
3
4
5
6
7
8
9
10
function prod = matmult(A, varargin)
    % check if one or two arguments passed
    if nargin == 1
        prod = A*A;
    elseif nargin == 2
        prod = A*varargin{1};
    else
        disp('too many inputs')
    end
end

You can see that we specify the first argument as A. We do this because our function requires at least one argument to run. Any addition input will be captured by varargin. We can check how many inputs are passed by nargin, and use that information to decide which calculation to perform. Remember, varargin is a cell array, and the contents must be referenced using {}.

What about variable output arguments? Fortunately the same logic applies. Assume that we need a function that returns the dimensions of a matrix as either a 1x2 vector or as two separate scalars. We can do this using varargout and nargout.

1
2
3
4
5
6
7
8
9
10
11
12
function [varargout] = matdim(A)
    % check if one or two output arguments in call
    if nargout == 1
        varargout{1}(1,1) = size(A,1);
        varargout{1}(1,2) = size(A,2);
    elseif nargout == 2
        varargout{1} = size(A,1);
        varargout{2} = size(A,2);
    else
        disp('not correct number of outputs');
    end
end

Using variable input and output arguments can add a great deal of complexity to your functions, but provides a vast improvement in flexibility. You will find yourself using variable input arguments frequently when writing code used by other programmers.

Recursive Functions

A recursive function is a function that is defined in terms of itself (a dream within a dream). Recursion is used often in programming, and can provide an elegant solution to a variety of problems. Assume that we would like to write a function to compute factorials (ex. 4! = 4 x 3 x 2 x 1 = 24). Upon closer inspection, you can see that factorial is composed of a general case ( n! = n x (n-1)! )and a base case ( 1! = 1 ). You might notice that our general case contains a factorial in the expression. As such, we have provided a recursive definition of factorial. How is this programmed?

1
2
3
4
5
6
7
8
function facn = fact(n)
    %check if we are at base case
    if n == 1
        facn = 1;
    else 
        facn = n* fact(n-1);
    end
end

By calling fact from within fact, we are recursively executing the same function. If we left out the base case, the function would infinitely recurse. But the base case allows us to recurse until we reach the value of 1. At which point, values for facn are returned to the previous call of fact. If we passed the value 4, the code would ultimately perform 4 x (3 x (2 x 1)). Although factorials are often used to explain recursion, there are many other situations where recursion might be useful.


Final Words

Functions are an indispensable part of programming in MATLAB. They provide a convenient way to write reusable code that is flexible and succinct. Chapter 10: Advanced Functions introduced many key concepts that you will likely rely on heavily in your own projects. We will explore anonymous functions and variable arguments in the following lab.