Back to archive

Data Structures(2)

Mastering Go essentials: A deep dive into slice mechanics, map operations, and the power of functional closures.

SliceRangeMapFunction

Slices#

Nil slices#

The zero value of a slice is nil A nil slice has a length and capacity of 0 and has no underlying array.

creating a slice with make#

Slice can be created with the built-inmakefunction; this is how you create dynamically-sized arrays. The make function allocates a zeroed array and returns a slice that refers to that array.

	a := make([]int,5)
	//len(a)=5

To specify a capacity,pass a third argument to make : make : make only can create Slice, Map,Channel

Type Syntax How(If not make only var) Behavior ifnil
Slice make([]int, 5, 10) nil Relatively safe. you can append to it; Go will automatically allocate the underlying array.
Map make(map[string]int) nil DangerousReading is allowed (return zero value),but writing will trigger a runtime panic.
Channel make(chan int) nil Dangerous. Both reading and writing will block forever(leading to a deadlock).
different between new and make
	b := make([]int, 0, 5) // len(b)=0, cap(b)=5
	b = b[:cap(b)] // len(b)=5, cap(b)=5
	b = b[1:]      // len(b)=4, cap(b)=4

The code illustrates how re-slicing adjusts the slice's pointer and length.

Slices of slices#

Slices can contain any type, including other slices.

	func main(){
	board :=[][]string{
	[]string{"_","_","_"},
	[]string{"_","_","_"},
	[]string{"_","_","_"},
	}
	board[0][0] = "X"
	board[2][2] = "O"
	board[1][2] = "X"
	board[1][0] = "O"
	board[0][2] = "X"
	for i := 0; i < len(board); i++ {
		fmt.Printf("%s\n", strings.Join(board[i], " "))
	}
print: 
X _ X
O _ X
_ _ O

The code illustrates how to express multi-level arrays with slices

Appending to a slice#

append function: The documentation of the built-in package describes append

	func append(s []T,vs...T)[]T

The first parameter s of append is a slice of type T, and the rest are T values to append to the slice.

the rest?(...T) is a Variadic Parameters It allowed user deliver multiple values to function

The resulting value of append is a slice containing all the elements of the original slice plus the provided values.

Notably, since slices are passed by value (the slice header is copied), append must return an updated header to reflect changes in length and capacity.

We must assign the result if append back to the variable to ensure both the "Metadata"(len/cap) and the "Pointer" remain consistent with the latest state.

Slice Header & Pass-by-value:#

slice header is a lightweight structure. On a 64-bit OS, it occupies 24 Bytes (8* 3)

	type SliceHeader struct{
	Data uinptr        //Pointer to underlying array
	Len int            //length
	Cap int            //Capacity
	}

When passing a slice to a function, Go creates a copy of the slice header (Pass-by-value). This copy includes the pointer, Len, Cap, but excludes the actual data in the underlying array.

Consequences Case 1: Modifying Elements Modifying elements in the function will reflect inthe original slice. Why: Both the original header and the copied header point to the same underlying array. Case 2: Modifying Length or Capacity changing len or cap inside the function will Not affect the original slice. Why: You are only modifying the Len and cap fields in the local copy of the header. Case 3: Reallocation (The append trap) if append triggers a memory reallocation, the local pointer will point to a newly allocated array. The original slice header remains unchanged, still pointing to the old array.

Range#

Define#

The range from of the for loop iterates over a slice or map

When ranging over a slice, two values are returned for each iteration. The first is the index, and the second is a copy of the element at that index.

    for i,v := range pow {
    //iteration
    }
    tempSlice :=pow
    n :=len(tempSlice)

    for(i := 0; i<n; i++){
    v = tempSlice[i]
    }
    for i := range pow{
    fmt.Println(i)
    }
    for i, v := range pow{
    
    }
    for _, v := range pow{
    fmt.Print(v)
    }

range returns in different structure

Target first variable: i second variable: v
Array index(int) value
Map key value
String int rune/Unicode
Channel value
Notably, V is a local copy. Changing V does not affect the original slice.
Notably, when ranging over a Channel, it only returns one value (the element received from the channel).

Map#

Define#

A map maps keys to values.

The zero value of a map is nil . A nil map has no keys,nor can keys be added. The make function returns a map of the given type, initialized and ready for use

	type Vertex struct{
	Lat, Long float64
	}
	var m max[string]Vertex

	func main(){
	m = make(map[string]Vertex)
	m["Bell Labs"] = Vertex{
	40.68, -74.39,
	}
	fmt.Println(m["Bell Labs"])
	}

Map Literals#

Map literals are like struct literals, but the keys are required.

If the top-level type is just a type name, you can omit it from the elements of the literal.

	type Vertex struct{
	Lat, Long float64
	}
	var projections = map[string]Point{
	"home": Point{10, 20},
	"office": Point{30,40}.
	}
	var projections = map[string]Point{
	"home": {10,20},
	"office": {30,40}
	}

Mutating Maps#

Insert or update an element in map m: m[key] =elm Retrieve an element: elem = m[key] Delete an element delete(m, key) Test that a key is present with a two-value assignment: elem, ok = m[key] If key is in m, ok is true.If not, okis false If key is not in the map, then elem is the zero value for the map's element type. Notably,If elem or ok have not yet been declared you could user a short declaration form:elem, ok := m[key]

	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)
	}

