JavaRush /Курсы /Swift SELF /Объявление функций: параметры и результат

Объявление функций: параметры и результат

Swift SELF
9 уровень , 1 лекция
Открыта

1. Зачем нужны функции

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

Можно представить функцию как «мини-машинку»: вы кладёте внутрь входные данные, машинка делает работу и либо возвращает результат, либо просто выполняет действие (например, печатает). Важная мысль: функция — это контракт. Она обещает: «если мне дать такие-то входные данные, я сделаю вот это».

Ниже — самый маленький пример функции-действия:

func sayHello() {
    print("Привет! Я функция 🙂")
}

sayHello() // Привет! Я функция 🙂

Обратите внимание: сначала мы объявили функцию (через func), а потом вызвали (через sayHello()).

2. Скелет функции: func, имя, параметры, тело

Когда люди видят объявление функции впервые, оно выглядит как «слишком много символов вокруг простого действия». Но на самом деле там всё логично: Swift заставляет нас явно описывать, что функция принимает и что возвращает. Это и есть контракт, который компилятор проверяет вместо вас (и это одна из причин, почему Swift так любит типы).

Сигнатура (то есть «шапка») функции обычно читается так:

func имя(параметры) -> типРезультата { тело }

Давайте визуализируем части функции в табличке — так проще запомнить:

Часть Пример Зачем нужна
Ключевое слово
func
Говорит Swift: «сейчас будет функция»
Имя
square
Чтобы вы могли вызывать функцию по имени
Параметры
(x: Int)
Входные данные функции
Результат
-> Int
Тип результата (что функция обещает вернуть)
Тело
{ ... }
Код, который выполняется при вызове

Самый минимальный пример «функция что-то вычисляет и возвращает»:

func square(x: Int) -> Int {
    return x * x
}

let v = square(x: 6)
print(v) // 36

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

3. Параметры и аргументы: не путать «внутри» и «снаружи»

Параметры и аргументы — слова похожие, из-за чего мозг пытается «сэкономить память» и считать их одним и тем же. Но разница простая и очень практичная: параметр живёт в объявлении функции, а аргумент — в месте вызова. То есть параметр — это как «слот», а аргумент — конкретное значение, которое вы в этот слот передаёте.

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

func greeting(name: String) -> String {
    return "Привет, \(name)!"
}

let text = greeting(name: "Аня")
print(text) // Привет, Аня!

В объявлении name — параметр. При вызове "Аня" — аргумент.

Функция может принимать несколько параметров. Тогда она становится похожей на «формулу» с несколькими входами:

func rectangleArea(width: Int, height: Int) -> Int {
    return width * height
}

let area = rectangleArea(width: 4, height: 7)
print(area) // 28

Заметьте, как приятно читать вызов: width: 4, height: 7. Даже если вы забудете порядок, код всё равно «сам себя объясняет».

4. Возвращаемое значение и return

Возвращаемое значение — это то, ради чего многие функции вообще пишутся. И тут важно понимать два правила. Первое: если в сигнатуре есть -> Type, то функция обязана вернуть значение этого типа. Второе: вернуть значение можно только через return.

Простейший пример: функция определяет знак числа и возвращает строку:

func signDescription(value: Int) -> String {
    if value > 0 {
        return "positive"
    } else if value < 0 {
        return "negative"
    } else {
        return "zero"
    }
}

print(signDescription(value: 10))  // positive
print(signDescription(value: 0))   // zero

Обратите внимание на дисциплину: каждая ветка if/else if/else возвращает String. Если хотя бы в одной ветке забыть return, компилятор скажет: «Ты обещал String, где он?»

Ещё важная деталь: return не только возвращает значение, но и сразу завершает выполнение функции. Всё, что написано после return в этой ветке, уже не выполнится.

5. Void: функция «делает», но ничего не возвращает

В реальных программах огромное количество функций ничего «полезного» не возвращают: они печатают, читают ввод, записывают в файл, показывают экран, отправляют запрос. На нашем текущем уровне самое понятное — печать: print() ведь тоже ничего не возвращает, он просто делает действие.

