Data Structures(2)
Mastering Go essentials: A deep dive into slice mechanics, map operations, and the power of functional closures.
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:到处传递,传来传去