Функція дозволяє згрупувати код у повторно використовуваний блок.
Вона складається з ключового слова func, назви функції та розділеного комами списку з нуля або більше параметрів і типів у круглих дужках.
Усі параметри повинні мати явно вказаний тип; виведення типу для параметрів не працює. Параметри не мають типових значень, тому всі параметри функції є обовʼязковими.
import "fmt"
// No parameters
func PrintHello() {
fmt.Println("Hello")
}
// Two parameters
func PrintGreetingName(greeting string, name string) {
fmt.Println(greeting + " " + name)
}
Параметри одного типу можна оголосити разом, а після них указати один спільний тип.
import "fmt"
func PrintGreetingName(greeting, name string) {
fmt.Println(greeting + " " + name)
}
Швидко розглянемо два терміни, які часто плутають: parameters і arguments.
Параметри функції - це назви, визначені в сигнатурі функції, як-от greeting і name у функції PrintGreetingName вище.
Аргументи функції - це конкретні значення, які передаються параметрам функції, коли ми викликаємо функцію.
Наприклад, у прикладі нижче "Hello" і "Katrina" - це аргументи, передані параметрам greeting і name:
PrintGreetingName("Hello", "Katrina")
Після параметрів функції йдуть нуль або більше повернених значень, які теж повинні мати явно вказаний тип.
Одиничні повернені значення записують без дужок, а кілька повернених значень беруть у дужки.
Значення повертаються у код, що викликав функцію, за допомогою ключового слова return.
У функції може бути кілька інструкцій return.
Виконання функції завершується, щойно вона натрапить на одну з таких інструкцій return.
Якщо з функції потрібно повернути кілька значень, їх розділяють комами.
func Hello(name string) string {
return "Hello " + name
}
func HelloAndGoodbye(name string) (string, string) {
return "Hello " + name, "Goodbye " + name
}
Щоб викликати функцію, потрібно вказати її назву та передати аргументи для кожного з параметрів функції в дужках.
import "fmt"
// No parameters, no return value
func PrintHello() {
fmt.Println("Hello")
}
// Called like this:
PrintHello()
// One parameter, one return value
func Hello(name string) string {
return "Hello " + name
}
// Called like this:
greeting := Hello("Dave")
// Multiple parameters, multiple return values
func SumAndMultiply(a, b int) (int, int) {
return a+b, a*b
}
// Called like this:
aplusb, atimesb := SumAndMultiply(a, b)
Так само, як і параметри, повернені значення можна за бажанням назвати.
Якщо використано іменовані повернені значення, інструкція return без аргументів поверне саме їх.
Це називають «голим» return.
func SumAndMultiplyThenMinus(a, b, c int) (sum, mult int) {
sum, mult = a+b, a*b
sum -= c
mult -= c
return
}
Також важливо розібратися з поняттям передачі за значенням і передачі за посиланням.
Спершу розберемо передачу за значенням.
У прикладі нижче, коли ми передаємо змінну val у функцію MultiplyByTwo, ми передаємо копію val.
Через це newVal має оновлене значення 4, але початкова змінна val досі дорівнює 2.
Під капотом Go по суті створює копію початкового значення, тож функція змінює лише цю копію, вона ж v.
val := 2
func MultiplyByTwo(v int) int {
v = v * 2
return v
}
newVal := MultiplyByTwo(val)
// newval is 4, val is still 2 because only a copy of its value was passed into the function
Строго кажучи, у Go всі аргументи передаються за значенням, тобто створюється копія значення чи даних, наданих функції.
Але якщо ми не хочемо створювати копію даних, переданих у функцію, і хочемо змінювати ці дані всередині функції,
слід використовувати pointers як аргументи, тобто передачу за посиланням.
Щоб досягти передачі за посиланням, ми використовуємо pointer.
Передаючи аргументи-pointer у функцію,
ми можемо змінювати самі дані, передані у функцію, а не лише працювати з копією цих даних.
Поки що достатньо знати, що типи вказівників можна розпізнати за символом * перед типом у сигнатурі функції.
func HandlePointers(x, y *int) {
// Some logic to handle integer values referenced by pointers x and y
}
Якщо поняття pointer здається заплутаним, не варто хвилюватися.
Далі в силабусі є окремий розділ, який допоможе нам опанувати вказівники.
Зауважмо, що slices і maps - це винятки з правила, згаданого вище.
Коли ми передаємо slice або map як аргументи у функцію, з ними поводяться як з типами вказівників, навіть якщо в типі немає явного *.
Це означає, що якщо ми передаємо слайс або мапу у функцію й змінюємо дані, що лежать в їхній основі,
ці зміни відобразяться на початковому слайсі чи мапі.
У цій вправі ми напишемо ще трохи коду, повʼязаного з приготуванням і запіканням нашої чудової лазаньї з улюбленої кулінарної книги.
На нас чекають чотири завдання. Перше стосується самого приготування, а решта три - ідеальної підготовки.
Готуючись до наступної лазаньї, ми хочемо переконатися, що в нас зарезервовано достатньо часу, аби приготування було в радість. Ми вже спланували, з яких шарів складатиметься наша лазанья. Тепер ми хочемо оцінити, скільки часу займе підготовка, виходячи з цього.
Реалізуйте функцію PreparationTime, яка приймає зріз шарів як []string і середній час підготовки одного шару в хвилинах як int.
Функція має повернути оцінку загального часу підготовки на основі кількості шарів як int.
У Go немає типових значень для функцій.
Якщо середній час підготовки передано як 0 (типове початкове значення для int), тоді слід використати типове значення 2.
layers := []string{"sauce", "noodles", "sauce", "meat", "mozzarella", "noodles"}
PreparationTime(layers, 3)
// => 18
PreparationTime(layers, 0)
// => 12
Окрім зарезервованого часу, ми також хочемо переконатися, що в нас достатньо соусу й локшини, щоб приготувати лазанью своєї мрії. На кожен шар локшини в нашій лазаньї знадобиться 50 грамів локшини. На кожен шар соусу в нашій лазаньї знадобиться 0.2 літра соусу.
Визначте функцію Quantities, яка приймає зріз шарів як параметр типу []string.
Потім функція визначить кількість локшини й соусу, потрібну для нашої страви.
Результат має бути повернуто як два значення: noodles типу int і sauce типу float64.
Quantities([]string{"sauce", "noodles", "sauce", "meat", "mozzarella", "noodles"})
// => 100, 0.4
Якось ми завітали до друга й їли там лазанью. Вона була неймовірна, і в ній було щось особливе. Друг надіслав нам список інгредієнтів і сказав, що останній елемент у списку - це «секретний інгредієнт», який зробив страву такою особливою. Тепер ми хочемо додати цей секретний інгредієнт і до свого рецепта.
Напишіть функцію AddSecretIngredient, яка приймає два зрізи інгредієнтів типу []string як параметри.
Перший параметр - це список, який надіслав нам друг, а другий - список інгредієнтів нашого власного рецепта.
Останній елемент у нашому списку інгредієнтів завжди "?".
Функція має замінити його останнім елементом зі списку друга.
Примітка: AddSecretIngredient нічого не повертає, тож список наших інгредієнтів ми маємо змінити безпосередньо.
Список з інгредієнтами друга не має бути змінений.
Крім того, оскільки slice передається у функцію як вказівник, зміни в обох аргументах []string, переданих у AddSecretIngredient, застосовуються безпосередньо.
friendsList := []string{"noodles", "sauce", "mozzarella", "kampot pepper"}
myList := []string{"noodles", "meat", "sauce", "mozzarella","?"}
AddSecretIngredient(friendsList, myList)
// myList => []string{"noodles", "meat", "sauce", "mozzarella", "kampot pepper"}
Кількості, наведені в кулінарній книзі, дають лазанью лише на дві порції. Оскільки наступного разу ми хочемо приготувати для більшої кількості людей, потрібно обчислити кількості для різної кількості порцій.
Реалізуйте функцію ScaleRecipe, яка приймає два параметри.
float64, потрібних для 2 порцій.Функція має повернути зріз float64 з кількостями, потрібними для бажаної кількості порцій.
Але ми хочемо зберегти початковий рецепт.
Це означає, що аргумент quantities не має змінюватися в цій функції.
quantities := []float64{ 1.2, 3.6, 10.5 }
scaledQuantities := ScaleRecipe(quantities, 4)
// => []float64{ 2.4, 7.2, 21 }
Зареєструйтеся на Exercism, щоб вивчати й опановувати Go, а також 34 концепції166 вправ та справжнє наставництво від людей, і все це безкоштовно.