Определение и вызов функций в Go

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

Go предоставляет мощную стандартную библиотеку, которая имеет много предопределенных функций. С пакетом fmt вы, вероятно, уже знакомы:

  • fmt.Println() выводит объекты на стандартный вывод (скорее всего, это ваш терминал).
  • fmt.Printf() позволяет форматировать полученный вывод.

Имена функций всегда включают круглые скобки и могут содержать параметры.

В этом мануале вы узнаете, как определить собственные функции для своих программ.

Определение функции

Давайте попробуем для начала превратить классическую программу «Hello, World!» в функцию.

Для этого мы создадим новый текстовый файл и вызовем программу hello.go. Затем мы определим функцию.

Функция определяется с помощью ключевого слова func. Затем следует имя (можно выбрать любое) и скобки, где содержатся все параметры, которые примет функция (если параметров нет, скобки будут пустыми). Строки кода функции заключаются в фигурные скобки {}.

Давайте определим функцию по имени hello():

func hello() {}

Это начальный оператор для создания функции.

Сюда мы добавим вторую строку, чтобы предоставить инструкции о том, что делает эта функция. В данном случае функция должна выводить фразу «Hello, World!» на консоль:

func hello() {
fmt.Println("Hello, World!")
}

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

Следовательно, нужно вызвать функцию hello() внутри блока функции main():

package main
import "fmt"
func main() {
hello()
}
func hello() {
fmt.Println("Hello, World!")
}

Теперь давайте запустим программу:

go run hello.go

Вы получите следующий вывод:

Hello, World!

Обратите внимание, что мы также ввели в код функцию main() – это специальная функция, которая сообщает компилятору, что именно здесь должна начинаться программа. Любая исполняемая программа (которую можно запустить из командной строки) должна содержать функцию main(). Функция main() должна появляться в коде только один раз, находиться в пакете main(), и не получать и не возвращать никаких аргументов. Это позволяет выполнять программы в Go. Посмотрите на следующий пример:

package main
import "fmt"
func main() {
fmt.Println("this is the main section of the program")
}

Функции могут быть намного сложнее, чем определенная нами функция hello(). В функциях мы можем использовать циклы for, условные операторы и многое другое.

Читайте также: Условные операторы в Go

Например, следующая функция использует условный оператор, чтобы проверить, есть ли гласный во входных данных для переменной name, а затем использует цикл for для перебора букв в строке name.

package main
import (
"fmt"
"strings"
)
func main() {
names()
}
func names() {
fmt.Println("Enter your name:")
var name string
fmt.Scanln(&name)
// Check whether name has a vowel
for _, v := range strings.ToLower(name) {
if v == 'a' || v == 'e' || v == 'i' || v == 'o' || v == 'u' {
fmt.Println("Your name contains a vowel.")
return
}
}
fmt.Println("Your name does not contain a vowel.")
}

Функция names (), которую мы здесь определяем, устанавливает переменную name с помощью input, а затем устанавливает условный оператор в цикле for. Это показывает, как код может быть организован в определении функции. Однако, в зависимости от того, что мы намерены использовать в нашей программе и как мы хотим настроить наш код, мы можем захотеть определить условный оператор и цикл for как две отдельные функции.

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

Работа с параметрами

До сих пор мы рассматривали функции с пустыми скобками, которые не принимают аргументов, но в скобках можно определять параметры для функций.

Параметр – это именованная сущность в определении функции, указывающая аргумент, который может принимать данная функция. В Go нужно указывать тип данных для каждого параметра.

Читайте также: Основные типы данных в Go

Давайте создадим программу, которая повторяет слово определенное количество раз. Потребуется строковый параметр с именем word и параметр int с именем reps для количества повторений слова.

package main
import "fmt"
func main() {
repeat("8host", 5)
}
func repeat(word string, reps int) {
for i := 0; i < reps; i++ {
fmt.Print(word)
}
}

Мы передали значение 8host для параметра word и 5 для параметра reps. Значения соответствуют параметрам в том порядке, в котором они были заданы. Функция repeat имеет цикл for, который будет повторяться столько раз, сколько указано параметром reps. Для каждой итерации выводится значение параметра word.

Вот вывод этой программы:

8host8host8host8host8host

Если у вас есть набор параметров с одинаковыми значениями, вы можете не указывать тип каждый раз. Давайте создадим небольшую программу, которая принимает параметры x, y и z, и пусть все они принимают значения типа int. Мы создадим функцию, которая складывает значения параметров в разных конфигурациях. Их суммы будут выведены функцией на экран. Затем мы вызовем функцию и передадим ей числа.

package main
import "fmt"
func main() {
addNumbers(1, 2, 3)
}
func addNumbers(x, y, z int) {
a := x + y
b := x + z
c := y + z
fmt.Println(a, b, c)
}

