Control Flow

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

Written against MATLAB R2026b. The sessions below were run in GNU Octave 11.3.0 (MATLAB code that Octave also accepts) and show Octave’s output, whose layout may differ from MATLAB’s. Everything on this page has been in MATLAB for decades (the documentation pages give "Introduced before R2006a"), so no release is singled out. Statements that Octave does not share, such as the exact text of MATLAB error messages, are marked "not run here".

Control flow decides which statements run and how often. MATLAB has the usual four constructs: if for a choice between blocks, switch for a choice among values, for for a known sequence of iterations and while for repetition until a condition fails. break, continue and return change the course from inside a block, and try and catch handle errors. All of them end with end. Two things differ from many languages and explain most surprises: the condition of if and while can be an array (see Logical Type and Conditions), and for iterates over the columns of its argument.

The page closes with the heatflow time-stepping loop, the one place in this section where a loop is the right tool because each step needs the result of the previous one. The stencil it uses is written with the range indexing of Indexing; the scenario is introduced on the section home page.

if, elseif and else

if expression
  statements
elseif expression
  statements
else
  statements
end

An expression is true when its result is non-empty and every element is nonzero. The elseif and else blocks are optional, and an if can have several elseif blocks. Only the first block whose expression is true runs. The documentation of if states the rule for arrays: an expression such as A > 0 is true only when every element of the result is nonzero, so test with any or all (or isequal for whole-array equality) when you mean one of those.

Combine scalar conditions with && and ||. They short-circuit, which is what makes if exist("x", "var") && x > 0 safe. The documentation adds that inside the condition of an if (or while) the operators & and | also short-circuit, but it recommends && and || in conditions anyway; see Operators and Precedence.

T = 350;
if T > 400
  disp("hot")
elseif T > 300
  disp("warm")
else
  disp("cool")
end

Octave 11.3.0 output: warm.

switch, case and otherwise

switch switch_expression
  case case_expression
    statements
  case case_expression
    statements
  otherwise
    statements
end

switch tests the cases in order and runs the first one that matches, then leaves the block. A case matches when:

  • for numbers, case_expression == switch_expression;

  • for character vectors and strings, strcmp(case_expression, switch_expression) is true, so the comparison is case sensitive;

  • for a cell array, at least one element matches, which is how several values share one block;

  • for objects that support eq, the result of eq is true.

The switch expression must be a scalar or a character vector, and a case expression a scalar, a character vector or a cell array of scalars or character vectors. otherwise is optional and runs only when no case matched.

kind = "Neumann";
switch kind
  case "Dirichlet"
    disp("fixed value")
  case {"Neumann", "Periodic"}
    disp("zero flux or wrap")
  otherwise
    disp("unknown")
end
n = 2;
switch n
  case 1
    disp("one")
  case {2, 3}
    disp("two or three")
end
switch "dirichlet"
  case "Dirichlet"
    disp("matched")
  otherwise
    disp("case matters")
end

Octave 11.3.0 output:

zero flux or wrap
two or three
case matters

There is no fall-through. Unlike C, only the first matching case runs and then the switch ends, so no break is needed and none is wanted: the documentation points out that break leaves the enclosing for or while loop, not the switch. Because exactly one case runs, a variable defined in one case is not available in another; define whatever a case needs inside it. The documentation also advises against relational operators in a case expression (case x < 5 does not do what it appears to); test inequalities with if, elseif and else.

The switch is the natural way to branch on the heatflow boundary kind: the Dirichlet, Neumann and Periodic members of +heatflow.BoundaryKind (an enumeration, introduced on Enumerations) each get a case, and otherwise raises an error for an unknown kind.

for loops

for index = values
  statements
end

values has one of three forms, according to the documentation:

  • initVal:endVal increments the index by 1 until it is greater than endVal;

  • initVal:step:endVal increments by step, or decrements when step is negative;

  • valArray makes index equal to a column of valArray on each iteration, valArray(:,1) on the first, for as many iterations as there are columns. valArray can be of any data type, including a character vector, a cell array or a struct.

So the loop goes over columns. For a row vector, which is the usual range, every column is one element and the loop looks like a loop over elements. For a matrix, the loop variable is a whole column. For a cell array the loop variable is a cell (a 1-by-1 cell array holding the contents, not the contents themselves), so you write c = e{1} to open it (see Cell Arrays). To iterate over the elements of a column vector, transpose it first, as the documentation advises.

for k = 1:3
  fprintf("%d ", k);
