Функции и передача параметров по ссылке var

Урок учит писать функции, возвращающие значение, и объясняет разницу между передачей параметров по значению и по ссылке var.

Функция — это подпрограмма, которая вычисляет и возвращает значение, которое можно использовать в выражениях.

Чем функция отличается от процедуры

Процедура выполняет действие (печатает, рисует, меняет данные). А функция вычисляет и возвращает результат — число, строку, логическое значение, — который можно подставить в выражение, присвоить переменной или вывести. Вы уже пользовались встроенными функциями: sqrt(9) возвращает 3, length(s) возвращает длину. Теперь научимся писать свои.

Простое правило: если подпрограмма должна что-то вернуть для дальнейших вычислений — это функция. Если она просто что-то делает — это процедура.

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

Функцию описывают словом function, и в её заголовке после параметров через двоеточие указывают тип возвращаемого значения. Результат внутри функции присваивают специальному имени Result (или, в классическом стиле, имени самой функции):

function Kvadrat(x: integer): integer;   // вернёт integer
begin
  Result := x * x;     // присваиваем результат
end;

begin
  writeln(Kvadrat(5));           // 25
  writeln(Kvadrat(3) + Kvadrat(4));  // 9 + 16 = 25
end.

Заголовок function Kvadrat(x: integer): integer читается так: «функция Kvadrat принимает целое x и возвращает целое». Строка Result := x * x задаёт, что вернуть. Главное преимущество видно во второй строке: вызов функции можно подставлять прямо в выражение, складывать, сравнивать — как обычное значение. В классическом Паскале вместо Result пишут имя функции: Kvadrat := x * x; оба варианта работают в PascalABC.NET. Запустите аналог на Python (там результат возвращают через return):

def kvadrat(x):
    return x * x

print(kvadrat(5))
print(kvadrat(3) + kvadrat(4))

Вывод:

25
25

Функция с логическим результатом

Очень полезны функции, возвращающие boolean — они отвечают на вопрос «да или нет» и красиво читаются в условиях:

def chetnoe(n):
    return n % 2 == 0

for x in [4, 7, 10]:
    if chetnoe(x):
        print(x, '- чётное')
    else:
        print(x, '- нечётное')

Вывод:

4 - чётное
7 - нечётное
10 - чётное

На Паскале это была бы function Chetnoe(n: integer): boolean; begin Result := n mod 2 = 0; end;, а в условии — if Chetnoe(x) then .... Такой код читается почти как человеческий язык.

Параметры по значению: копия данных

До сих пор все параметры мы передавали по значению: функция получает копию данных. Что бы она ни делала с параметром внутри, на исходную переменную это не влияет — она работает с копией. Это безопасно и используется по умолчанию:

def isportit(x):
    x = 999          # меняем копию
    print('Внутри:', x)

a = 10
isportit(a)
print('Снаружи a:', a)   # a не изменилась

Вывод:

Внутри: 999
Снаружи a: 10

Видите? Внутри параметр стал 999, но снаружи a осталась 10 — менялась копия. В большинстве случаев это именно то, что нужно.

Параметры по ссылке: ключевое слово var

Но иногда нужно, чтобы подпрограмма изменила исходную переменную. Например, процедура обмена должна реально поменять значения у вызывающей стороны. Для этого параметр объявляют с ключевым словом var — тогда он передаётся по ссылке: подпрограмма работает не с копией, а с самой исходной переменной.

procedure Obmen(var a, b: integer);   // var — по ссылке!
var
  temp: integer;
begin
  temp := a;
  a := b;
  b := temp;
end;

var
  x, y: integer;
begin
  x := 1;
  y := 2;
  Obmen(x, y);              // меняет сами x и y
  writeln('x = ', x, ', y = ', y);   // x = 2, y = 1
end.

Без var процедура поменяла бы местами лишь свои копии, и снаружи x и y остались бы прежними. Слово var перед параметром — это «работай с оригиналом, а не с копией». Так же через var возвращают несколько результатов сразу (функция возвращает только одно значение, а процедура с несколькими var-параметрами — сколько угодно). Различие наглядно:

ПризнакПо значениюПо ссылке (var)
Что получает подпрограммакопию данныхсаму переменную
Изменения видны снаружи?нетда
Когда использоватьобычные вычислениякогда нужно изменить переменную

Попробуй сам

Напишите функцию Maximum(a, b), которая возвращает большее из двух чисел, и используйте её, чтобы найти максимум из трёх: Maximum(Maximum(x, y), z). Проверьте на Python:

def maximum(a, b):
    if a > b:
        return a
    else:
        return b

x, y, z = 7, 3, 9
print('Максимум из трёх:', maximum(maximum(x, y), z))

Вывод:

Максимум из трёх: 9

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

  • Забыли задать Result. Если внутри функции не присвоить Result (или имя функции), она вернёт мусор. Каждая ветка должна задавать результат.
  • Не указали тип возврата. В заголовке функции после параметров обязателен : тип, иначе это будет процедура.
  • Ждут изменения переменной без var. Без var подпрограмма меняет лишь копию; чтобы изменить оригинал, параметр объявляют с var.
  • Передают выражение в var-параметр. По ссылке (var) можно передавать только переменную, а не выражение вроде Obmen(x + 1, y) — оригинала у выражения нет.

Итоги

  • Функция вычисляет и возвращает значение; в заголовке указывают тип возврата, результат задают через Result или имя функции.
  • Вызов функции можно подставлять в выражения, как обычное значение; функции с boolean удобны в условиях.
  • По умолчанию параметры передаются по значению — подпрограмма работает с копией, оригинал не меняется.
  • Параметр с ключевым словом var передаётся по ссылке: изменения видны снаружи.
  • Через var-параметры процедура может «вернуть» сразу несколько значений.
Проверьте себя
1. Чем функция принципиально отличается от процедуры?
AФункция работает быстрее
BФункция возвращает значение, которое можно использовать в выражении
CФункция не может иметь параметров
DФункция описывается после begin
2. Что делает ключевое слово var перед параметром?
AДелает параметр необязательным
BПередаёт параметр по ссылке, чтобы изменения были видны снаружи
CОбъявляет локальную переменную
DУскоряет вызов подпрограммы
3. Что произойдёт с переменной a после вызова процедуры, если параметр передан по значению (без var) и внутри ему присвоили 999?
Aa станет равной 999
Ba останется прежней, ведь менялась копия
CПрограмма выдаст ошибку
Da обнулится
Поддержать проект