Под конец данного урока вы сможете:
- Определить важные моменты для объявления функции;
- Написать функцию, которую можно повторно использовать в других, более крупных программах.
Данный урок начинается с разбора документации стандартной библиотеки для функций, что были использованы в ранних уроках.
Рекомендуем вам супер TELEGRAM канал по Golang где собраны все материалы для качественного изучения языка. Удивите всех своими знаниями на собеседовании! 😎
Мы публикуем в паблике ВК и Telegram качественные обучающие материалы для быстрого изучения Go. Подпишитесь на нас в ВК и в Telegram. Поддержите сообщество Go программистов.
Содержание статьи
Познакомившись с синтаксисом создания функций, мы напишем функции для программы анализа погоды. Станция экологического мониторинга Ровер (REMS) собирает данные о погоде на поверхности Марса. Мы создадим функции, что в теории могли бы стать частью программы REMS — к примеру, конвертер температуры.
Рассмотрим ситуацию, когда вам нужно сделать бутерброд. Кажется, будто это просто, но все-таки процесс готовки строится из нескольких этапов. Помыть овощи, нарезать все ингредиенты и так далее. Если вдаваться в более глубокие детали, можно упомянуть сбор пшеницы, превращение ее в муку и выпечку хлеба. Однако данные подготовительные функции вас не касаются, они являются частью работы фермера и пекаря.
Можно посмотреть на описанный процесс через призму функций для каждого шага. Таким образом, если в будущем вам понадобятся кусочки помидоров для пиццы, оставшиеся ингредиенты можно будет использовать для нового блюда.
Наверняка вы ежедневно сталкиваетесь с примерами вещей, которые можно использовать повторно. Подумайте, какие из данных задач можно разбить на функции?
Объявление функции в Golang
В документации Go значится список функций, что объявляются в каждом пакете стандартной библиотеки. Функций довольно много, и охватить их все в одном уроке просто невозможно.
Для использования функций в своем проекте вам часто придется сверяться с документацией, чтобы посмотреть, как именно объявлять и вызывать функцию. Изучив объявления для Intn
, Unix
, Atoi
, Contains
и Println
, вы сможете использовать полученные знания для работы с другими функциями и написания своих собственных.
В одном из предыдущих уроков мы уже использовали функцию Intn
для генерации псевдослучайных чисел. Перейдите на следующую страницу документации, найдите пакет math/rand
и функцию Intn
. Для нахождения Intn
можете использовать их поиск по сайту.
Объявление Intn
из пакета rand
выглядит следующим образом:
1 |
func Intn(n int) int |
Освежим в памяти способ использования функции Intn
:
1 |
num := rand.Intn(10) |
На следующей схеме части объявления идентифицируются, как и синтаксис для вызова функции Intn
. Ключевое слово func
позволяет Go понять о том, что это объявление функции. Название самой функции, Intn
, должно начинаться с большой буквы.
Объявление и вызов функции Intn
В Go функции, переменные и другие идентификаторы, что начинаются с большой буквы, экспортируются и становятся доступными для других пакетов. Пакет rand
также содержит функции, что начинаются с маленькой буквы, однако они не доступны для использования из пакета main
.
Функция Intn
принимает один параметр, что заключен в скобки. Параметром является название переменной, затем указывается тип и объявляется переменная:
1 |
var n int |
При объявлении функции Intn
целое число 10 передается в качестве единственного аргумента, он также заключается в скобки. Аргумент соответствует ожиданиям параметра Intn
. Если аргумент не передается или не относится к типу int, компилятор Go сообщит об ошибке.
На заметку: Параметр и аргумент являются математическими терминами, но есть небольшое различие. Функция принимает параметры и вызывается с аргументами, хотя в некоторых случаях данные термины используются как взаимозаменяемые.
Функция Intn
возвращает один результат, что является псевдослучайным целым числом типа int
. Результат передается назад вызывающему, где используется для инициализации вновь объявленной переменной num
.
Функция Intn
принимает только один параметр. Однако функции могут принимать и несколько параметров, если их представить в виде списка, где параметры разделены запятыми. Функция Unix
из пакета time
принимает два параметра int64
, что соответствуют количеству секунд и наносекунд, прошедших с 1 Января 1970 года. В соответствии с документацией, объявление будет выглядеть следующим образом:
1 |
func Unix(sec int64, nsec int64) Time |
Далее дан пример вызова функции Unix
с двумя аргументами, что соответствуют параметрам sec
и nsec
:
1 |
future := time.Unix(12622780800, 0) |
Unix возвращает результат типа Time
. Тип назначается автоматически, поэтому в коде, вызывающем Unix
, тип результата уточнять не требуется, что привело бы к лишней многословности.
На заметку: В следующем уроке мы рассмотрим способы объявления новых типов вроде
time.Time
и big.Int.
Пакет time
объявляет и экспортирует тип Time
, что начинается с большой буквы, как и функция Unix
. Использование верхнего регистра указывает на экспорт, следовательно, тип Time
доступен из других пакетов.
Функция Unix
принимает два параметра одинакового типа:
1 |
func Unix(sec int64, nsec int64) Time |
Когда параметры значатся в списке при объявлении функции, вам нужно уточнить тип, когда тот меняется. Это делается следующим образом:
1 |
func Unix(sec, nsec int64) Time |
Возможен сокращенный вариант, но он может быть использовать где-то еще. К примеру, в функции Contains
пакета strings
, что принимает два параметра типа string
:
1 |
func Contains(s, substr string) bool |
На заметку: В документации Go иногда даются небольшие примеры, в которые можно добавить нужные вам детали. Дополнительные примеры также можно найти на gobyexample.com. Если вы уже работаете над собственными проектами, данные примеры могут прийтись кстати.
Во многих языках программирования есть функции, что принимают несколько параметров, однако Go также возвращает несколько результатов. Рассмотренная в одном из предыдущих уроков функция Atoi конвертирует строку в число и возвращает два результата, что в следующем примере присваиваются переменным countdown
и err
:
1 |
countdown, err := strconv.Atoi("10") |
В документации для пакета strconv
функция Atoi
объявляется следующим образом:
1 |
func Atoi(s string) (i int, err error) |
Обратите внимание, что тип
error
является встроенным типом для указания ошибок.
С самого начала изучения Golang мы использовали функцию Println. Это универсальная функция в том плане, что она может принимать как один, так и несколько параметров. Она также принимает параметры разных типов, включая целые числа и строки:
1 2 |
fmt.Println("Hello, playground") fmt.Println(186, "seconds") |
Объявление функции из документации, может показаться несколько странным, так как здесь используются некоторые аспекты, которые мы еще не изучали:
1 |
func Println(a ...interface{}) (n int, err error) |
Функция Println
принимает один параметр а
, но вы уже видели, что передача нескольких аргументов также возможна. Кроме того, вы можете передать функции Println
переменное количество аргументов, на данную особенность указывает многоточие (...
). Для этого есть специальный термин — вариативная функция, которой является Println
. Параметр а
является набором аргументов, передаваемых функции. О вариативных функциях более детально поговорим в одном из следующих уроков.
Типом параметра а
является interface{}
, что называют пустым типом интерфейса. Это специальный тип, что позволяет Println
принимать int
, float64, string, time.Time
и другие типы без того, чтобы компилятор Go не выводил ошибку.
Комбинация вариативных функций и пустого интерфейса, написанная как …interface{}
, значит, что вы передаете функции Println
любое число аргументов любого типа. Все передаваемые данные отображаются без проблем.
На заметку: Все это время мы игнорировали два результата, что возвращает
Println
. Однако игнорирование ошибок в программировании считается дурным тоном. Подробнее об этом в уроке по обработке ошибок.
Вопросы для проверки:
- Для вызова функции вы используете аргументы или параметры?
- Функция принимает аргументы или параметры?
- Чем функция, названием которой начинается с большой буквы (
Contains
) отличается от той, что пишется с маленькой буквы (contains
)? - На что указывает многоточие (…) при объявлении функции?
Создание функции в Golang
Пока что код всех рассматриваемых примеров был помещен в функцию main
. При работе с крупными приложениями вроде программы для наблюдения за окружающей средой удобно разделять крупную задачу на несколько более мелких подзадач. Организация программы через функции делает код более простым для понимания, использования и обслуживания.
Данные сенсора температуры должны быть получены в единицах измерения, понятных для землян. Сенсоры считывают температуры по шкале Кельвина, где 0° K является абсолютным нулем, или минимальной возможной температурой. Функция в следующем листинге конвертирует температуру из градусы Кельвина в градусы Цельсия. После создания, функция может использоваться вновь, когда потребуется конвертация температуры.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package main import "fmt" // kelvinToCelsius конвертирует °K в °C func kelvinToCelsius(k float64) float64 { // Объявляет функцию, что принимает параметр и возвращает результат k -= 273.15 return k } func main() { kelvin := 294.0 celsius := kelvinToCelsius(kelvin) // Вызывает функцию передачи kelvin как первого аргумента fmt.Print(kelvin, "° K is ", celsius, "° C") // Выводит: 294° K is 20.850000000000023° C } |
Здесь функция kelvinToCelsius
принимает один параметр под названием k
типа float64
. В соответствии правилами Go, первый комментарий для функции kelvinToCelsius
начинается с названием функции, за которым следует описание того, что она делает.
Функция возвращает значение типа float64
. Результат вычисления возвращается вызывающему с ключевым словом return, который потом используется для инициализации новой переменной celsius
в внутри функции main
.
Обратите внимание, что функции внутри одного и того же пакета вызываются без уточнения названия пакета.
Преимущества изоляции функции в Golang
Функция kelvinToCelsius
из Листинга 1 изолирована от других функций. Ее единственной входной информацией является принимаемый параметр, а единственным выводом является возвращаемый результат. Он не вносит изменения во внешнее состояние программы. У таких функций нет побочных эффектов, они являются наиболее простыми для понимания, проверки и повторного использования.
Функция kelvinToCelsius
модифицирует переменную k
, однако k
и kelvin
полностью независимые переменные, поэтому присваивание нового значения к k
внутри функции не оказывает никакого влияния на переменную kelvin
внутри main
. Такое поведение называется передачей значения, так как параметр k
инициализируется через значение аргумента kelvin
. Передача значения закрепляет границы перед функциями, помогая изолировать одну функцию от другой.
Мы дали переменным разные названия, однако передача значений предполагается даже в том случае, когда у аргументов и значений одинаковые названия.
Плюс ко всему переменная под названием k
в kelvinToCelsius
является полностью независимой от других переменных под названием k
в других функциях, что возможно благодаря области видимости переменной. У параметров при объявлении функции и переменных, объявленных внутри тела функции, есть область видимости функции. Переменные, объявленные в других функциях являются полностью независимыми, даже если у них одно и то же название.
Вопрос для проверки:
В чем преимущества разделения кода на функции?
Заключение
- Функции объявляются с названием, списком параметров и списком результатов;
- Названия функций и типов, что начинаются с большой буквы, делают их доступными для других пакетов;
- За каждым названием параметра или результата следует тип, хотя типы могут опускаться, когда у нескольких именованных параметров или результатов одинаковый тип. Результаты также могут быть представлены как типы без названий в списке;
- В вызовах функции есть префиксы с названием пакета, где объявляется функция, пока данная функция объявляется внутри того же пакета, откуда вызывается;
- Функции вызываются с аргументами, что соответствуют тем параметрам, что они принимают. Результаты возвращаются вызывающему с ключевым словом
return
.
Итоговое задание для проверки:
Используйте Go Playground и модифицируйте Листинг 1 для добавления дополнительных функций конвертирования температуры:
- Повторно используйте функцию
kelvinToCelsius
для конвертации 233° К в градусы Цельсия; - Напишите и используйте функцию конвертации температуры в градусы Фаренгейта —
celsiusToFahrenheit
. Формула для конвертации температуры в градусы по Фаренгейту: (c * 9.0 / 5.0) + 32.0; - Напишите функцию
kelvinToFahrenheit
и проверьте, чтобы она конвертирова 0° К в приблизительно –459.67° F.
Вы использовали kelvinToCelsius
и celsiusToFahrenheit
в своей новой функции или написали независимую функцию с новой формулой? Оба подхода подойдут.
Администрирую данный сайт с целью распространения как можно большего объема обучающего материала для языка программирования Go. В IT с 2008 года, с тех пор изучаю и применяю интересующие меня технологии. Проявляю огромный интерес к машинному обучению и анализу данных.
E-mail: vasile.buldumac@ati.utm.md
Образование
Технический Университет Молдовы (utm.md), Факультет Вычислительной Техники, Информатики и Микроэлектроники
- 2014 — 2018 Universitatea Tehnică a Moldovei, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Universitatea Tehnică a Moldovei, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»