Когда мы создавали сигнатуру функции для addNumbers, нужно было указать тип только в конце, а не каждый раз.

Мы передали число 1 для параметра x, 2 для y и 3 для z. Эти значения соответствуют каждому параметру в указанном порядке.

Программа выполняет следующие вычисления на основе значений, которые мы передали параметрам:

a = 1 + 2
b = 1 + 3
c = 2 + 3

Функция также выводит a, b и c, и, исходя из заданных значений, мы ожидаем, что a будет равно 3, b 4, а c — 5. Давайте запустим программу:

go run add_numbers.go
3 4 5

Когда мы передаем 1, 2 и 3 в качестве параметров функции addNumbers(), мы получаем ожидаемый результат.

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

Возврат значения

Функции можно передать значение параметра, но и функция также может создать значение.

Функция может сделать это с помощью оператора return, который выйдет из функции и при необходимости передаст выражение вызывающей стороне. Тип возвращаемых данных нужно указывать.

Ранее в функциях вместо оператора return мы использовали оператор fmt.Println(). Давайте создадим программу, которая будет возвращать переменную.

В новом текстовом файле double.go мы напишем программу, которая удваивает значение параметра x и возвращает переменную y. Мы вызываем вывод переменной result, который получается в результате запуска функции double(3):

package main
import "fmt"
func main() {
result := double(3)
fmt.Println(result)
}
func double(x int) int {
y := x * 2
return y
}

Давайте запустим программу:

go run double.go
6

В качестве выходных данных возвращается целое число 6 – это правильный результат умножения 3 на 2.

Если функция указывает возврат, вы должны предоставить его как часть кода. Если вы этого не сделаете, вы получите ошибку компиляции.

Чтобы продемонстрировать это, давайте закомментируем строку с оператором return:

package main
import "fmt"
func main() {
result := double(3)
fmt.Println(result)
}
func double(x int) int {
y := x * 2
// return y
}

А теперь давайте снова запустим программу:

go run double.go
./double.go:13:1: missing return at end of function

Без оператора return программа не может быть скомпилирована.

Функции завершаются сразу же после обработки оператором return, даже если этот оператор находится не в конце функции:

package main
import "fmt"
func main() {
loopFive()
}
func loopFive() {
for i := 0; i < 25; i++ {
fmt.Print(i)
if i == 5 {
// Stop function at i == 5
return
}
}
fmt.Println("This line will not execute.")
}

Здесь написан цикл for на 25 итераций. Однако внутри цикла for есть условный оператор if, который проверяет, равно ли значение i 5. Если это так, он выдает оператор return. Поскольку мы находимся в функции loopFive, return в любой точке функции приведет к завершению функции. В результате программа никогда не доберется до последней строки в этой функции, чтобы отобразить строку «This line will not execute».

Использование оператора return в цикле for завершает функцию, поэтому строка, находящаяся вне цикла, не будет выполняться. Если бы вместо него мы использовали оператор break, то в этом месте закончился бы только цикл, и последняя строка fmt.Println() запустилась бы.

Читайте также: Операторы break и continue в циклах Go

Оператор return прерывает функцию и может возвращать значение, если оно указано в сигнатуре функции.

Возврат нескольких значений

Функции можно задать более одного возвращаемого значения. Давайте рассмотрим программу repeat.go и сделаем так, чтобы она возвращала два значения: первое значение будет повторяемым, а второе будет ошибкой, которая возникнет, если параметр reps меньше 0:

package main
import "fmt"
func main() {
val, err := repeat("8host", -1)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(val)
}
func repeat(word string, reps int) (string, error) {
if reps <= 0 {
return "", fmt.Errorf("invalid value of %d provided for reps. value must be greater than 0.", reps)
}
var value string
for i := 0; i < reps; i++ {
value = value + word
}
return value, nil
}

Сначала функция repeat проверяет, является ли значение аргумента reps допустимым. Любое значение меньше 0 приведет к ошибке. Поскольку мы передали значение -1, эта ветвь кода будет выполнена. Обратите внимание, что, выходя из функции, мы должны предоставить string и error. Поскольку предоставленные аргументы привели к ошибке, для первого значения return будет пустая строка, а для второго значения return будет ошибка.

Мы можем получить оба значения в функции main(), объявив две новые переменные, value и err. Поскольку в возврате может быть ошибка, нужно проверить, не получили ли мы ошибку, прежде чем продолжить работу программы. В этом примере мы получили ошибку. Мы выводим ее и выходим из функции main(), что завершит работу программы.

Если бы ошибки не было, программа бы вывела значение функции.

Примечание: Рекомендуется возвращать только два или три значения. Кроме того, все ошибки всегда должны возвращаться как последнее значение функции.

Программа выведет такой результат:

invalid value of -1 provided for reps. value must be greater than 0.

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

Заключение

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

Читайте также: Как писать Go-пакеты

Tags: ,