Skip to content

Commit

Permalink
Merge pull request #24 from UoMResearchIT/feat/easy-starting-func
Browse files Browse the repository at this point in the history
Feat/easy starting func
  • Loading branch information
fherreazcue authored Oct 18, 2023
2 parents 7eeb9af + f42d3da commit e23b15d
Showing 1 changed file with 156 additions and 40 deletions.
196 changes: 156 additions & 40 deletions episodes/06-func.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,41 +6,52 @@ exercises: 20

::::::::::::::::::::::::::::::::::::::: objectives

- "Compare and contrast MATLAB function files with MATLAB scripts."
- "Learn how to write a function"
- "Define a function that takes arguments."
- "Test a function."
- "Compare and contrast MATLAB function files with MATLAB scripts."
- "Recognize why we should divide programs into small, single-purpose functions."

::::::::::::::::::::::::::::::::::::::::::::::::::

:::::::::::::::::::::::::::::::::::::::: questions

- "How can I teach MATLAB how to do new things?"
- "How can I teach MATLAB to do new things?"
- "How can I make programs I write more reliable and re-usable?"

::::::::::::::::::::::::::::::::::::::::::::::::::

## Writing functions from scratch

It has come to our attention that the data about inflammation that we've been analysing contains some systematic errors.
The measurements were made using the incorrect scale, with inflammation recorded in Arbitrary Inflammation Units (AIU)
rather than the scientific standard International Inflmmation Units (IIU). Luckily there is a handy formula which can be
used for converting measurements in AIU to IIU, but it involves some hard to remember constants:

In the `patient_analysis` script we created,
we can choose which patient to analyse by modifying the variable `patient_number`.
If we want patient 13, we need to open `patient_analysis.m`, go to line 9, modify the variable,
save and then run `patient_analysis`.
This is a lot of steps for such a simple request.
```matlab
inflammation_IIU = (inflammation_AIU + B)*A
B = 5.634
A = 0.275
```
There are several files worth of data to be converted from AIU to IIU: is there a way we can do this quickly and
conveniently? If we have to re-enter the conversion formula multiple times, the chance of us getting the constants
wrong is high. Thankfully there is a convenient way to teach MATLAB how to do new things, like our converting AIU to IIU.
We can do this by writing a function.

We have used a few predefined Matlab functions, to which we can provide arguments.
So how can we define a function in Matlab?
We have already used some predefined MATLAB functions which we can pass arguments to. How can we define our own?

A MATLAB function *must* be saved in a text file with a `.m` extension.
The name of that file must be the same as the function defined
inside it. The name must start with a letter and cannot contain spaces.
The name of the file must be the same as the name
of the function defined in the file.

The first line of our function is called the *function definition*.
Anything following the function definition line is called the *body* of the
function. The keyword `end` marks the end of the function body, and the
function won't know about any code after `end`.
The first line of our function is called the *function definition* and must include the special `function` keyword to
let MATLAB know that we are defining a function. Anything following the function definition line is called the *body*
of the function. The keyword `end` marks the end of the function body. The function only knows about code that comes
between the function definition line and the `end` keyword. It will not have access to variables from outside this block
of code apart from those that are passed in as *arguments* or *input parameters*. The rest of our code won't have access
to any variables from inside this block, apart from those that are passed out as *output parameters*.

A function can have multiple input and output parameters if required,
but isn't required to have any of either.
The general form of a function is shown in the pseudo-code below:
A function can have multiple input and output parameters as required, but doesn't have to have any. The general form
of a function is shown in the pseudo-code below:

```matlab
function [out1, out2] = function_name(in1, in2)
Expand All @@ -49,15 +60,15 @@ function [out1, out2] = function_name(in1, in2)
% An example is always useful!
% This section below is called the body of the function
out1 = something calculated;
out2 = something else;
out1 = calculation using in1 and in2;
out2 = another calculation;
end
```

Just as we saw with scripts, functions must be _visible_ to MATLAB, i.e.,
a file containing a function has to be placed in a directory that
MATLAB knows about. The most convenient of those directories is the
current working directory.
Just as we saw with scripts, functions must be _visible_ to MATLAB,
i.e., a file containing a function has to be placed in a directory that MATLAB knows about.
Following the same logic we used with scripts,
we will put our code source files in the `src` folder.

::::::::::::::::::::::::::::::::::::::::: callout

Expand All @@ -68,7 +79,115 @@ the path for functions called from the command line.

::::::::::::::::::::::::::::::::::::::::::::::::::

We already have a `.m` file called `patient_analysis`, so lets define a function with that name.
Let's put this into practice to create a function that will teach MATLAB to use our AIU to IIU conversion formula.
Create a file called `inflammation_AIU_to_IIU.m` in the `src` folder,
enter the following function definition, and save the file:

```matlab
function inflammation_in_IIU = function inflammation_AIU_to_IIU(inflammation_in_AIU)
% INFLAMMATION_AIU_TO_IIU Convert inflammation mesured in AIU to inflammation measued in IIU.
A = 0.275;
B = 5.634;
inflammation_in_IIU = (inflammation_in_AIU + B)*A;
end
```
We can now call our function as we would any other function in MATLAB:

```matlab
>> inflammation_AIU_to_IIU(6)
```

```output
ans = 3.19935
```

We got the number we expected, and at first glance it seems like it is almost the same as a script.
However, if you look at the variables in the workspace, you'll probably notice one big difference.
Although a variable called `inflammation_in_IIU` was defined in the function, it does not exist in our workspace.

