[A Tour of Go Study Notes] 06 Range, Map, and Function
Understanding Go Range, Map, and Function using official tutorials
Entering the world of Golang, we first acquaint ourselves with basic Golang usage using the official tutorial: A Tour of Go
.
This article introduces Range, Map, and Function.
Range
The range
form of a for loop can iterate over a slice or map, similar to the foreach usage in other languages.
When iterating over a slice with a for loop, each iteration returns two values.
The first value is the element’s index, and the second value is a copy of the element’s content.
package main
import "fmt"
var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
func main() {
for i, v := range pow {
fmt.Printf("2**%d = %d\n", i, v)
}
}
/*
>>> 2**0 = 1
>>> 2**1 = 2
>>> 2**2 = 4
>>> 2**3 = 8
>>> 2**4 = 16
>>> 2**5 = 32
>>> 2**6 = 64
>>> 2**7 = 128
*/
If a value isn’t needed (usually the index), it can be ignored using _
.
Golang is sensitive to defining variables that aren’t used, which causes an error: key declared but not used
.
for i, _ := range pow
for _, value := range pow
If only the index is needed, the second variable can be ignored directly.
for i := range pow
Here’s an example that only retrieves the index:
package main
import "fmt"
func main() {
pow := make([]int, 10)
for i := range pow {
pow[i] = 1 << uint(i) // Sets the value to left shift the index, equivalent to 2 to the power of i
}
for _, value := range pow {
fmt.Printf("%d\n", value)
}
}
/*
>>> 1
>>> 2
>>> 4
>>> 8
>>> 16
>>> 32
>>> 64
>>> 128
>>> 256
>>> 512
*/
Map
In previous examples, whether an array or slice, numeric values were used as indexes, while maps can use strings as indexes.
They are used in a Key-Value
combination.
When a map is uninitialized or lacks assignments, the initial value of the map (zero value) is nil
.
A nil
map lacks keys and cannot have keys added.
Using the make
function initializes a map of a specified type and returns it.
package main
import "fmt"
type Vertex struct {
Lat, Long float64
}
var m map[string]Vertex
func main() {
m = make(map[string]Vertex)
m["Bell Labs"] = Vertex{
40.68433, -74.39967,
}
fmt.Println(m["Bell Labs"])
}
/*
>>> {40.68433 -74.39967}
*/
Maps are similar to structs, but keys are mandatory.
package main
import "fmt"
type Vertex struct {
Lat, Long float64
}
var m = map[string]Vertex{
"Bell Labs": {40.68433, -74.39967},
"Google": {37.42202, -122.08408},
}
func main() {
fmt.Println(m)
}
/*
>>> map[Bell Labs:{40.68433 -74.39967} Google:{37.42202 -122.08408}]
*/
If the top-level type is just a type name, it can be omitted.
package main
import "fmt"
type Vertex struct {
Lat, Long float64
}
var m = map[string]Vertex{
"Bell Labs": {40.68433, -74.39967},
"Google": {37.42202, -122.08408},
}
func main() {
fmt.Println(m)
}
/*
>>> map[Bell Labs:{40.68433 -74.39967} Google:{37.42202 -122.08408}]
*/
Modifying Values in Maps
Inserting or modifying elements in a map:
m[key] = elem
Retrieving an element:
elem = m[key]
Deleting an element:
delete(m, key)
Checking if a certain key exists using two-value assignment:
elem, ok = m[key]
If the key exists in m
, ok = true
; otherwise, ok = false
.
When reading a non-existent key, you get the default value of the map’s element type.
In cases where elem
or ok
aren’t defined during usage, :
can be used for short variable declaration.
elem, ok := m[key]
In practice:
package main
import "fmt"
func main() {
m := make(map[string]int)
m["Answer"] = 42
fmt.Println("The value:", m["Answer"])
m["Answer"] = 48
fmt.Println("The value:", m["Answer"])
delete(m, "Answer")
fmt.Println("The value:", m["Answer"])
v, ok := m["Answer"]
fmt.Println("The value:", v, "Present?", ok)
}
/*
>>> The value: 42
>>> The value: 48
>>> The value: 0
>>> The value: 0 Present? false
*/
Function
Function values
Functions are also values and can be passed around. They can be used as parameters or return values for functions.
package main
import (
"fmt"
"math"
)
func compute(fn func(float64, float64) float64) float64 {
return fn(3, 4)
}
func main() {
hypot := func(x, y float64) float64 {
return math.Sqrt(x*x + y*y)
}
fmt.Println(hypot(5, 12))
fmt.Println(compute(hypot))
fmt.Println(compute(math.Pow))
}
/*
>>> 13
>>> 5
>>> 81
*/
Function Closures
A Go function can also be a closure. A closure is a function value that references variables outside its body.
The closure function is assigned to a variable and can access the variables.
In other words, closure functions are “bound” to variables.
In the example, the adder
function returns a closure, so each sum
variable has its own closure function.
package main
import "fmt"
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
func main() {
pos, neg := adder(), adder()
for i := 0; i < 10; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
}
}
/*
>>> 0 0
>>> 1 -2
>>> 3 -6
>>> 6 -12
>>> 10 -20
>>> 15 -30
>>> 21 -42
>>> 28 -56
>>> 36 -72
>>> 45 -90
*/
This example demonstrates that the adder
function returns a closure, allowing each sum
variable to possess its own enclosed function.
Within the main
function, pos
and neg
leverage two distinct closure functions for operations.