Параметры по умолчанию и именованные аргументы

Разбираемся, как задать параметру значение «по умолчанию» и как вызывать процедуры, называя аргументы по имени.

Параметр по умолчанию — значение, которое подставляется автоматически, если при вызове процедуры аргумент не передан. Именованный аргумент — способ вызова, при котором значение указывается вместе с именем параметра, а не только по позиции.

Зачем это нужно

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

Параметры по умолчанию решают это: можно указать «типичное» значение прямо в объявлении процедуры, и тогда при вызове его можно не передавать — Nim подставит его сам. А именованные аргументы решают вторую проблему — путаницу с порядком: вместо того чтобы запоминать «какой аргумент по счёту что означает», можно писать явно, каким параметрам какие значения соответствуют.

Синтаксис параметров по умолчанию

proc createProfile(name: string, age: int, country: string = "Россия") =
  echo name & ", " & $age & " лет, " & country

createProfile("Иван", 20)
createProfile("Мария", 25, "Казахстан")

Вывод:

Иван, 20 лет, Россия
Мария, 25 лет, Казахстан

Построчный разбор

  • country: string = "Россия" — это и есть параметр по умолчанию. После указания типа через = задаётся значение, которое будет использовано, если аргумент не передан явно.
  • При вызове createProfile("Иван", 20) мы передали только два аргумента — для name и age. Параметр country Nim заполнил сам значением "Россия".
  • При вызове createProfile("Мария", 25, "Казахстан") мы явно передали третий аргумент, и он переопределил значение по умолчанию — вместо «Россия» подставилась «Казахстан».
  • Важное правило: параметры со значением по умолчанию обычно ставят после обязательных параметров без значения по умолчанию. Так компилятору проще понять, какие аргументы обязательны, а какие можно опустить.

Синтаксис именованных аргументов

Теперь возьмём процедуру с несколькими параметрами, где легко перепутать порядок:

proc rectangle(width: float, height: float, color: string = "чёрный") =
  echo "Прямоугольник " & $width & "x" & $height & ", цвет: " & color

rectangle(width = 10.0, height = 5.0)
rectangle(height = 3.0, width = 8.0, color = "красный")

Вывод:

Прямоугольник 10.0x5.0, цвет: чёрный
Прямоугольник 8.0x3.0, цвет: красный

Обрати внимание на второй вызов: аргументы height и width переданы в порядке, обратном тому, в каком они объявлены в процедуре (сначала height, потом width). Это не ошибка — раз мы указываем имя параметра явно через =, Nim смотрит именно на имя, а не на позицию, и подставляет значения правильно.

Почему это удобно

Именованные аргументы особенно полезны, когда у процедуры много параметров одного типа. Например, если бы rectangle принимала два числа подряд без имён, легко перепутать местами ширину и высоту, ведь оба параметра — float, и такая ошибка не будет замечена компилятором (оба значения технически подходящего типа, просто смысл перепутан). Указав имя параметра при вызове, ты защищаешь себя от этой путаницы — код становится ещё и самодокументируемым: читая вызов, сразу видно, что есть что.

Мини-пример: комбинируем оба приёма

proc orderPizza(size: string, toppings: string = "сыр", spicy: bool = false) =
  var note = size & " пицца с " & toppings
  if spicy:
    note = note & " (острая)"
  echo note

orderPizza("большая")
orderPizza(size = "средняя", spicy = true)
orderPizza("маленькая", toppings = "грибы", spicy = true)

Вывод:

большая пицца с сыр
средняя пицца с сыр (острая)
маленькая пицца с грибы (острая)

Здесь видно, как приёмы работают вместе. Первый вызов передаёт только обязательный параметр size — остальные два взяли значения по умолчанию. Второй вызов пропускает toppings (он снова возьмёт значение "сыр" по умолчанию), но явно указывает spicy по имени, хотя тот стоит не сразу после size. Третий вызов смешивает позиционный аргумент ("маленькая" — первый по порядку, без имени) с двумя именованными.

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

  • Параметр без значения по умолчанию после параметра со значением. Запись вида proc f(a: int = 5, b: int) компилятор Nim пропустит с предупреждением о неочевидном порядке — по возможности всегда ставь обязательные параметры первыми, а параметры со значением по умолчанию — в конце списка.
  • Пропуск параметра «в середине» без указания имени. Нельзя вызвать rectangle(10.0, , "красный"), оставив пустое место для height — если хочешь пропустить параметр не в конце списка, для оставшихся аргументов после пропуска нужно использовать именованный синтаксис (как во втором примере выше).
  • Путают именованный аргумент с обычным присваиванием. Запись rectangle(width = 10.0, ...) внутри вызова — это не создание переменной width, а указание, какому параметру процедуры соответствует значение 10.0. Внутри списка аргументов при вызове = означает именно связь «параметр — значение».
  • Ожидают, что значение по умолчанию пересчитывается при каждом вызове по актуальным данным. Значение по умолчанию вычисляется исходя из выражения, заданного в объявлении процедуры, и не «следит» за внешними переменными автоматически — если нужно поведение, зависящее от контекста вызова, значение стоит передавать явно.

Итоги

  • Значение по умолчанию задаётся прямо в объявлении процедуры после типа параметра через = — и подставляется, если аргумент не передан.
  • Именованные аргументы позволяют указывать значения по имени параметра при вызове, независимо от порядка объявления.
  • Комбинация этих приёмов делает вызовы процедур короче, безопаснее и понятнее — особенно когда параметров много или они одного типа.
  • Параметры со значением по умолчанию обычно располагают после обязательных параметров в списке.
Проверьте себя
1. Что произойдёт при вызове createProfile("Иван", 20), если процедура объявлена как proc createProfile(name: string, age: int, country: string = "Россия")?
AБудет ошибка компиляции — не хватает аргумента
Bcountry автоматически получит значение "Россия"
Ccountry останется пустой строкой
DПроцедуру нельзя вызвать с двумя аргументами
2. Почему вызов rectangle(height = 3.0, width = 8.0, color = "красный") корректно работает, хотя порядок аргументов отличается от порядка параметров в объявлении?
ANim игнорирует порядок параметров для всех вызовов процедур
BИменованные аргументы связываются по имени параметра, а не по позиции
CЭто ошибка, которую компилятор просто не замечает
DПорядок совпадает, потому что параметры пересортировались автоматически при объявлении