Предыдущая часть – часть 8 – циклы.
Функция – это группа операторов, которые вместе выполняют определённую задачу. В каждой программе Go имеется как имнимум одна функция, которая называется main()
. Кроме неё – вы можете разбить код на другие функции по вашему усмотрению, но логически каждая функция должна выполнять определённую задачу.
Объявление (declaration) функции указывает компилятору имя функции, тип возвращаемого значения (или значений) и её аргументы. Описание (definition) функции включает в себя непоследственно тело фнукции.
Стандартная библиотека Go предоставляет большой набор встроенных функций, которые вы можете использовать в своих программах, например – функция len()
принимает объект в виде аргумента и возвращет длину элементов в этом объекте:
package main import "fmt" func main() { var a [5]int fmt.Printf("len array called a: %d\n", len(a)); }
Результат:
[simterm]
$ go run len.go
len array called a: 5
[/simterm]
Содержание
Описание функции
Синтаксис описания функции:
func function_name( [parameter list] ) [return_types] { body of the function }
Описание состоит из заголовка функции и тела фукнции.
Остальные части из примера синтаксиса выше:
func
– объявление о начале описания функцииfunction_name
– имя фукнцииparameter list
– список параметров функции. При вызове функции и передаче ей параметров – в параметре указыавется значение, которое называется аргументом функции. В списке параметров указывается тип принимаемого аргумента, порядок параметров, и их количество. Параметры функции являются опциональными, т.е. функция может не иметь параметров вообще.return_types
– возврщаемое значение или значения, в которых указываются типы данных, которые возвращает функция. Функция может не возвращать никаких значени й вообще, в таком случаеreturn_types
можно не указывать.body of the function
– тело функции, в котором указываются операторы и выражения для выполнения операции
Пример
В следующем коде демонстрируется функция с именем max()
, которая принимает два параметра – num1
и num2
, и возвращает большее из них:
/* function returning the max between two numbers */ func max(num1, num2 int) int { /* local variable declaration */ result int if (num1 > num2) { result = num1 } else { result = num2 } return result }
Вызов функции
Когда вы создаёте функцию – вы указывает её описание, и что она будет делать. Для её использования – вам необходимо вызвать эту функцию.
Когда программа вызывает функцию – она передаёт управление выполнением программы этой функции: функция выполняет необходимую задачу, и когда она достигает оператора return
или замыкающей фигурной скобки – она передаёт контроль выполнения обратно программме.
Для вызова функции вы просто указываете имя функции и необходимые параметры. Если функция возвращает какое-либо значение – вы можете сохранить его в переменную:
package main import ( "fmt" "os" "strconv" ) func main() { // pass two args to a, b vartiables, convert them to integer a, _ := strconv.Atoi(os.Args[1]) b, _ := strconv.Atoi(os.Args[2]) // define variable to return var ret int // calling a function to get max value ret = max(a, b) fmt.Printf( "Max value is : %d\n", ret ) } /* function returning the max between two numbers */ func max(num1, num2 int) int { // local variable declaration var result int if (num1 > num2) { result = num1 } else { result = num2 } return result }
Результат выполнения этой программы:
[simterm]
$ go run max.go 100 200
Max value is : 200
[/simterm]
Возврат нескольких значений
В Go функция может вернуть не одно, а два и более значений, например:
package main import ( "fmt" "os" ) func swap(x, y string) (string, string) { return y, x } func main() { var a string = os.Args[1] var b string = os.Args[2] c, d := swap(a, b) fmt.Println(c, d) }
Результат:
[simterm]
$ go run mult.go one two
two one
[/simterm]
Аргументы функции
Когда функция использует аргументы – в её заголовке должны быть указаны типы переменных, которые будут принимать эти аргументы. Такие переменные называются формальные параметрами (formal parameter) функции.
Формальные параметры ведут себя в функции как обычные локальные фукнции переменные, создаются при начале выполнения функции и удаляются при её завершении.
При вызове функции имеется два способа передачи аргументов:
Sr.No | Call Type & Description |
---|---|
1 | Call by value
Вызов по значению – этот способ выполняет копирование непосредственно значения аргумента в формальный параметр функции, т.е. значение запсиывается в участок памяти, выделенный под переменную, которая является формальным аргументом функции. В таком случае изменения, сделанные внутри функции, не повлияют на значение аргумента. |
2 | Call by reference
Вызов по ссылке – этот методе копирует адрес участка памяти, который содержит значение аргумента в формальный параметр. Внутри функции этот адрес используется для обработки и изменения значения.
|
Вызов по значению
По умолчанию в Go используется этот метод передачи данных в функцию, что означает, что функция не может менять значение аругмента.
Например, функция swap()
:
/* function definition to swap the values */ func swap(int x, int y) int { var temp int temp = x /* save the value of x */ x = y /* put y into x */ y = temp /* put temp into y */ return temp; }
Теперь – вызовем её, и передадим ей значения из main()
:
package main import "fmt" func main() { // local variable definition var a int = 100 var b int = 200 fmt.Printf("Before swap, value of a : %d\n", a ) fmt.Printf("Before swap, value of b : %d\n", b ) // calling a function to swap the values swap(a, b) fmt.Printf("After swap, value of a : %d\n", a ) fmt.Printf("After swap, value of b : %d\n", b ) } func swap(x, y int) int { var temp int temp = x // save the value of x x = y // put y into x y = temp // put temp into y return temp; }
И результат выполнения:
[simterm]
$ go run swap.go
Before swap, value of a : 100
Before swap, value of b : 200
After swap, value of a : 100
After swap, value of b : 200
[/simterm]
Мы видим, что хотя внутри функции swap()
значения были поменяны местами – их реальные значения вне функции остались неизменными.
Вызов по ссылке
Примечание: указатели ещё не рассматривались, можно посмотреть в посте C: указатели – подробный разбор
Для передачи значения по ссылке – функции передаётся указатель аргумента. Соответсвенно, при описании функции в её заголовке вы должны указать тип переменных параметров как указатель.
Например:
/* function definition to swap the values */ func swap(x *int, y *int) { var temp int temp = *x /* save the value at address x */ *x = *y /* put y into x */ *y = temp /* put temp into y */ }
Теперь давайте вызовем swap()
, передав значения по указателю:
package main import "fmt" func main() { // local variable definition var a int = 100 var b int = 200 fmt.Printf("Before swap, value of a : %d\n", a ) fmt.Printf("Before swap, value of b : %d\n", b ) /* calling a function to swap the values. * &a indicates pointer to a ie. address of variable a and * &b indicates pointer to b ie. address of variable b. */ swap(&a, &b) fmt.Printf("After swap, value of a : %d\n", a ) fmt.Printf("After swap, value of b : %d\n", b ) } func swap(x *int, y *int) { var temp int temp = *x // save the value at address x *x = *y // put y into x *y = temp // put temp into y }
И результат:
[simterm]
$ go run swap_rf.go
Before swap, value of a : 100
Before swap, value of b : 200
After swap, value of a : 200
After swap, value of b : 100
[/simterm]
Теперь видно, что изменения в функции затронули и значения вне её, в отличии от использования вызова по значению.
Применение функций
Функции можно применять следующими способами:
Sr.No | Function Usage & Description |
---|---|
1 | Function as ValueФункция создаётся “на лету”, и используется как значение. |
2 | Function ClosuresЗамыкания – анонимные функции в динамическом программировании |
3 | MethodМетоды – специальные функции с получателями (receiver) |
Функция как значение
В Go предоставляется возможность создавать функции на лету и использовать их как значения.
В следующем примере мы инициализируем переменную с описанием функции, чья роль – просто использовать встроенную функцию math.sqrt()
:
package main import ( "fmt" "math" ) func main(){ // declare a function variable getSquareRoot := func(x float64) float64 { return math.Sqrt(x) } // use the function fmt.Println(getSquareRoot(9)) }
Результат:
[simterm]
$ go run func_val.go
3
[/simterm]
Замыкания
В Go поддерживаются анонимные функции, которые работают как функции замыкания. Анонимные функции используются когда мы хотим создать функцию без объявления её имени.
В следующем примере мы создадим функцию getSequence()
, которая возвращает другую функцию. Роль этой функции – использовать внешнюю ей переменную i
вышестоящей функции, что бы сформировать замыкание:
package main import "fmt" func getSequence() func() int { i:=0 return func() int { i+=1 return i } } func main(){ // nextNumber is now a function with i as 0 nextNumber := getSequence() // invoke nextNumber to increase i by 1 and return the same fmt.Println(nextNumber()) fmt.Println(nextNumber()) fmt.Println(nextNumber()) // create a new sequence and see the result, i is 0 again nextNumber1 := getSequence() fmt.Println(nextNumber1()) fmt.Println(nextNumber1()) }
Результат:
[simterm]
$ go run func_clos.go
1
2
3
1
2
[/simterm]
Методы
Go поддерживает специальные типы функций, называемые методами. В объявлении методов присутсвует “получатель”, который представляет собой контейнер функции. Этот получатель может быть использован, что бы вызывать функцию используя оператор “.
“.
Синтаксис:
func (variable_name variable_data_type) function_name() [return_type]{ /* function body*/ }
Пример:
package main import ( "fmt" "math" ) // define a circle type Circle struct { x,y,radius float64 } // define a method for circle func(circle Circle) area() float64 { return math.Pi * circle.radius * circle.radius } func main(){ circle := Circle{x:0, y:0, radius:5} fmt.Printf("Circle area: %f\n", circle.area()) }
[simterm]
$ go run func_meth.go
Circle area: 78.539816
[/simterm]
Продолжение.