Exercise: Map#

Implement WordCount. It should return a map of the counts of each “word” in the string s. The wc.Test function runs a test suite against the provided function and prints success or failure. You might find strings.Fields helpful. strings.Fields scans the string and treats each run of whitespace characters as a single separator . syntax: strings.Fields(s)

import(
	"fmt"
	"strings"
)
func main(){
	s := " I     love    Go    programming"
	words := strings.Fields(s)

	fmt.Printf("%q\n", words)
}
print: ["I" "love" "Go" "programming"]

string.Split scans the string and cuts the it at evey occurrence of the separator you provide. syntax string.Split(s,sep)

	data := "Apple, Banana, Orange"
	result := strings.Split(data, ",")

	print: ["Apple", "Banana", "Orange"]
	s := GO
	result :=strings.Split(s, "")
	print ["G", "o"]

If you pass in an empty string(""),it will split the string into individual characters.

feature strings.Fields(s) string.Split(s, ")
Delimiters Any whitespace(space,tab,newline,etc.) Only the exact string provided(e.g. " ")
Consecutive Spaces Treats a sequence of spaces as one separator Treats each space separately(creates empty strings)
Surrounding Spaces Automatically ignores leading and trailing spaces Creates empty string("") if space are at the start/end
Best used for Extracting words from natural language Parsing structured data with fixed delimiters
Result for" a b " ["a", "b"] ["","a","", "b",""
	import (
	"golang.org/x/tour/wc"
	"strings"
)
func WordCount(s string) map[string]int {
	m := make(map[string]int)
	for _, word := range strings.Fields(s){
	m[word]++
	}
	return m
}
func main() {
	wc.Test(WordCount)
}

Function values#

Functions are values too. They can be passed around just like other values. Function values may be used as function arguments and return values.

Function closures#

	func adder() func(int) int {
		sum := 0
		return func(x int) int{
			sum += x
			return sum
		}
	}

return func(x int)int{.....} is Anonymous Function ![[Function closures.png]]

Vocabulary List#

Operand:操作数
denote:意味着,代表着
underlying value:实际值,底层值
dereferencing:解引用
indirecting:间接寻址
field:字段
dot:指"."
cumbersome:笨重的、累赘的、低效的
Literal:字面量,常量
subset:子集
Newly allocated:新分配的
lead to:导致
Garbage collected:回收
derive from/create from:从大数组提取小切片一般用derive(派生)/slice from
memory allocator:术语,内存分配器
backed by:专业表达切片和底层数组的(被....支撑关系)
Extend a slice:重新切片
extend:延申,扩展
append:附加,追加
rest"the rest are T....":除了第一个以外,剩下的部分
pass by value:值的传递
side effect:副作用
Reflect:反映、生效
Local Copy;本地副本
shared memory: 共享内存
Reallocation: 重新分配
Modify:修改
Independently:独立的
implement :实现
Grayscale:灰度
mutate:修改
trigger:触发
runtime panic:运行时崩溃
block forever:永久阻塞
deadlock:死锁
Map of A to B/ Map of B 通常是代码里的键值关系
against:对照/碰撞,当to用
Validate a string against a regex:针对正则表达式验证字符串
Run a query against the database:针对数据库运行查询
Check the code against the style guide:对照代码规范检查代码
pass around:到处传递,传来传去