end
fprintf("\n");
for v = [1 2; 3 4]
  disp(v')
end
for e = {1, "ab", [1 2 3]}
  disp(class(e))
end
count = 0;
for k = 1:0
  count = count + 1;
end
disp(count)
for t = 1:-0.25:0
  fprintf("%.2f ", t);
end
fprintf("\n");

Octave 11.3.0 output:

1 2 3
   1   3
   2   4
cell
cell
cell
0
1.00 0.75 0.50 0.25 0.00

The second loop shows the columns [1;3] and [2;4] (printed transposed). The cell loop shows that e is a cell on every iteration. The empty range 1:0 runs zero times, which is why for k = 1:numel(v) is safe for an empty v, and numel is the safer length for a vector.

The loop variable. Do not assign to it. The documentation states that for overrides any change made to the index inside the loop; the next iteration takes the next column regardless, and the assignment only affects the rest of that iteration.

for x = [10 20 30]
  x = x + 1;
  fprintf("%d ", x);
end
fprintf("\n");

Octave 11.3.0 output: 11 21 31. Each pass started from the original value, so the assignments had no effect on the sequence.

Three more rules about ranges:

  • Floating-point steps. A step such as 0.1 is not exactly representable, so the values of a range are rounded, and with other bounds the last element can be missing or present by a hair. When the number of iterations matters, loop over an integer count and compute the value (t = (k-1)*dt), which also avoids accumulated rounding. This is exactly what the time-stepping loop below does, with a counter and a computed time.

  • i and j. They are functions that return the imaginary unit. A loop for i = 1:n creates a variable that shadows the function, so a later 3 + 2*i in the same workspace uses the last loop value and silently gives a real number. A literal such as 2i or 1i is part of the number syntax and always means the imaginary unit, whatever variables exist. Use names such as row, col or k for counters, and write the imaginary unit as 1i.

  • Growing an array in a loop (result(end+1) = ...) reallocates it each time; preallocate with zeros, as described on Vectorization and Preallocation.

The parfor loop runs iterations in parallel on workers; running them in parallel needs the Parallel Computing Toolbox (see Toolboxes and Add-On Products), and the statement is not used in this section.

while, break, continue and return

while expression, statements, end repeats the statements for as long as the expression is true, with the same rule as if: non-empty and every element nonzero. Test the condition before every pass, so the body may never run.

  • break leaves the innermost enclosing for or while loop at once. It does not leave a switch, and in nested loops it leaves only the inner loop.

  • continue skips the rest of the current pass and begins the next one (for a while, the condition is tested again).

  • return is stronger: it ends the whole script or function and returns control to the caller, or to the command prompt if the script was called directly. The documentation warns to be careful when using it inside a conditional block or a loop, because it does not just exit the loop. See Functions.

n = 0;
while true
  n = n + 1;
  if mod(n,2) == 0, continue, end
  if n > 7, break, end
  fprintf("%d ", n);
end
fprintf("\n");

Octave 11.3.0 output: 1 3 5 7. Even values were skipped by continue, and the loop stopped when n reached 9. A while true loop with a break is the standard form when the exit test sits in the middle of the body; if you start a loop that never ends by mistake, stop it with Ctrl+C.

try and catch in brief

try
  statements
catch exception
  statements
end

If any statement in the try block raises an error, control goes immediately to the catch block, where the optional exception variable holds an MException object with fields such as identifier and message. The two blocks can contain nested try statements. A one-line form is common in short sessions.

try, error("heatflow:badStep", "dt %g too large", 0.5), catch err, disp(err.identifier), disp(err.message), end

Octave 11.3.0 output:

heatflow:badStep
dt 0.5 too large

Catch the specific errors you can handle and rethrow the rest; an empty catch that ignores everything hides real bugs. Identifiers, rethrow, MException, warning and onCleanup are on Errors, Warnings and Exceptions.

Loops versus vectorization

A loop over array elements is correct but often slower and longer than the equivalent array expression, because MATLAB is designed to apply operators and functions to whole arrays. The rule of thumb in this section: vectorize operations that are independent from element to element (sums, masks, the five-point stencil on the grid), and use a loop for work that is sequential, where each pass needs the previous one, as in time stepping. Preallocate whatever the loop fills. The trade-offs, with timings, are on Vectorization and Preallocation and Profiling and Timing; measure before rewriting working code.

The heatflow time-stepping loop

Explicit time stepping of the heat equation updates the interior of the plate from the previous temperature field with the five-point stencil, and repeats until the largest change in a step falls below a tolerance. The scheme is stable only if dt <= dx^2/(4*alpha), so the script checks that before it starts. The loop has two exits, convergence and a step limit, so it is a while loop with a compound condition; after it, an if tells the caller which exit was taken.

flowchart TD A["Set alpha, dx, dt, tolerance and maximum steps"] --> B{"dt within the stability limit?"} B -->|no| X["Stop with an error"] B -->|yes| C["Initialize T and set delta to Inf"] C --> D{"delta above tolerance and step below maximum?"} D -->|no| H{"delta above tolerance?"} D -->|yes| E["Increment step, compute the stencil update Tn from T"] E --> F["delta is the largest absolute change, T becomes Tn"] F --> D H -->|yes| I["Warn: did not converge"] H -->|no| J["Report converged after step steps"]

The code below is a complete script. It uses a 21-by-21 grid on a unit plate, a diffusivity of 1e-4, a time step of 80% of the stability limit and a left edge held at 100 degrees. The updates use whole-array indexing for the stencil, and the loop is over time. The grid and the values are illustrative; the final temperature field belongs to the example, not to a physical plate.

alpha = 1e-4;
n = 21;
dx = 1/(n-1);
dt = 0.2*dx^2/alpha;
assert(dt <= dx^2/(4*alpha))

T = zeros(n,n);
T(:,1) = 100;

tol = 1e-6;
maxSteps = 20000;
step = 0;
delta = Inf;
while delta > tol && step < maxSteps
  step = step + 1;
  Tn = T;
  Tn(2:end-1,2:end-1) = T(2:end-1,2:end-1) + alpha*dt/dx^2 * ...
    (T(1:end-2,2:end-1) + T(3:end,2:end-1) ...
     + T(2:end-1,1:end-2) + T(2:end-1,3:end) ...
     - 4*T(2:end-1,2:end-1));
  delta = max(abs(Tn(:) - T(:)));
  T = Tn;
end

if delta > tol
  warning("heatflow:notConverged", "no convergence in %d steps", maxSteps);
else
  fprintf("converged after %d steps, delta %.2e\n", step, delta);
end
fprintf("T center %.4f\n", T(11,11));

Octave 11.3.0 output:

converged after 1304 steps, delta 9.96e-07
T center 24.9999

Points to notice. delta starts at Inf so that the first test passes. The condition uses && on two scalars. A new array Tn is built from the old T, and T is replaced only at the end of the pass, which is what an explicit scheme needs; updating T in place would mix old and new values. The assertion stops the run with an error if the step is unstable; a failed assert without a message argument raises the error identifier MATLAB:assertion:failed (not run here). Pass an identifier and message, assert(dt <= dx^2/(4*alpha), "heatflow:unstableStep", "dt %g exceeds the stability limit", dt), to give the caller something more specific to catch. The other three edges stay at zero because the update touches only the interior and the left edge was set once, which is a fixed-value (Dirichlet) boundary. Other boundary kinds would be a switch over the boundary kind, as shown above. The 1304-step count and the centre value are the numbers Octave printed for this grid and tolerance; MATLAB follows the same IEEE 754 arithmetic, but treat them as an example output, not a reference value.

Pitfalls

  • if on an array is true only if all elements are nonzero and the array is not empty. Use any, all, isempty or isequal to say what you mean.

  • Text in switch is compared with strcmp, so it is case sensitive. Normalize with lower first if the input is not controlled.

  • Expecting fall-through (or a break in each case): there is none, and break in a case leaves the enclosing loop, not the switch.

  • Relational operators in a case: use if, elseif, else.

  • Assigning to the loop variable does not change the sequence.

  • Looping over a cell array gives a cell, not its contents.

  • Reading past the end of an array in a loop (x(k) with k > numel(x), often from a hard-coded bound) is an error with the identifier MATLAB:badsubscript (not run here). Use numel(x) or size(x,dim) as the bound.

  • Looping over the rows of a matrix with for r = A gives columns. Loop over 1:size(A,1) and index A(r,:), or transpose.

  • Shadowing i and j.

  • Floating-point loop counters: loop over integers and compute the value.

  • A convergence loop without a step limit may never end. Always combine the tolerance with a maximum number of steps, and report which exit was taken.

  • return inside a loop ends the function or script, not just the loop.

  • A comparison of floating-point values with == in a condition: use a tolerance (see Logical Type and Conditions).

Where older books differ

The if, switch, for, while, break and continue statements are unchanged in the books of the older generation, and what they teach about them is still correct. What has changed is the style around them: the books show element-by-element loops where current code often uses an array expression or a function such as sum, cumsum or max, and they use i and j freely as loop counters. They use character vectors in switch and case (switch lower(kind)), which still works, and strings in double quotes are matched the same way, with strcmp. They rarely mention that a switch accepts a cell array of values in one case. Older code may read the last error with lasterr instead of catching an MException object, which is the current form (see Errors, Warnings and Exceptions).