# MATLAB Best Practices
# Indent code properly
Proper indentation gives not only the aesthetic look but also increases the readability of the code.
For example, consider the following code:
%no need to understand the code, just give it a look
n = 2;
bf = false;
while n>1
for ii = 1:n
for jj = 1:n
if ii+jj>30
bf = true;
break
end
end
if bf
break
end
end
if bf
break
end
n = n + 1;
end
As you can see, you need to give a careful look to see which loop and if
statements are ending where.
With smart indentation, you'll get this look:
n = 2;
bf = false;
while n>1
for ii = 1:n
for jj = 1:n
if ii+jj>30
bf = true;
break
end
end
if bf
break
end
end
if bf
break
end
n = n + 1;
end
This clearly indicates the starting and ending of loops/if
statement.
You can do smart indentation by:
• selecting all your code (Ctrl+A)
• and then pressing Ctrl+I or clicking (opens new window) from edit bar.
(opens new window)
# Keep lines short
Use the continuation character (ellipsis) ...
to continue long statement.
Example:
MyFunc( parameter1,parameter2,parameter3,parameter4, parameter5, parameter6,parameter7, parameter8, parameter9)
can be replaced by:
MyFunc( parameter1, ...
parameter2, ...
parameter3, ...
parameter4, ...
parameter5, ...
parameter6, ...
parameter7, ...
parameter8, ...
parameter9)
# Use assert
Matlab allows some very trivial mistakes to go by silently, which might cause an error to be raised much later in the run - making debugging hard. If you assume something about your variables, validate it.
function out1 = get_cell_value_at_index(scalar1,cell2)
assert(isscalar(scalar1),'1st input must be a scalar')
assert(iscell(cell2),'2nd input must be a cell array')
assert(numel(cell2) >= scalar1),'2nd input must have more elements than the value of the 1st input')
assert(~isempty(cell2{scalar1}),'2nd input at location is empty')
out1 = cell2{scalar1};
# Avoid loops
Most of the time, loops are computationally expensive with Matlab. Your code will be orders of magnitudes faster if you use vectorization. It also often makes your code more modular, easily modifiable, and easier to debug. The major downside is that you have to take time to plan the data structures, and dimension errors are easier to come by.
# Examples
Don't write
for t=0:0.1:2*pi
R(end+1)=cos(t);
end
but
t=0:0.1:2*pi;
R=cos(t)
Don't write
for i=1:n
for j=1:m
c(i,j)=a(i)+2*b(j);
end
end
But something similar to
c=repmat(a.',1,m)+2*repmat(b,n,1)
For more details, see vectorization (opens new window)
# Block Comment Operator
It is a good practice to add comments that describe the code. It is helpful for others and even for the coder when returned later.
A single line can be commented using the %
symbol or using the shortkey Ctrl+R
.
To uncomment a previously commented line remove the %
symbol or use shortkey Crtl+T
.
While commenting a block of code can be done by adding a %
symbol at the beginning of each line, newer versions of MATLAB (after 2015a) let you use the Block Comment Operator %{ code %}
.
This operator increases the readability of the code. It can be used for both code commenting and function help documentation.
The Block can be folded and unfolded to increase the readability of the code.
As it can be seen the %{
and %}
operators must appear alone on the lines. Do not include any other text on these lines.
function y = myFunction(x)
%{
myFunction Binary Singleton Expansion Function
y = myFunction(x) applies the element-by-element binary operation
specified by the function handle FUNC to arrays A and B, with implicit
expansion enabled.
%}
%% Compute z(x, y) = x.*sin(y) on a grid:
% x = 1:10;
y = x.';
%{
z = zeros(numel(x),numel(y));
for ii=1:numel(x)
for jj=1:numel(y)
z(ii,jj) = x(ii)*sin(y(jj));
end
end
%}
z = bsxfun(@(x, y) x.*sin(y), x, y);
y = y + z;
end
# Create Unique Name for Temporary File
While coding a script or a function, it can be the case that one or more than one temporary file be needed in order to, for example, store some data.
In order to avoid overwriting an existing file or to shadow a MATLAB function the tempname (opens new window) function can be used to generate a unique name for a temporary file in the system temporary folder.
my_temp_file=tempname
The filename is generated without the extension; it can be added by concatenating
the desired extension to the name generated by tempname
my_temp_file_with_ext=[tempname '.txt']
The locaton of the system temporary folder can be retrieved by caling the tempdir (opens new window) function.
If, during the execution of the function / script, the temporary file is no longer needed, it can be deleted by using the function delete (opens new window)
Since delete
does not ask for confirmation, it might be useful to set on
the
option to move the file to be deleted in the recycle
folder.
This can be done by using the function recycle (opens new window) this way:
recycle('on')
In the following example, a possible usage of the functions tempname
, delete
and
recycle
is proposed.
%
% Create some example data
%
theta=0:.1:2*pi;
x=cos(theta);
y=sin(theta);
%
% Generate the temporary filename
%
my_temp_file=[tempname '.mat'];
%
% Split the filename (path, name, extension) and display them in a message box
[tmp_file_path,tmp_file_name, tmp_file_ext]=fileparts(my_temp_file)
uiwait(msgbox(sprintf('Path= %s\nName= %s\nExt= %s', ...
tmp_file_path,tmp_file_name,tmp_file_ext),'TEMPORARY FILE'))
%
% Save the varaibles in a temporary file
%
save(my_temp_file,'x','y','theta')
%
% Load the varaibles from the temporary file
%
load(my_temp_file)
%
% Set the reclycle option on
%
recycle('on')
%
% Delete the temporary file
%
delete(my_temp_file)
Caveat
The temporary filename is generated by using the java.util.UUID.randomUUID
method
(randomUUID (opens new window)).
If MATLAB is run without JVM, the temporary filename is generated by using
matlab.internal.timing.timing
based on the CPU counter and time. In this case
the temporary filename is not guaranteed to be unique.
# Use validateattributes
The function validateattributes (opens new window) can be used to validate an array against a set of specifications
It can be therefore used to validate the input provided to a function.
In the following example, the function test_validateattributes
requires three
input
function test_validateattributes(input_1,input_2,input_3)
The input specification are:
-
- class: double
- size: [3,2]
- values: elements must be not NaN
- class: char
- value: the string must not be empty
- class: double
- size: [5 1]
- values: elements must be real
A
is the array to be vaidatedclasses
: is thetype
of the array (e. g.single
,double
,logical
)attributes
: are the atrributes the input array has to match (e. g.[3,2], size
to specify the size of the array,nonnan
to specify that the array shall not have NaN values)funcName
: is the name of the function in which the validation occurs. This argument is used in the generation of the error message (if any)
char_array:
array_3
To validate the three input, the function validateattributes
can be called
with the following syntax:
validateattributes(A,classes,attributes,funcName,varName,argIndex)
where:
varName
: is the name of the array under validation. This argument is used in the generation of the error message (if any)
argIndex
: is the position of the inpurt array in the list of input.
This argument is used in the generation of the error message (if any)In case one or more than one input does not match the specification, an error message is generated.
In case of more than one invalid input, the validation stops when the first mismatch is found.
This is function test_validateattributes
in which the input validation
has been implemented.
Since the function requires three input, a first check on the number of input provided is performed using the fnction nargin (opens new window).
function test_validateattributes(array_1,char_array_1,array_3)
%
% Check for the number of expected input: if the number of input is less
% than the require, the function exits with an error message
%
if(nargin ~= 3)
error('Error: TEST_VALIDATEATTRIBUTES requires 3 input, found %d',nargin)
end
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Definition of the expected characteristics of the first input %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% INPUT #1 name (only used in the generation of the error message)
%
input_1_name='array_1';
%
% INPUT #1 position (only used in the generation of the error message)
%
input_1_position=1;
%
% Expected CLASS of the first input MUST BE "double"
%
input_1_class={'double'};
%
% Expected ATTRIBUTES of the first input
% SIZE: MUST BE [3,2]
%
input_1_size_attribute='size';
input_1_size=[3,2];
%
% VALUE CHECK: the element MUST BE NOT NaN
%
input_1_value_type='nonnan';
%
% Build the INPUT 1 attributes
%
input_1_attributes={input_1_size_attribute,input_1_size, ...
input_1_value_type};
%
% CHECK THE VALIDITY OF THE FIRST INPUT
%
validateattributes(array_1, ...
input_1_class,input_1_attributes,'', ...
input_1_name,input_1_position);
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Definition of the expected characteristics of the second input %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% INPUT #1 name (only used in the generation of the error message)
%
input_2_name='char_array_1';
%
% INPUT #2 position (only used in the generation of the error message)
%
input_2_position=2;
%
% Expected CLASS of the first input MUST BE "string"
%
input_2_class={'char'};
%
% VALUE CHECK: the element must be not NaN
%
input_2_size_attribute='nonempty';
%
% Build the INPUT 2 attributes
%
input_2_attributes={input_2_size_attribute};
%
% CHECK THE VALIDITY OF THE SECOND INPUT
%
validateattributes(char_array_1, ...
input_2_class,input_2_attributes,'', ...
input_2_name,input_2_position);
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Definition of the expected characteristics of the third input %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% INPUT #3 name (only used in the generation of the error message)
%
input_3_name='array_3';
%
% INPUT #3 position (only used in the generation of the error message)
%
input_3_position=3;
%
% Expected CLASS of the first input MUST BE "double"
%
input_3_class={'double'};
%
% Expected ATTRIBUTES of the first input
% SIZE: must be [5]
input_3_size_attribute='size';
input_3_size=[5 1];
% VALUE CHECK: the elements must be real
input_3_value_type='real';
%
% Build the INPUT 3 attributes
%
input_3_attributes={input_3_size_attribute,input_3_size, ...
input_3_value_type};
%
% CHECK THE VALIDITY OF THE FIRST INPUT
%
validateattributes(array_3, ...
input_3_class,input_3_attributes,'', ...
input_3_name,input_3_position);
disp('All the three input are OK')
The following script can be used to test the implementation of the validation procedure.
It generate the three input required and, randomly, it makes them not valid.
%
% Generate the first input
%
n_rows=randi([2 3],1);
n_cols=2;
input_1=randi([20 30],n_rows,n_cols);
%
% Generate the second input
%
if(rand > 0.5)
input_2='This is a string';
else
input_2='';
end
%
% Generate the third input
%
input_3=acos(rand(5,1)*1.2);
%
% Call the test_validateattributes function with the above generated input
%
input_1
input_2
input_3
%
test_validateattributes(input_1,input_2,input_3)
These are a couple of examples of wrong input detected by the validateattributes
function:
Wrong input
input_1 =
23 22
26 28
input_2 =
''
input_3 =
0.0000 + 0.4455i
1.2420 + 0.0000i
0.4063 + 0.0000i
1.3424 + 0.0000i
1.2186 + 0.0000i
Error using test_validateattributes (line 44)
Expected input number 1, array_1, to be of size 3x2 when it is actually
size 2x2.
Wrong input
input_1 =
22 24
21 25
26 27
input_2 =
This is a string
input_3 =
1.1371 + 0.0000i
0.6528 + 0.0000i
1.0479 + 0.0000i
0.0000 + 0.1435i
0.0316 + 0.0000i
Error using test_validateattributes (line 109)
Expected input number 3, array_3, to be real.
Valid input
input_1 =
20 25
25 28
24 23
input_2 =
This is a string
input_3 =
0.9696
1.5279
1.3581
0.5234
0.9665
All the three input are OK
# Remarks
This topic displays best practices that the community has learned over time.