Передача аргументов в функцию – фундаментальный механизм абстракции и повторного использования кода. От выбранной модели передачи (по значению, по ссылке, по указателю, копирование-вход/копирование-выход, по имени, по «совместному использованию объекта») зависят корректность алгоритма, вычислительные затраты, предсказуемость побочных эффектов и доказуемость инвариантов. В работе формализованы базовые понятия (формальные/фактические параметры, область видимости, время жизни, активационная запись), разобраны семантики и их связь с мутабельностью данных, даны строгие правила проектирования и типовые идиомы, показаны риски алиасинга и ловушки значений по умолчанию. Показано, как эти знания применяются в задачах ЕГЭ: трассировка, вычисление результатов, работа с массивами/строками/записями, оценка стоимости копирования и преобразование решений между разными семантиками. Завершает материал практикум из пяти упражнений формата ЕГЭ.
Функция (процедура) – именованный фрагмент программы, принимающий формальные параметры и (в случае функции) возвращающий значение.
Фактические параметры (аргументы вызова) – выражения, переданные при вызове.
Связывание (binding) – сопоставление фактических параметров формальным: по позиции и/или по имени (ключевые аргументы).
Активационная запись (кадр стека) – структура, создаваемая при вызове: хранит формальные параметры, локальные переменные, адрес возврата, сохранённые регистры и т. п.
Область видимости – множество участков кода, где имя параметра доступно (обычно статическая/лексическая).
Время жизни – интервал, когда объект существует в памяти (локальные – до возврата; динамические – до явного освобождения/сборки мусора).
Ключевая идея. Передача – это не «копировать или не копировать» в отрыве от контекста. Важны (а) семантика языка, (б) тип и мутабельность объекта, (в) договорённость о направлении передачи: in (только чтение), out (только запись), inout (чтение+запись).
По значению (call by value)
Фактическое выражение вычисляется, результат копируется в формальный параметр. Изменения формального параметра не влияют на аргумент вызова.
Плюсы: изоляция, предсказуемость, простое доказательство корректности.
Минусы: стоимость копирования; не подходит для намеренной модификации входа.
Стоимость: Θ(размера данных) (скаляры – O(1), большие структуры – O(n)).
По ссылке (call by reference)
Формальный параметр становится синонимом (алиасом) аргумента. Изменения видны вызывающей стороне.
Плюсы: отсутствие копирования; возможность out/inout.
Минусы: риск неочевидных побочных эффектов, сложнее инварианты; опасность «висячих» ссылок при выходе.
Требование: задавайте контракт (пред-/постусловия), отмечайте «только чтение» (константная ссылка) при отсутствии намерения модификации.
По указателю (pointer semantics)
Передаётся значение указателя (адрес). Семантически близко к передаче по значению указателя, что даёт опосредованный доступ к объекту.
Плюсы: явная управляемость адресами, совместима с «нет ссылок в языке».
Минусы: нулевые/невалидные указатели, двойные разыменования, необходимость дисциплины владения.
Копирование-вход/копирование-выход (copy-in/copy-out, value-result)
На вход – копия, на выход – синхронизация изменённой копии обратно в аргумент (если он адресуем).
Плюсы: упрощает reasoning внутри функции (как по значению), но сохраняет эффект изменения к моменту возврата.
Минусы: два копирования; неоднозначность при алиасинге нескольких фактических параметров на один объект.
По имени (call by name, историческая)
Текстуальная подстановка «параметр как макро-выражение» (ALGOL 60). Вычисление откладывается до момента использования.
Плюсы: выражает ленивость.
Минусы: трудно предсказывать число вычислений и побочные эффекты; сегодня почти не используется в прикладных ЯП, важно как теория.
По «совместному использованию объекта» (call by sharing)
Формальный параметр получает ссылку на один и тот же объект, но связь с именем аргумента не передаётся (нельзя «перепривязать» исходную переменную). Типична для Python/Java (для ссылочных типов), JavaScript, многих функциональных ЯП с мутабельными структурами.
Следствие: изменение внутреннего состояния мутабельного объекта видно снаружи; попытка «переприсвоить параметр» не влияет на переменную вызывающего.
Ключ к пониманию: различайте мутабельность объекта и перепривязку имени.
Мутабельность: объекты делятся на изменяемые (списки, словари, массивы) и неизменяемые (числа, строки в ряде ЯП, кортежи).
Алиасинг: два параметра (или параметр и глобальная переменная) указывают на один объект. Без должной осторожности приводит к неинтуитивному поведению.
Правило безопасности: если функция декларирована как in, внутри неё нельзя изменять состояние аргумента; для изменяемых типов пользуйтесь копиями/защитным клонированием.

