Перейти к содержимому
Теория · Первая программа · урок 6 из 8

Первая функция

В кулинарной книге соус описывают один раз, на отдельной странице. Остальные рецепты не повторяют его по шагам, а пишут коротко: «приготовьте соус». Своя функция в программе устроена так же: её шаги записаны в одном месте, а вызвать её можно откуда угодно.

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

GO
package main

import "fmt"

func add(a, b int) int {
    return a + b
}

func main() {
    fmt.Println(add(2, 3))   // 5
    fmt.Println(add(10, -4)) // 6
}

Давайте проследим за первым вызовом:

  1. add(2, 3) отправляет выполнение в функцию add, где a равно 2, а b равно 3.
  2. Строка return a + b считает 5 и возвращает это число туда, откуда функцию вызвали.
  3. fmt.Println получает 5 и печатает его.

Второй вызов работает так же, но с другими числами, и печатает 6.

Нажмите «Следующий шаг» и следите, как управление уходит из main в add и возвращается с готовым числом.

ВживуюВызов функции по шагамшаг 1 из 8 · рамок: 1

Чтобы напечатать результат, main сначала вызывает add(2, 3).

Рамка — место, где живут переменные одного вызова

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

Строка func add(a, b int) int читается слева направо:

  • func — ключевое слово: «объявляю функцию»;
  • add — имя функции;
  • (a, b int) — параметры, переменные, которые получают значения при вызове; оба имеют тип int, то есть целое число;
  • int после скобок — тип результата функции.

Инструкция return завершает функцию и возвращает значение выражения после неё. Выражение — это код, который даёт значение, например a + b.

Функцию можно объявить выше или ниже main. Порядок объявлений в файле не важен: компилятор читает пакет целиком.

Вызов и аргументы

В вызове add(2, 3) числа 2 и 3 — аргументы. Они и сам вызов подчиняются трём правилам:

  • аргументы попадают в параметры по порядку: 2 в a, 3 в b;
  • вызов заменяется значением, которое вернула функция, поэтому его можно передать в fmt.Println;
  • число аргументов должно совпадать с числом параметров: вызов add(1) компилятор отклонит с ошибкой not enough arguments in call to add.

Здесь картинка с соусом перестаёт работать. Соус по рецепту каждый раз выходит одинаковым. А функция при каждом вызове получает свои аргументы, и её результат зависит от них.

Порядок вычислений

В одном выражении могут встретиться несколько операторов и вызовов. Давайте посмотрим на три строки:

GO
fmt.Println(1 + 2*3)           // 7
fmt.Println((1 + 2) * 3)       // 9
fmt.Println(add(add(1, 2), 3)) // 6

Первые две строки подчиняются правилам операторов, а третья — правилу вложенных вызовов. Разберём оба правила.

Операторы

С целыми числами работают операторы +, - и *. Порядок задают такие правила:

  • как в математике, * выполняется раньше + и -;
  • скобки меняют этот порядок;
  • пробелы на порядок не влияют: 1 + 2*3 и 1+2 * 3 дают одно и то же.

Слитная запись 2*3 только подсказывает читателю, что умножение выполнится первым. По этим правилам Go считает первые две строки примера так:

  1. 1 + 2*3: сначала 2*3, и 1 + 6 даёт 7.
  2. (1 + 2) * 3: скобки заставляют сначала сложить 1 + 2, и 3 * 3 даёт 9.

Вложенные вызовы

Вызов функции тоже может стоять аргументом другого вызова. Тогда Go сначала выполняет внутренний вызов, и его результат становится аргументом внешнего.

В третьей строке примера это выглядит так:

  1. add(1, 2) возвращает 3.
  2. add(3, 3) возвращает 6.
  3. fmt.Println печатает только 6.

Частая ошибка

Функция печатает сумму вместо того, чтобы вернуть её, и тип результата у неё не указан:

GO
package main

import "fmt"

func add(a, b int) {
    fmt.Println(a + b)
}

func main() {
    fmt.Println(add(2, 3)) // ✗
}
TEXT
add(2, 3) (no value) used as value

Функция без типа результата ничего не возвращает, поэтому её вызов нельзя передать в fmt.Println. Чтобы исправить ошибку, укажите int после скобок и верните сумму через return, как в первом примере.

Главное

  • Параметры с типами стоят в скобках после имени функции, а тип результата — после скобок.
  • return завершает функцию, и вызов заменяется значением, которое она вернула.
  • * выполняется раньше + и -, а внутренний вызов — раньше внешнего.
  • Функция без типа результата ничего не возвращает, и её вызов нельзя передать в fmt.Println.

Подпишись, чтобы пройти курс

Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.

Войти другим способом