Functions and Scripts

Encapsulation

A key idea of structured programming:

  • package code into self-contained functions
  • avoid side effects

A function should only change the rest of the world through the outputs it explicitly returns. This is called encapsulation.

For this to work, all variables inside a function must be invisible to other code – they are local.

Example:

function y = f(x)
	a = 5;
	y = x + a;
end

% Command line:
>> a = 3; y = f(0); disp(y)
5
>> disp(a)
3
% Now the converse
function y = g(x)
	z = x + a;
end
>> g(0)
% Error: `a is not defined`

Namespaces

A namespace is a set of functions or scripts that can “see” the same objects.

Example:

  • In the function f(x) above, a was in f(x)’s namespace, but not in g(x)’s.
  • Conversely, the a inside f(x) is local and not in the command line’s namespace.

In Matlab, there are only 2 namespaces:

  1. Global: this is what you can access from the command line.
  2. Local: inside a function.

Note: This is not exactly true because there are nested functions.

This means in particular that all global variables and all functions are visible everywhere. Their names must be unique.

Other languages have more control over namespaces (using “modules”).

Functions versus Scripts

Scripts are simply collections of commands that are run as if they were typed at the command prompt.

Scripts run in the global workspace. This is, generally speaking, not good. They create side effects.

Rule of thumb:

Always use functions, never scripts!

Functions are similar to scripts with one crucial difference:

  • All variables inside a function are private.
  • Other functions cannot see the variables inside a function (encapsulation).
  • Any variable a function should know must be passed to it as an input argument.
  • Any variable to be retained after the function finishes must be passed out of it as a return argument.

An important principle of structured programming:

Package a well-defined task into a function with a simple interface.

A function looks like this

function [y1, y2] = fname(x1, x2)
	% Do stuff with x1 and x2 to generate y1 and y2
end

This would be stored in a text file called fname.m.

A side note: Even built-in Matlab commands are often written in Matlab.

Try open sub2ind. You get to see (and you can modify) the source code for the built-in sub2ind command (sub2ind.m).

Now try open zeros. This opens zeros.m.

  • you get an m-file that is blank except for comments
  • this means: zeros is not written in Matlab (it’s truly built-in)

The Matlab Path

The Matlab path determines which m-files are visible in the global namespace (and inside all functions).

In contrast to other languages, this is an all or nothing affair: If a function is visible somewhere, it is visible everywhere.

When the user issues a command, such as m = zeros([3,4])), Matlab searches for a matching m-file, in this case zeros.m and runs it.

It does not matter whether zeros is a built-in command or an m-file written by a user.

Where Matlab looks is defined by the matlab path.

This is a list of directories that Matlab goes through when it looks for functions. In case you wonder, this is done as a cell array of strings.

You can see what’s on the path by typing path.

You add directories to the path with addpath.

In addition, Matlab searches the current directory (the one shown in the file browser).

Therefore, every time you write new code, you need to put the directories on the path before the code can be called.

There is no way to allow one function to access some code without changing the path globally. This differs from other languages. It makes it hard to organize code.

There is one exception: local functions are only visible to functions within the same m-file.

Organizing Code

When you write new commands, you need to make sure Matlab can find them. This is best accomplished in two ways:

  1. For files that belong to the one particular project, place them in a project directory. Then switch the current directory to that directory using cd.

    Or put the directory on the path using a startup routine.

  2. For files that are shared between several projects, place them in a special directory (blah/shared). Place this directory on the path: addpath('blah/shared') .

Over time you will write many general purpose routines that should be stored in this shared directory. Reusable code!

Name Conflicts

Now you run into a problem: name conflicts.

Each time you write a new function, you need to make sure that there is no other function with the same name.

There are 2 ways of ensuring this:

  1. Suffixes: For each project, invent a suffix and append it to every function name.

    Such as plot_821.m

  2. Packages: If you place an m-file into a directory that starts with +, let’s say +econ821, you can access it like this: econ821.plot(x).

    For this to work, the parent dir of +econ821 must be on the path.

    This is also useful to organize your code within a project (which may contain hundreds of functions).

Example of a Function

Imagine we want to compute the sum of 3 variables.

Type the following code in a text editor:

function xSum = sum3(x1, x2, x3);
% This function sums 3 variables
	sum1 = x1 + x2;
	xSum = sum1 + x3;
end

Save it as sum3.m in a directory Matlab knows to find.

Row 1 defines the name of the function, its input arguments and its output arguments.

Row 2 is a comment starting with %. Matlab ignores it.

