Функции: файлы-функции и анонимные @

Как оформить переиспользуемый код: от файла-функции с несколькими выходами до однострочной анонимной функции.

Функция в MATLAB имеет собственную область видимости и может возвращать несколько значений сразу.

Объявление функции

Функция начинается со слова function, перечня выходов в квадратных скобках, имени и входов. В отличие от скрипта, переменные функции локальны — они не попадают в рабочую область. Несколько выходов — характерная черта MATLAB: вы видели это у max и size.

function [s, p] = sum_prod(a, b)
    s = a + b;        % первый выход
    p = a * b;        % второй выход
end

% вызов:
[su, pr] = sum_prod(3, 4);   % su = 7, pr = 12

Файл-функция традиционно сохраняется как sum_prod.m — имя файла должно совпадать с именем функции. С версии R2016b функции можно писать и в конце скрипта.

Анонимные функции через @

Для коротких выражений не нужен отдельный файл — есть анонимные функции. Символ @ создаёт функцию «на лету»: @(x) x.^2 + 1. Их хранят в переменных и передают другим функциям. Анонимная функция «захватывает» значения переменных в момент создания.

f = @(x) x.^2 + 1;
f(3)              % 10
g = @(x, y) x + y;
g(2, 5)           % 7

Вывод:

ans =
    10

ans =
     7

Функции как аргументы

Анонимные функции — основной способ передать «что вычислять» в численные методы. Когда вы решаете уравнение или интегрируете, вы передаёте саму функцию через @. Это пригодится в разделе про численные методы, где ode45 и integral принимают функцию-аргумент.

% площадь под x^2 от 0 до 1 будет считаться так:
fun = @(x) x.^2;
% integral(fun, 0, 1)  →  около 0.3333

Как работает под капотом

Анонимная функция при создании делает снимок переменных, которые в ней упомянуты, но не являются аргументами. Если потом изменить исходную переменную, функция всё равно будет использовать старое значение. Это удивляет тех, кто ждёт «живой» ссылки. Например, если задать a=2; h=@(x) a*x;, а затем a=10, вызов h(3) всё равно даст 6, а не 30 — внутри функции a «заморожено» как 2.

Частые ошибки

  • Назвать файл иначе, чем функцию, — MATLAB вызовет функцию по имени файла, а не по объявлению.
  • Забыть точку в x.^2 внутри анонимной функции — тогда она не сработает для векторного аргумента.
  • Ждать, что анонимная функция увидит более позднее изменение захваченной переменной.

Локальная область видимости как защита

Главное преимущество функции перед скриптом — изоляция. Переменные внутри функции существуют только на время её работы и не «протекают» в рабочую область. Это кажется ограничением, но на деле спасает от целого класса ошибок: в большом скрипте переменные легко перезаписать по неосторожности, тогда как функция гарантирует, что её внутренняя кухня никого не затронет. Данные входят через аргументы, выходят через возвращаемые значения — и больше никак. Эта дисциплина делает код предсказуемым: чтобы понять функцию, достаточно посмотреть на её вход и выход, не держа в голове всё состояние программы.

Функции высшего порядка

Поскольку функцию можно передать в другую функцию через @ (создав дескриптор функции, function handle), MATLAB поддерживает функции высшего порядка. Численные методы этим и живут: integral(@f, a, b), fzero(@f, x0), ode45(@f, ...) — все принимают вычисляемую функцию как аргумент. Дескриптор именованной функции пишут как @myfunc, а короткое выражение — анонимной функцией @(x) .... Этот механизм превращает «что вычислять» в обычное передаваемое значение и лежит в основе всего раздела численных методов. Без него пришлось бы зашивать формулу внутрь каждого решателя; с ним один универсальный решатель работает с любой вашей функцией.

Итоги

  • function [out1,out2] = name(in) — несколько выходов, локальная область.
  • @(x) выражение — анонимная функция для коротких формул.
  • Анонимная функция замораживает захваченные переменные в момент создания.
Проверьте себя
1. Как объявить функцию с двумя выходами?
Afunction s, p = f(a,b)
Bfunction [s, p] = f(a, b)
Cdef f(a,b) -> s,p
Dfunction f(a,b) return s,p
2. Что создаёт запись @(x) x.^2?
AКомментарий
BАнонимную функцию, возводящую аргумент в квадрат
CСсылку на файл
DМатрицу
3. Что произойдёт с захваченной переменной в анонимной функции, если её потом изменить?
AФункция увидит новое значение
BФункция использует значение, замороженное при создании
CВозникнет ошибка
DФункция удалится