Если функция не возвращает результат, то говорят, что она возвращает Void. В Swift Void — это алиас для пустого tuple () (то есть «пустое значение»). Обычно -> Void в сигнатуре не пишут: отсутствие стрелки уже означает «ничего не возвращаю».

Пример функции, которая печатает заголовок:

func printHeader(title: String) {
    print("=== \(title) ===")
}

printHeader(title: "Моя программа") // === Моя программа ===

Иногда return встречается и в Void-функциях — но не чтобы «вернуть результат», а чтобы выйти раньше. Это похоже на «стоп-слово» внутри функции.

Пример:

func printIfPositive(value: Int) {
    if value <= 0 {
        return
    }
    print("Число положительное")
}

printIfPositive(value: -3) // (ничего не напечатает)
printIfPositive(value: 5)  // Число положительное

Это очень полезный приём: вместо глубоких вложенных if вы можете «раньше выйти» и оставить основной путь исполнения более прямым.

6. Контракт функции: return и неизменяемые параметры

Все ветки должны вернуть значение

Ошибки с return — одна из самых частых причин, почему новичок смотрит на компилятор и думает: «Он придирается». Но компилятор не придирается: он охраняет ваш контракт. Если функция обещает Int, то она должна вернуть Int в любом сценарии.

Например, вот так писать нельзя (показываю как антипример, ❌ не компилируется):

func badSign(value: Int) -> String {
    if value > 0 {
        return "positive"
    }
    // если value <= 0 — что вернуть? компилятор не даст собрать проект
}

Правильный вариант — всегда предусматривать «иначе»:

func goodSign(value: Int) -> String {
    if value > 0 {
        return "positive"
    } else {
        return "not positive"
    }
}

print(goodSign(value: 0)) // not positive

Да, иногда это выглядит «избыточно». Но зато любой, кто вызывает вашу функцию, уверен: результат будет всегда, и он нужного типа.

Параметры внутри функции — как let

Новички часто пытаются «переиспользовать параметр как рабочую переменную»: прибавить к нему что-то, обрезать строку, изменить скидку. И тут Swift говорит: «нельзя». Причина простая: параметр — это вход, его лучше воспринимать как константу. Функция получила значение — и работает с ним, не подменяя сам «вход».

Если вам нужно менять значение — делайте локальную копию через var. Это выглядит скучно, зато очень ясно.

Пример:

func withPrefix(text: String, prefix: String) -> String {
    var result = text
    result = prefix + result
    return result
}

print(withPrefix(text: "world", prefix: "hello ")) // hello world

Здесь text мы не трогаем. Мы создаём result и работаем с ним. Это дисциплина, которая позже спасает от неожиданных эффектов: когда входные данные «вдруг поменялись» и непонятно почему.

7. Пример: консольное приложение DiscountBuddy

Сейчас мы соберём маленькую программу, которую будем развивать в следующих лекциях дня. Идея простая: «калькулятор скидки». Пользователь вводит цену и процент скидки, а программа считает итоговую цену и печатает результат. Наша цель не в математике (там всё скучно), а в том, чтобы увидеть, как функции превращают простыню кода в аккуратные блоки.

Приветствие

Сначала сделаем функцию приветствия:

func printWelcome() {
    print("DiscountBuddy v0.1")
    print("Посчитаем цену со скидкой.")
}

printWelcome()
// DiscountBuddy v0.1
// Посчитаем цену со скидкой.

Ввод числа

Теперь вынесем ввод числа в отдельную функцию. Пока что будем максимально прямолинейны: если пользователь ввёл ерунду, получим 0. Это не идеальная валидация, но для текущего этапа курса — нормально:

func readInt(prompt: String) -> Int {
    print(prompt, terminator: " ")
    let line = readLine() ?? ""
    return Int(line) ?? 0
}

let x = readInt(prompt: "Введите число:")
print("Вы ввели \(x)")

Расчёт цены со скидкой

И наконец, функция расчёта цены со скидкой — она возвращает значение, а не печатает:

func discountedPrice(price: Int, discountPercent: Int) -> Int {
    let discount = price * discountPercent / 100
    return price - discount
}