Lets have a look using the debugger to see what is happening.

When we pass a value, like `6`, to the function, it is assigned to the variable `inflammation_in_AIU` so that it can
be used in the body of the function. To return a value from the function, we must assign that value to the variable
`inflammation_in_IIU` from our function definition line. What ever value `inflammation_in_IIU` has when the `end`
keyword in the function definition is reached, that will be the value returned.

Outside the function, the variables `inflammation_in_AIU`, `inflammation_in_IIU`, `A`, and `B` aren't accessible; they
are only used by in function body.

This is one of the major differences between scripts and functions: a script can be thought of as automating the command line, with full access to all variables in the base workspace, whereas a function has its own separate workspace.

To be able to access variables from your workspace inside a function, you have to pass them in as inputs.
To be able to save variables to your workspace, it needs to return them as outputs.

As with any operation, if we want to save the result, we need to assign the result to a variable, for example

```matlab
>> val_in_IIU = inflammation_AIU_to_IIU(6)
```

```output
val_in_IIU = 3.19935
```

And we can see `val_in_IIU` saved in our workspace.

::::::::::::::::::::::::::::::::::::::: challenge

## Writing your own conversion function

We'd like a function that reverses the conversion of AIU to IIU. Re-arrange the conversion
formula and write a function called `inflammation_IIU_to_AIU` that converts inflammation measued in IIU to inflammation
measured in AIU.

Remember to save your function definition in a file with the required name, start the file with the function definition
line, followed by the function body, ending with the `end` keyword.

::::::::::::: solution

```matlab
function inflammation_in_AIU = inflammation_IIU_to_AIU(inflammation_in_IIU)
% INFLAMMTION_IIU_TO_AIU Convert inflammation measured in IIU to inflammation measured in AIU.
A = 0.275;
B = 5.634;
inflammation_in_AIU = inflammation_in_IIU/A - B;
end
```

:::::::::::::::::::::::::

::::::::::::::::::::::::::::::::::::::::::::::::::

::::::::::::::::::::::::::::::::::::::::: callout

## Functions that work on arrays

One of the benefits of writing functions in MATLAB is that often they will also be able to operate on an array of numerical variables *for free*.

This is only the case when each operation in the function can be applied to an array too.
In our example, we are adding a number and multiplying by another, both of which are ok to operate on arrays.

This will make converting the inflammation data in our files using the function we've just written very quick. Give it
a go!

::::::::::::::::::::::::::::::::::::::::::::::::::

## Transforming scripts into functions

In the `patient_analysis` script we created, we can choose which patient to analyse by modifying the variable
`patient_number`. If we want information about patient 13, we need to open `patient_analysis.m`, go to line 9, modify
the variable, save and then run `patient_analysis`. This is a lot of steps for such a simple request.

Can we use what we've learned about writing functions to transform (or *refactor*) our script into a function, increasing
its usefulness in the process?

We already have a `.m` file called `patient_analysis`, so lets begin by defining a function with that name.

Open the `patient_analysis.m` file, if you don't already have it open.
Instead of line 9, where `patient_number` is set, we want to provide that variable as an input.
Expand Down Expand Up @@ -107,10 +226,10 @@ function patient_analysis(patient_number)
end
```

Congratulations! You've now created a Matlab function.
Congratulations! You've now created a Matlab function from a Matlab script!

You may have noticed that the code inside the function is indented.
Matlab does not need this, but it makes it much more readable!
You may have noticed that the code inside the function is indented. Matlab does not need this, but it makes it much more
readable!

Lets clear our workspace and run our function in the command line:
```matlab
Expand All @@ -131,16 +250,10 @@ Lowest min?
So now we can get the patient analysis of whichever patient we want,
and we do not need to modify `patient_analysis.m` anymore.
However, you may have noticed that we have no variables in our workspace.
Inside the function, the variables `patient_data`, `g_mean`, `g_max`, `g_min`, `p_mean`,
`p_max`, and `p_min` are created, but then they are deleted when the function ends.

This is one of the major differences between scripts and functions:
a script can be thought of as automating the command line,
with full access to all variables in the base workspace,
whereas a function has its own separate workspace.

To be able to access variables from your workspace, you have to pass them in as inputs.
To be able to save variables to your workspace, it needs to return them as outputs.
Remember, inside the function, the variables
`patient_data`, `g_mean`, `g_max`, `g_min`, `p_mean`, `p_max`, and `p_min` are created,
but then they are deleted when the function ends.
If we want to save them, we need to pass them as outputs.

Lets say, for example, that we want to save the mean of each patient.
In our `patient_analysis.m` we already compute the value and save it in `p_mean`,
Expand Down Expand Up @@ -336,7 +449,10 @@ end

:::::::::::::::::::::::::::::::::::::::: keypoints

- "Break programs up into short, single-purpose functions with meaningful names."
- A MATLAB function *must* be saved in a text file with a `.m` extension. The name of the file must be the same as the name
of the function defined in the file.
- "Define functions using the `function` keyword."
- Functions have an independent workspace. Access variables from your workspace inside a function by passing them as inputs. Access variables from the function returning them as outputs.
- "Break programs up into short, single-purpose functions with meaningful names."

::::::::::::::::::::::::::::::::::::::::::::::::::

0 comments on commit e23b15d

Please sign in to comment.