При вызове:
Вычисляются фактические параметр(ы) в порядке, определённом языком (лево-направо, право-налево или не определён).
Производится связывание с формальными параметрами (позиционное и/или именное).
Создаётся активационная запись: места под формальные параметры, локальные переменные.
Выполняется тело; на return (или неявном завершении) возвращается управление и, при необходимости, значение/результаты out/inout.
Разрушается активационная запись (или снимается со стека).
Замечание о порядке вычисления аргументов. В ряде языков он не определён стандартом. Не полагайтесь на побочные эффекты в выражениях аргументов.
Контракты in/out/inout. На уровне спецификации фиксируйте направление передачи. Для in – гарантируйте отсутствие модификаций; для out/inout – опишите постусловия.
Константность интерфейса. Если изменения не требуются – используйте константные ссылки (или не мутабельные типы) даже при передаче «по ссылке» с целью экономии копирования.
Защитное копирование. При сомнениях в источнике аргумента (особенно если объект разделяется) делайте «копию для записи» (copy-on-write).
Запрет на «скрытые эффекты». Не модифицируйте аргументы без явного указания в спецификации; это критично для доказуемости и тестируемости.
Чёткая политика по умолчанию. Значения по умолчанию не должны быть мутабельными объектами (классическая ловушка Python: общая разделяемая структура между вызовами).
Чёткая стоимость. Для больших структур (массивы, строки) явно оценивайте цену копирования; передавайте по ссылке/указателю, если копия не нужна.
Избегайте алиасинга параметров. Если два формальных параметра могут ссылаться на один объект, сделайте копию или запретите такую ситуацию пред-/постусловиями.
Нулевая стоимость абстракции. Для «горячих» путей используйте перемещение/взятые по ссылке представления, чтобы вызов не увеличивал асимптотику.
swap
По значению – не обменяет исходные переменные, нужен возврат пары или ссылки/указатели.
По ссылке – честный обмен без возврата.
По sharing – переприсвоение параметра не влияет на аргумент; менять нужно внутреннее состояние объекта.
Агрегирующие «аккумуляторы»
Передавайте аккумулятор как inout (или возвращайте новое значение). Запрещено полагаться на побочный эффект «глобальной» переменной.
«Функция чистая снаружи – быстрая внутри»
Интерфейс in + локальная копия, если внутри нужно мутабельное представление (например, буферизованная обработка строки).
Позиционные и именованные аргументы
Для читабельности и устойчивости к рефакторингу предпочитайте именованные в вызовах с множеством однотипных параметров.
Ловушка «мутабельное по умолчанию»
# Неправильно:
def f(x, acc=[]): acc.append(x); return acc
# Правильно:
def f(x, acc=None):
if acc is None: acc = []
acc.append(x); return acc
На ЕГЭ регулярно встречаются задания, где требуется:
Трассировка вызовов и вычисление результата функции с параметрами (включая вложенные вызовы).
Понимание семантики изменения аргументов: вернётся ли обновлённый массив/строка? Нужно ли возвращать новое значение?
Оценка стоимости: сколько операций выполнится при передаче большого массива «по значению» (модельно) и как это влияет на асимптотику решения.
Преобразование решения: из «модифицируем аргумент» в «возвращаем новое значение» и обратно, с корректной работой со ссылками.
Работа с файлами/массивами: функции, принимающие пути/потоки/срезы, – аккуратная спецификация in/out.
Доказательство корректности: формулирование предусловий и постусловий на параметры (например, «массив отсортирован», «индексы корректны»).
По значению vs по ссылке
procedure inc_by_value(x): // x – копия
x := x + 1
procedure inc_by_ref(var x): // x – ссылка на аргумент
x := x + 1
a := 5
inc_by_value(a) // a = 5
inc_by_ref(a) // a = 6
По sharing (мутабельный объект)
procedure add_front(L, item): // L – ссылка на тот же список (sharing)
L.prepend(item) // изменяется исходный список
procedure rebind(L, new_list): // перепривязка формального параметра
L := new_list // не влияет на переменную вызывающего
Копирование-вход/копирование-выход
procedure normalize(value-result V):
V := V / max(1, abs(V)) // внутренняя работа с копией
// По возврате V синхронизируется назад
Контракты
procedure sum_prefix(const A[0..n-1], in k) returns s
require 0 <= k <= n
ensure s = Σ_{i=0}^{k-1} A[i]
Время вызова: создание кадра стека O(1); стоимость передачи = O(1) для скаляров/ссылок, O(n) для больших структур при копировании.
Память: параметры по значению занимают место под копии; по ссылке – под указатели/ссылки; рекурсия – глубина стека O(depth).
Оптимизации: перемещение/заимствование (move), «нулевые копии», sso/rope для строк – теоретически снижают накладные расходы, но на экзамене опирайтесь на абстрактную модель стоимости копирования.
Ожидание, что по значению изменится аргумент вызова. Лечение: возвращайте новое значение или используйте inout.
Неосознанная модификация общего объекта при sharing. Делайте защитную копию, если нужен локальный эффект.
Мутабельный параметр по умолчанию. Применяйте None/специальный маркер и создавайте объект внутри.
Алиасинг двух параметров на один объект. Фиксируйте запрет или клонируйте.
Побочные эффекты в выражениях аргументов при неопределённом порядке вычисления. Выносите эффекты до вызова.
Смешение уровней абстракции: «функция чистая» по контракту, но меняет глобальное состояние/аргумент.
Упражнение 1. Трассировка по значению и по ссылке
Условие. Дан псевдокод:
procedure F_by_val(x, y):
x := x + y
y := x - y
procedure F_by_ref(var x, var y):
x := x + y
y := x - y
a := 2; b := 5
F_by_val(a, b)
F_by_ref(a, b)
print a, b
Определите вывод.
Критерии. Корректное различение копий и ссылок; аккуратная трассировка.
Подсказки. После F_by_val a,b не меняются; после F_by_ref меняются оба.
Упражнение 2. Мутабельность и sharing
Условие.
procedure push_front(L, v): // sharing
L.prepend(v)
procedure assign_new(L): // попытка перепривязки
L := [0]
S := [1,2,3]
push_front(S, -1)
assign_new(S)
print S
Что будет напечатано? Объясните.
Критерии. Понимание, что модификация внутреннего состояния списка видна, а перепривязка формального параметра – нет.
Подсказки. Ответ должен содержать список из 4 элементов, начинающийся с -1.
Упражнение 3. Ловушка значения по умолчанию
Условие.
function collect(x, acc = []):
acc.append(x)
return acc
print collect(1)
print collect(2)
Упражнение 4. Стоимость копирования
Условие. Функция H(A) по значению принимает массив из n целых и выполняет k проходов линейной обработки (без дополнительных копий). Оцените суммарное время в терминах n и k, если копирование массива на входе стоит cn, а один линейный проход – dn.
Критерии. Корректная сумма: cn + k·dn; вывод о преимуществах передачи по ссылке при больших n.
Подсказки. Учтите только одно копирование на входе (функция не копирует далее).
Упражнение 5. Контракты in/out/inout
Условие. Спроектируйте спецификацию процедуры normalize_inplace(var A), которая масштабирует все элементы массива так, чтобы максимальный по модулю стал равен 1, и возвращает число изменений. Укажите:
(a) предусловия; (b) постусловия; (c) направление параметров; (d) поведение на пустом массиве.
Критерии. Ясное разделение in (ничего), out (счётчик изменений), inout (массив); корректная обработка нулевого максимума (max=0).
Подсказки. При max=0 массив оставляем без изменений; возвращаем 0.
Передача аргументов – не техническая деталь, а часть контракта алгоритма, определяющая наблюдаемое поведение, стоимость и доказуемость. Осознанный выбор семантики (значение/ссылка/указатель/sharing), дисциплина мутабельности, формулирование направлений in/out/inout и аккуратная работа с алиасингом обеспечивают корректность и эффективность программ. В логике ЕГЭ это выражается в умении трассировать код, предсказывать побочные эффекты, оценивать стоимость копирования и преобразовывать решения между различными моделями передачи. Освоив предложенные правила и отработав упражнения, выпускник получает устойчивые навыки проектирования интерфейсов функций и уверенно решает задачи повышенной сложности.