let finalPrice = discountedPrice(price: 1000, discountPercent: 15)
print(finalPrice) // 850

Склеиваем сценарий в top-level код

Теперь соберём это в один цельный кусок top-level кода, который выглядит как «сценарий», а не «каша»:

func printWelcome() {
    print("DiscountBuddy v0.1")
}

func readInt(prompt: String) -> Int {
    print(prompt, terminator: " ")
    let line = readLine() ?? ""
    return Int(line) ?? 0
}

func discountedPrice(price: Int, discountPercent: Int) -> Int {
    let discount = price * discountPercent / 100
    return price - discount
}

printWelcome()

let price = readInt(prompt: "Введите цену (евро):")
let percent = readInt(prompt: "Введите скидку (%):")

let result = discountedPrice(price: price, discountPercent: percent)
print("Итоговая цена: \(result) евро")

Заметьте, что top-level часть теперь читается почти как обычный текст: «поприветствуй → спроси цену → спроси скидку → посчитай → выведи». Вот ради этого мы и любим функции: они превращают код в понятный сценарий.

Как думать о функциях: вход → функция → выход

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

Небольшая блок-схема помогает это закрепить:

flowchart LR
    A[Аргументы при вызове] --> B[Тело функции]
    B --> C[return значение]
    B --> D["побочный эффект: print()"]

В нашем DiscountBuddy это выглядит так: readInt(prompt:) принимает строку-подсказку, печатает её (побочный эффект), читает ввод, а потом возвращает Int. А discountedPrice(...) вообще ничего не печатает: просто возвращает число. Такое разделение — очень сильная привычка: «функции-вычислители» проще проверять глазами, и они обычно меньше ломаются.

8. Типичные ошибки

Ошибка №1: функция обещает вернуть значение, но return есть не во всех ветках.
Это классика: вы написали -> Int, сделали if value > 0 { return 1 }, а дальше забыли else. Компилятор в этом месте ваш лучший друг: он не даёт вам выпустить программу, где в одном сценарии «результат не определён». Лечится привычкой: если функция возвращает значение, мысленно спрашивайте «а что вернётся во всех остальных случаях?».

Ошибка №2: смешивание вычислений и вывода без необходимости.
Новичок часто делает так: функция и считает, и печатает. А потом оказывается, что вы хотите использовать результат в другом месте без печати — и приходится переписывать. На уровне этого курса очень полезно разделять: «посчитать и вернуть» — одна функция, «красиво показать пользователю» — другая функция или top-level код.

Ошибка №3: попытка изменить параметр напрямую.
Внутри функции параметр ведёт себя как константа, и Swift не даст написать что-то вроде value = value + 1. Это не «придирка языка», а защита от путаницы: вход должен оставаться входом. Если нужно «покрутить» значение, создайте локальную переменную var temp = value и меняйте её.

Ошибка №4: неправильные ожидания от Void-функций.
Иногда хочется сделать let x = printWelcome() и ждать, что x будет строкой или чем-то полезным. Но Void означает: функция ничего не возвращает, она только делает действие. Если вам нужно значение — значит, функция должна возвращать тип (например, String) и внутри иметь return.

Ошибка №5: слишком длинные функции «на все случаи жизни».
На радостях от функций легко сделать одну огромную process() на 200 строк, которая и читает, и проверяет, и считает, и печатает. Формально это функция, но по сути — та же простыня, только в фигурных скобках. Хорошее правило для старта: если вы не можете за 10 секунд объяснить, что делает функция, и её имя получается вроде doEverythingAndMore(), значит пора делить на более маленькие шаги.

1
Задача
Swift SELF, 9 уровень, 1 лекция
Недоступна
Квадрат награды
Квадрат награды
1
Задача
Swift SELF, 9 уровень, 1 лекция
Недоступна
Разделитель отчёта
Разделитель отчёта
1
Задача
Swift SELF, 9 уровень, 1 лекция
Недоступна
Погода в городе
Погода в городе
1
Задача
Swift SELF, 9 уровень, 1 лекция
Недоступна
Дробь на табло
Дробь на табло
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