5. More on Functions
func (p mytype) funcname(q int ) (r,s int ) { return 0,0 }
- The keyword
funcis used to declare a function. - A function can optionally be bound to a specific type. This is called the receiver.
-
funcnameis the name of your function. - The variable q of type
intis the input parameter. The parameters are passed pass-by-value meaning they are copied. - Go requires explicit returns, i.e. it won’t automatically return the value of the last expression.
- The variables r and s are the named return parameters for this function. Functions in Go can have multiple return values. If you want the return parameters not to be named you only give the types:
(int, int). If you have only one value to return you may omit the parentheses. -
{ return 0,0 }is the function’s body. Note thatreturnis a statement so the braces around the parameter(s) are optional. - Functions can be declared, in a program, in any order you wish. The compiler scans the entire file before execution, so function prototyping is a thing of the past in Go.
- Function names starting with a capital letter are exported outside the package, that is, they are visible and can be used by other packages; then they follow PascalCasing eg. MyFunctionInGo().
- When a function is not exported outside a package; then they follow camelCasing: every new word in the name starts with a capital letter eg. myFunctionInGo().
- Functions are values. Functions can be passed around just like any other value. A function’s type signature describes the types of its arguments and return values.
Examples:
Here are two examples. The first is a function without a return value, while the next one is a simple function that returns its input.
func subroutine(in int ) { return }
func identity(in int ) int { return in }
5.1 A function can have multiple return values. Some examples:
1 x, err := f()
Here is another example:
1 func f() (int, int) {
2 return 5, 6
3 }
4
5 func main() {
6 x, y := f()
7 }
Note: Three changes are necessary: change the return type to contain multiple types separated by comma, change the expression after the return so that it contains multiple expressions separated by comma and finally change the assignment statement so that multiple values are on the left side of the := or =.
It is legitimate to discard some or all of a function’s return values by assigning them to the blank identifier (_). However, if no return values are wanted it is more conventional to simply ignore them.
Here’s an example:
Program: func1.go
1 package main
2
3 import "fmt"
4
5 func multipleVals()(string, int){
6 return "Go-Lang",100
7 }
8
9 func main() {
10 lang, number := multipleVals()
11 fmt.Println(lang)
12 fmt.Println(number)
13
14 // Ignore the first return value with Blank identifier
15 _, anotherNum := multipleVals()
16 fmt.Println(anotherNum)
17 }
The output is:
Go-Lang
100
100
5.2 A function can take zero or more arguments
1 func add(x int, y int) int {
2 return x + y
3 }
When two or more consecutive named function parameters share a type, you can omit the type from all but the last.
Program: func2.go
1 package main
2
3 import "fmt"
4
5 func add(i, j int) int {
6 return i + j
7 }
8
9 func main() {
10 fmt.Println(add(42, 13))
11 }
In the above example, we shortened i int, j int to i, j int.
5.3 Named return values
Go’s return values may be named and act just like variables. These names should be used to document the meaning of the return values.
A return statement without arguments returns the current values of the results. This is known as a “naked” return. Naked return statements should be used only in short functions, as with the example shown here. They can harm readability in longer functions.
Program: func3.go
1 package main
2
3 import "fmt"
4
5 func split(sum int) (x, y int) {
6 x = sum * 4 / 9
7 y = sum - x
8 return
9 }
10
11 func main() {
12 fmt.Println(split(17))
13 }
|
Exercise 11Why is the output as shown? |
Program: ex11.go
1 package main
2
3 import "fmt"
4
5 var i = 10
6
7 func main() {
8 a()
9 b()
10 a()
11 }
12
13 func a() {
14 fmt.Println(i)
15 }
16
17 func b() {
18 i := 20
19 fmt.Println(i)
20 }
The output when you run the above program is:
10
20
10
|
Exercise 12Why is the output as shown? |
Program: ex12.go
1 package main
2
3 import "fmt"
4
5 var i int
6
7 func main() {
8 i := 20
9 fmt.Println(i)
10 b()
11 }
12
13 func b() {
14 i := 40
15 fmt.Println(i)
16 a()
17 }
18
19 func a() {
20 fmt.Println(i)
21 }
The output when you run the above program is:
20
40
0
5.4 Variadic Functions
We have discussed this along with range in “Variadic Functions using range” in a later chapter.
5.5 Closures
It is possible to create functions inside of functions:
Program: func4.go
1 package main
2
3 import "fmt"
4
5 func main() {
6 x := 0
7 increment := func() int {
8 x++
9 return x
10 }
11 fmt.Println(increment())
12 fmt.Println(increment())
13 }
The output is:
1
2
increment is a local variable that has the type func() int (a function that takes no parameters and returns an int).
increment adds 1 to the variable x which is defined in the main function’s scope. This x variable can be accessed and modified by the increment function. This is why the first time we call increment we see 1 displayed, but the second time we call it we see 2 displayed.
A function like this together with the non-local variables it references is known as a closure. In this case increment and the variable x form the closure.
One way to use closure is by writing a function which returns another function which – when called – can generate a sequence of numbers. For example here’s how we might generate odd numbers:
Program: func5.go
1 package main
2
3 import "fmt"
4
5 //This function makeOddGenerator returns
6 //another function, which we define anonymously
7 //in the body of makeOddGenerator
8 func makeOddGenerator() func() int {
9 i := 1
10 return func() int {
11 i += 2
12 return i
13 }
14 }
15
16 func main() {
17 nextOdd := makeOddGenerator()
18 fmt.Println(nextOdd())
19 fmt.Println(nextOdd())
20 fmt.Println(nextOdd())
21
22 //To confirm that the state is unique
23 //to that particular function, create
24 //and test a new one
25 newOdd := makeOddGenerator()
26 fmt.Println(newOdd())
27 fmt.Println(newOdd())
28 fmt.Println(newOdd())
29 }
The output is:
3
5
7
3
5
7
5.6 Recursion
Go supports recursive functions i.e. a function is able to call itself. Here’s a classic factorial example:
Program: func6.go
1 package main
2
3 import "fmt"
4
5 func fact(n int) int {
6 if n == 0 {
7 return 1
8 }
9 return n * fact(n-1)
10 }
11 func main() {
12 fmt.Println(fact(8))
13 }
Closure and recursion are powerful programming techniques which form the basis of a paradigm known as functional programming. Most people will find functional programming more difficult to understand than an approach based on for loops, if statements, variables and simple functions.
5.7 Solutions
Exercise 11 Reason: Variables declared outside any functions are global in Go, those defined in functions are local to those functions. If names overlap — a local variable is declared with the same name as a global one — the local variable hides the global one when the current function is executed.
Exercise 12 Reason: A local variable is only valid when we are executing the function in which it is defined.
YES…BUT THERE’S MORE…