04. Using Functions


We will learn different king of functions in golang.
High Order Functions
Variadic Functions
Anonymous Functions
Functions
Self contained units of code which carry out a certain job.
Help us dividing a program into small manageable, repeatable, and organizable chunks.
From a higher abstraction level, a function is just takes an input and returns an output.
Why to use functions?
Reusability: Define a function once, and call/use it multiple time.
Abstraction: To abstract the inner implementation, Ofcource we need to know the function name, working of the entire function, the returns values, and the arguments to create that function. But we need not to know how it works to just use it.
Function syntax
func functionName(params) returnType {
// Function body
}
// func functionName(params) returnType :> Function signature
Example:
func addNumbers(a int, b int) int {
// Function body
return a + b
}
A function can return a value, or not even return anything.
When a return statement is reached in a function, the program returns to the code that invoked or called that function.
Calling a function
<function_name>(<argument(s)>)
- Function name; argument(s)/input(s) that the function requires, separated by commas.
Example:
addNumbers(2, 3)
// Storing returns values by a function in a variable
sumOfNumbers := addNumbers(2, 3)
Naming Convention for functions
Must begin with a letter.
Can have any number of additional letters and symbols.
Cannot contain spaces.
Case-sensitive.
Parameters vs arguments
Function parameters are the names listed in the function’s definition.
We have input parameter(s) and a return parameter
Input parameter can be passed in two ways: 1. Call by value, and 2. Call by address
Function arguments are real values passed into the function.
func addNumbers(a int, b int) int { // Parameters
sum := a + b
return sum
}
func main() {
sumOfNumbers := addNumbers(2, 3) // Arguments
fmt.Println(sumOfNumbers)
}
func printGreeting(str string) {
fmt.Println("Hey there, ", str)
}
func main() {
printGreeting("Joe")
}
Function return-types
Returning single value
func addNumbers(a int, b int) string {
sum := a + b
return sum
}
func main() {
sumOfNumbers := addNumbers(2, 3)
fmt.Println(sumOfNumbers)
}
/*
This code will result in a compilation error because the addNumbers function
is defined to return a string, but it attempts to return an int.
*/
- Return value must match the function signature.
Returning multiple values
func operation(a int, b int) (int, int) {
sum := a + b
diff := a - b
return sum, diff // Returing multiple values
}
func main() {
sum, difference := operation(20, 10)
fmt.Println(sum, difference)
}
// Output: 30 10
- The statement containing the return values is also known as terminating statement. Here,
return sum, diff
is the terminating statement. As the function will terminate and return back to where it was called from the moment this statement is encountered.
func operation(a int, b int) (sum int, diff int) {
sum = a + b // Already declared in the function signature
diff = a - b // sum, and diff
return
}
func main() {
sum, difference := operation(20, 10)
fmt.Println(sum, " ", difference)
}
// Output: 30 10
- Golang allows giving names to the return or result parameters of the functions in the function signature/definition.
Variadic functions
Function that accepts variable number of arguments.
It is possible to pass a varying number of arguments of the same type as referenced in the function signature.
To declare a variadic function, the type of the final parameter is preceded by an ellipsis
“...”
Example -
fmt.Println
method.
Syntax
func <func_name>(param_1 type, param_2 type, param_3 ...type) <return_type>
It indicates that the function can called at any numbers of parameters of this type. There can be any number of parameters, but if we want to use variadic parameters, we’ll have to place it at the end.
func sumNumbers(numbers ...int) int
In the above example, the function signature accepts an arbitrary number of arguments of the type int. The function over here will accept zero or more integers, and within the function, the numbers
variable will contain a slice of all the arguments.
func sumNumbers(str string, numbers ...int)
Here, we have two parameters, a string and multiple integers.
Note: How the variadic parameter is placed towards the end.
Example 1.
package main
import "fmt"
func sumNumbers(numbers ...int) int {
sum := 0
for _, value := range numbers {
sum += value
}
return sum
}
func main() {
fmt.Println(sumNumbers()) // Output: 0
fmt.Println(sumNumbers(10)) // Output: 10
fmt.Println(sumNumbers(10, 20)) // Output: 30
fmt.Println(sumNumbers(10, 20, 30, 40, 50)) // Output: 150
}
Output:
0
10
30
150
Example 2.
package main
import "fmt"
func printDetails(student string, subjects ...string) {
fmt.Printf("Hey %s, here are your subjects - ", student)
for _, sub := range subjects {
fmt.Printf("%s, ", sub)
}
fmt.Println()
}
func main() {
printDetails("Joe", "Physics", "Biology")
}
// Output: Hey Joe, here are your subjects - Physics, Biology,
Blank identifier (‘_’)
The blank identifier is the single underscore operator. It is used to ignore the values that are returned by functions.
We have already used it in
range
function for looping.
Step 1: Using both return values
package main
import "fmt"
func f() (int, int) {
return 42, 53
}
func main() {
a, b := f()
fmt.Println(a, b)
}
Output:
42 53
Step 2: Attempting to use only one variable
package main
import "fmt"
func f() (int, int) {
return 42, 53
}
func main() {
v := f()
fmt.Println(v)
}
Error:
./prog.go:11:6: assignment mismatch: 1 variable but f returns 2 values
This error occurs because the function f
returns two values, but only one variable v
is used to capture them.
Step 3: Using a blank identifier to ignore the second return value
package main
import "fmt"
func f() (int, int) {
return 42, 53
}
func main() {
v, _ := f()
fmt.Println(v)
}
Output:
42
In this step, the blank identifier _
is used to ignore the second return value, allowing the first value to be stored in v
without causing an error.
Recursive function
Recursion is a concept where a function calls itself by direct or indirect means.
The function keeps calling itself until it reaches a base case
Used to solve a problem where the solution is dependent on the smaller instance of the same problem.
package main
import "fmt"
func factorial(n int) int {
if n == 1 {
return 1
}
return n * factorial(n-1)
}
func main() {
n := 5
result := factorial(n)
fmt.Println("Factorial of", n, "is:", result)
}
Output:
Factorial of 5 is: 120
Anonymous Functions
An anonymous function is a function that is declared without any named identifier to refer to it.
They can accepts inputs and return outputs, just as standard functions do.
They can be used for containing functionality that need not be named and possibly for short-term use.
Function inside function
package main
import "fmt"
func main() {
x := func(l int, b int) int {
return l * b
}
fmt.Printf("%T \n", x)
fmt.Println(x(20, 30))
}
Output:
func(int, int) int
600
Explanation:
In this code, an anonymous function is defined and assigned to the variable x
. This function takes two integer parameters, l
and b
, and returns their product. The fmt.Printf
statement prints the type of x
, which is a function that takes two integers and returns an integer. The fmt.Println
statement calls the function x
with the arguments 20
and 30
, and prints the result, which is 600
. This demonstrates how anonymous functions can be used to encapsulate functionality without needing a named identifier.
package main
import "fmt"
func main() {
x := func(l int, b int) int {
return l * b
}(20, 30)
fmt.Printf("%T \n", x)
fmt.Println(x)
}
Output:
int
600
Explanation:
In this code, an anonymous function is immediately invoked with the arguments 20
and 30
. The function takes two integer parameters, l
and b
, and returns their product. The result of this function call, 600
, is assigned to the variable x
. The fmt.Printf
statement prints the type of x
, which is int
, since the function returns an integer. The fmt.Println
statement then prints the value of x
, which is 600
. This demonstrates how an anonymous function can be used and immediately executed to produce a result.
package main
import (
"fmt"
"strconv"
)
var (
square = func(n int) string {
s := n * n
return strconv.Itoa(s)
}
)
func main() {
result := square(5)
fmt.Printf("The square of 5 is: %s, and its type is: %T\n", result, result)
}
Output:
The square of 5 is: 25, and its type is: string
In this example, the anonymous function is assigned to the variable square
at the package level. This means it can be accessed from anywhere within the package. The function takes an integer n
, calculates its square, converts the result to a string using strconv.Itoa
, and returns it. The main
function then calls square
with the argument 5
and prints the result.
High order functions
- Function that receives a function as an arguments or returns a function as output.
Why to use high order function?
Composition
Creating smaller functions that takes care or certain piece of logic.
Composing complex function by using different smaller functions.
Reduces bugs
Code gets easier to read and understand
Use case (Basic)
package main
import "fmt"
func calcArea(r float64) float64 {
return 3.14 * r * r
}
func calcPerimeter(r float64) float64 {
return 2 * 3.14 * r
}
func calcDia(r float64) float64 {
return 2 * r
}
func main() {
var query int
var radius float64
fmt.Print("Enter the radius of the circle: ")
fmt.Scanf("%f", &radius)
fmt.Printf("Enter - \n 1 - Area \n 2 - Perimeter \n 3 - Diameter:\n")
fmt.Scanf("%d", &query)
if query == 1 {
fmt.Println("Result:", calcArea(radius))
} else if query == 2 {
fmt.Println("Result:", calcPerimeter(radius))
} else if query == 3 {
fmt.Println("Result:", calcDia(radius))
} else {
fmt.Println("Invalid query")
}
}
For example, if the user inputs a radius of 5
and chooses 1
for the area, the output will be:
Enter the radius of the circle: 5
Enter -
1 - Area
2 - Perimeter
3 - Diameter:
1
Result: 78.5
If the user chooses 2
for the perimeter with the same radius, the output will be:
Enter the radius of the circle: 5
Enter -
1 - Area
2 - Perimeter
3 - Diameter:
2
Result: 31.400000000000002
And if the user chooses 3
for the diameter:
Enter the radius of the circle: 5
Enter -
1 - Area
2 - Perimeter
3 - Diameter:
3
Result: 10
If the user enters an invalid option, the output will be:
Invalid query
package main
import "fmt"
func calcArea(r float64) float64 {
return 3.14 * r * r
}
func calcPerimeter(r float64) float64 {
return 2 * 3.14 * r
}
func calcDia(r float64) float64 {
return 2 * r
}
func printResult(radius float64, calcFunction func(r float64) float64) {
result := calcFunction(radius)
fmt.Println("Result:", result)
fmt.Println("Thank You!..")
}
func getFunction(query int) func(r float64) float64 {
queryToFunction := map[int]func(r float64) float64{
1: calcArea,
2: calcPerimeter,
3: calcDia,
}
return queryToFunction[query]
}
func main() {
var query int
var radius float64
fmt.Print("Enter the radius of the circle: ")
fmt.Scanf("%f", &radius)
fmt.Printf("Enter - \n 1 - Area \n 2 - Perimeter \n 3 - Diameter:\n")
fmt.Scanf("%d", &query)
if calcFunction := getFunction(query); calcFunction != nil {
printResult(radius, calcFunction)
} else {
fmt.Println("Invalid query")
}
}
Defer Statement
A defer statement delays the execution of a function until the surrounding function returns.
The deferred call’s arguments are evaluated immediately, but the function call is not executed until the surrounding function returns.
package main
import "fmt"
func printName(str string) {
fmt.Println(str)
}
func printRollNo(rno int) {
fmt.Println(rno)
}
func printAddress(adr string) {
fmt.Println(adr)
}
func main() {
printName("Joe")
defer printRollNo(24)
printAddress("streat-45")
}
Output:
Joe
streat-45
24
Explanation:
The
printName
function is called first, printing "Joe".The
defer
statement is used withprintRollNo(24)
, which means this function call is deferred until the surrounding function (main
) returns.The
printAddress
function is called next, printing "streat-45".After the
main
function completes its execution, the deferredprintRollNo(24)
is executed, printing24
.
The defer
keyword ensures that printRollNo
is executed last, even though it appears before printAddress
in the code.
References
Subscribe to my newsletter
Read articles from Arindam Baidya directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