Rows 3 and 4 contain the commands to be executed.

To run sum3.m: At the command prompt type

	sum3(1, 2, 3)
	ans =
	6

To illustrate that the variables inside sum3.m are private, try typing sum1 at the command prompt. The result:

??? Undefined function or variable 'sum1'.

Matlab complains that variable sum1 does not exist.

sum1 was visible inside of sum3.m, but not outside of it.

More on Private Variables

Variables inside of sum3.m are also not visible in functions called from within sum3.m.

Example

Modify sum3.m to read:

	function xSum = sum3(x1, x2, x3);
		sum1 = x1 + x2;
		sum3sub;
		xSum = sum1 + x3;
	end

Then create the function:

	function sum3sub
		disp(sum1)
	end

Running sum3.m results in an error: sum1 is not found.

Global Variables

To make a variable visible from anywhere, define it as a global (in sum3.m and sum3sub.m):

global sum1;

Now running sum3.m does not yield an error message.

Remark: Avoid globals where possible.
A function should be a self-contained unit with a clear interface to the outside world (via its input and output arguments).
Globals create confusion.

Nested Functions

If you need a function to see the local variables in another function, nest it inside the other function.

Example:

function f
	a = 5;
	disp(g(17));
	% After calling g(), a == 52

	% Nested function can see `a`
	function z = g(x)
		z = x + a;
		a = 52;
	end
end

The main use of this: optimization. See below.

Passing by Value

In Matlab, all function arguments are passed by value.

That means, Matlab creates a copy of the variable and passes it to the function.

However, Matlab is smart enough not to create a copy when the function does not change the variable (“copy on change”).

Still, this can be very inefficient. If you pass a gigantic array to a function and change one element inside the function, the entire array needs to be copied.

There is no way of passing by reference.

Examples

Example: Cobb-Douglas production function

This is a general purpose Cobb-Douglas function

Notable features:

  • block comment describing inputs and outputs

    this is all the rest of the world needs to know about the function

  • a unique name

  • self-test code governed by debugging switch dbg

Example: Finding the Zero of a Function

Task: Find the solution to the equation \(f(x)=\ln(x)-5=0 \).

Set up a function that returns the deviation f(x)

	function dev = dev1_821(x);
	dev = log(x) - 5;
	end

Use the built-in function fzero to search for the solution:

	fzero('dev1_821', [0.1 100])
	ans =
	7.3891

What if I want to pass additional parameters to the objective function? Make it a nested function.

Example here.

Example: Two period household

Household solves \(\max u\left(c,g\right)\)

subject to \(y=c+s \) and \(g=z+sR\)

A solution: \(c,g,s\)

that solve 2 budget constraints and Euler equation

\(u_{c}=u_{g} R \)

Assume \(u\left(c,g\right)=\frac{c^{1-\sigma}}{1-\sigma}+\beta\frac{g^{1-\sigma}}{1-\sigma} \)

Pseudo code

This is not a trivial program to write. So we break it down into trivial steps.

See Tips on programming

We design top-down.

Level 1:

Task: Find optimal \(c\).

  1. Set parameters. Set up a grid of values for c
  2. For each c: Calculate deviation from Euler equation.
  3. Find the c with the smallest deviation.
Note: Usually one would not restrict \(c\) to lie on a grid.

Level 2:

Task: Calculate deviation from Euler equation.

Given: guess for \(c\), parameter values

  1. Use budget constraints to calculate \(s,g\)
  2. Return deviation: \( dev=u_{c}-u_{g}R \)

Level 3:

Utility function.

  • Return \(u_{c} \) and \(u_{g} \)
  • for given \(c,g\) and parameters.

Code

We write the code bottom up.

Utility function:

  • Allow matrix inputs (cM, gM).
  • Parameters as arguments.
  • This should really be a general purpose function (my library contains an OOP version).

Sample call:

>> hh_example_821(2, 0.5, 1.04, 0.9, 1.5)
c = 1.224490   Dev = 0.034947

Exercises

  1. Write a CES utility function that computes \(u’(c)\) and \(u(c)\).
  2. Write a function that computes the inverse of \(u’(c)\).
  3. Write a test function that checks properties of the utility function: 4. The inverse of the inverse equals \(u’(c)\). 5. Marginal utility is decreasing.

Extra credit:

  1. Package all of that into an object (a user defined data type).
  2. Now write all of this for \(u(c)=e^{-\phi c}\).
  3. In your test function, set things up so that you only need to change a single line of code to test both utility functions (the benefit of OOP in action).