闲搞(一),看看数组和切片随机数填充
func FillArray(t *[100]int) { for i := range *t { (*t)[i] = rand.Intn(100) } } func FillSlice(t []int) { for i := range t { t[i] = rand.Intn(100) } } func main() { var a [100]int FillArray(&a) fmt.Println(a) b := make([]int, 100) FillSlice(b) fmt.Println(b) }
对数组进行填充,FillArray还可以这样写,因为go语言默认会在函数内给你
解引用 ,不需要一直带着* 。func FillArray(t *[100]int) { for i := range t { t[i] = rand.Intn(100) } }
结果:
[60 41 59 18 98 58 28 89 20 63 39 85 62 34 1 71 7 5 64 72 11 78 21 19 90 83 62 4 2 43 87 22 76 85 33 57 22 15 19 35 61 85 53 71 15 95 12 3 71 65 78 95 21 55 5 43 4 23 59 98 98 95 9 78 72 90 24 8 92 48 45 97 50 43 36 1 15 25 63 46 10 59 74 71 33 80 42 8 71 37 32 94 8 40 48 2 80 69 19 8 2] [51 28 24 55 62 43 85 13 93 13 53 55 76 78 70 9 12 21 69 72 34 6 69 54 29 4 42 2 2 59 61 8 31 53 60 6 76 37 60 24 8 58 66 47 85 34 18 98 64 62 21 70 13 95 7 20 9 9 87 15 71 67 76 8 10 25 69 43 19 50 0 65 52 48 2 1 31 30 42 66 71 56 24 78 27 3 4 51 33 89 17 96 9 0 96 62 20 49 41 26 13 51 87]
闲搞(二)Golang并发编程==?
"并发编程不等于简单的创建一大堆goroutine,而是还要对这些goroutine进行有效的管理"。最简单的管理就是等待他们都结束,复杂一点的等待结束并返回结果(可使用channel)。相关的知识还涉及到context,可以看我写的context标准包探索。
//简单的等待填充随机数完成 func main() { wg := sync.WaitGroup{} nums := make([]int, 100) wg.Add(2) go func() { defer wg.Done() Fill(nums[:50]) }() go func() { defer wg.Done() Fill(nums[50:]) }() wg.Wait() fmt.Println(nums) } func Fill(n []int) { for i := range n { n[i] = rand.Intn(101) } } //结果 [97 21 86 33 79 81 95 38 90 3 15 0 39 52 41 34 17 88 80 91 76 80 48 29 30 89 62 81 68 60 24 97 43 93 36 11 86 72 46 33 2 79 29 12 50 13 40 56 29 40 17 34 27 75 1 22 73 51 99 75 24 68 63 43 37 42 18 26 91 83 42 93 20 90 18 19 73 58 3 45 55 8 5 84 13 55 38 36 25 66 47 67 24 30 88 12 65 6 23 60]
稍微加点等待结果的:
func main() { wg := sync.WaitGroup{} nums := make([]int, 100) wg.Add(2) go func() { defer wg.Done() Fill(nums[:50]) }() go func() { defer wg.Done() Fill(nums[50:]) }() wg.Wait() fmt.Println(nums) ch1 := make(chan int) ch2 := make(chan int) ch3 := make(chan int) go sum(nums[:33], ch1) go sum(nums[33:66], ch2) go sum(nums[66:], ch3) res1 := <-ch1 res2 := <-ch2 res3 := <-ch3 fmt.Printf("Avg:%f\n", float64(res1+res2+res3)/float64(len(nums))) fmt.Printf("Avg:%f\n", GetAvg(nums)) } func sum(n []int, c chan<- int) { total := 0 for _, val := range n { total += val } c <- total } func GetAvg(n []int) (avg float64) { total := 0 for _, val := range n { total += val } avg = float64(total) / float64(len(n)) return } func Fill(n []int) { for i := range n { n[i] = rand.Intn(101) } } //结果: [16 55 76 78 17 27 83 91 92 93 53 52 60 7 53 94 58 94 30 38 52 69 84 55 87 2 70 29 87 32 87 11 48 99 74 46 54 0 42 14 67 60 84 19 88 100 38 94 81 0 54 28 51 22 19 25 68 69 28 76 37 61 61 22 47 52 36 1 99 22 11 21 59 31 100 29 20 98 17 10 3 3 17 43 64 14 54 72 35 44 6 34 25 50 57 60 92 48 57 14] Avg:49.060000 Avg:49.060000
值得注意的是,这里
res1 := <-ch1
res2 := <-ch2
res3 := <-ch3
会等待通道将结果写入res_,所以不会有主进程退出的情况。刚好哈
闲搞(二)泛型?
golang的泛型在1.18版本正式落地,之前,泛型一直是个热度很高、但在整个 Go 社区中备受争议的话题。欧克,那就来康康。
比如我要对一个
整数切片进行insertfunc main() { var Int_Slice = []int{1, 2, 3} //我想在index=1的位置插入一个0,index=2的位置插入一个-1 Int_Slice = append(Int_Slice[:1], append([]int{0}, Int_Slice[1:]...)...) Int_Slice = append(Int_Slice[:2], append([]int{-1}, Int_Slice[2:]...)...) fmt.Println(Int_Slice) } //结果: [1 0 -1 2 3]
OK,看来结果是可行的,正确的。
那么接下来我想对一个
字符串切片进行insert。var String_Slice = []string{"1", "2", "3"} //我想在index=1的位置插入一个"0",index=2的位置插入一个"-1" String_Slice = append(String_Slice[:1], append([]string{"0"}, String_Slice[1:]...)...) String_Slice = append(String_Slice[:2], append([]string{"-1"}, String_Slice[2:]...)...) fmt.Println(String_Slice) //结果: [1 0 -1 2 3]
OK,看来也可以。
那如果我要insert很多次呢?你可能要说了,写成个函数呗。ok
func main() { var Int_Slice = []int{1, 2, 3} //我想在index=1的位置插入一个0,index=2的位置插入一个-1 Int_Slice = IntSliceInsert(Int_Slice, 1, 0) Int_Slice = IntSliceInsert(Int_Slice, 2, -1) fmt.Println(Int_Slice) var String_Slice = []string{"1", "2", "3"} //我想在index=1的位置插入一个"0",index=2的位置插入一个"-1" String_Slice = StringSliceInsert(String_Slice, 1, "0") String_Slice = StringSliceInsert(String_Slice, 2, "-1") fmt.Println(String_Slice) } func IntSliceInsert(target []int, pos int, val int) []int { target = append(target[:pos], append([]int{val}, target[pos:]...)...) return target } func StringSliceInsert(target []string, pos int, val string) []string { target = append(target[:pos], append([]string{val}, target[pos:]...)...) return target } //结果: [1 0 -1 2 3] [1 0 -1 2 3]
OK,也木有问题,需要注意的是,不能写成下面这种形式:
IntSliceInsert(Int_Slice, 1, 0) IntSliceInsert(Int_Slice, 2, -1) func IntSliceInsert(target []int, pos int, val int) { target = append(target[:pos], append([]int{val}, target[pos:]...)...) } //结果:这是因为Int_Slice如果这样传,len和cap都不会变,本身len还是3。 [1 0 -1]
这是函数写法,但如果我还有int8,int16,int64,float……bool…..,slice里还有map,甚至还有自己定义的结构体等等等等。那么就子子孙孙无穷匮也,即使咱是愚公也不行啊。
好,你可能要说我可以用any啊,any是interface{}的别名,可以接受任何类型。加上reflect,岂不妙哉!
下面是any+reflect的用法:
package main import ( "fmt" "reflect" ) func main() { // 定义一个包含不同类型值的切片 values := []interface{}{42, "hello", 3.14, true} // 遍历切片中的每个值,并打印其类型和值 for _, v := range values { // 获取值的类型 valueType := reflect.TypeOf(v) // 获取值的真实类型和值 value := reflect.ValueOf(v) // 打印类型和值 fmt.Printf("Type: %s, Value: %v\n", valueType, value.Interface()) } } //结果: Type: int, Value: 42 Type: string, Value: hello Type: float64, Value: 3.14 Type: bool, Value: true
ok,那我们是不是要这么写了?
func SliceInsert(target []any, pos int, val any) []any { ...... ...... ...... return target }
但是,这么写效率比较低,reflect固然很好,但它是大国重器,是用来造船造炮的,杀鸡焉用牛刀?
那么,我们就没有别的办法了?
好了,下面泛型登场了。
func SliceInsert[T any](target []T,pos int,val T)[]T { target = append(target[:pos],append([]T{val},target[pos:]...)...) return target }
var Int_Slice = []int{1, 2, 3} //我想在index=1的位置插入一个0,index=2的位置插入一个-1 Int_Slice = SliceInsert(Int_Slice, 1, 0) Int_Slice = SliceInsert(Int_Slice, 2, -1) fmt.Println(Int_Slice) var String_Slice = []string{"1", "2", "3"} //我想在index=1的位置插入一个"0",index=2的位置插入一个"-1" String_Slice = SliceInsert(String_Slice, 1, "0") String_Slice = SliceInsert(String_Slice, 2, "-1") fmt.Println(String_Slice) //结果: [1 0 -1 2 3] [1 0 -1 2 3]
ok,非常好,我们甚至可以将自定义的数据类型进行inset。
type Dzc struct { Name string Address string } func main(){ var Dzc_Slice = []Dzc{{"sam", "shanghai"}, {"amy", "beijing"}, {"john", "chengdu"}} Dzc_Slice = SliceInsert(Dzc_Slice, 1, Dzc{"Dengzhichao", "hangzhou"}) Dzc_Slice = SliceInsert(Dzc_Slice, 2, Dzc{"zhichao", "dezhou"}) fmt.Println(Dzc_Slice) } //结果: [{sam shanghai} {Dengzhichao hangzhou} {zhichao dezhou} {amy beijing} {john chen gdu}]
ok,是不是很可以?但是有很多人diss这个go的泛型,说太丑陋,啊以前没有,为了加个这个临时搞了个这么样的东西出来,不好等等。。我个人来说感觉go的泛型设计得还可以。
闲搞,sort?
看一下切片的sort,使用slices.sort()
可以看到这个其实是
slices包里的sort。package main import ( "fmt" "math/rand" "slices" ) func main() { slice := make([]int, 100) FillSlice_1(slice) fmt.Println(slice) slices.Sort(slice) fmt.Println(slice) } func FillSlice_1(s []int) { for index, _ := range s { s[index] = rand.Intn(101) } } /*结果 [13 7 73 57 64 49 18 57 45 85 2 56 17 49 16 12 61 20 18 46 8 65 72 86 93 3 96 41 8 33 49 54 89 27 43 31 68 87 71 49 50 46 100 8 32 25 59 69 17 83 65 71 44 53 8 20 86 93 5 65 27 62 9 89 0 70 1 88 8 44 42 91 98 87 82 62 93 66 10 7 8 24 25 88 75 57 20 29 0 80 40 71 91 14 22 87 76 27 30 13] [0 0 1 2 3 5 7 7 8 8 8 8 8 8 9 10 12 13 13 14 16 17 17 18 18 20 20 20 22 24 25 25 27 27 27 29 30 31 32 33 40 41 42 43 44 44 45 46 46 49 49 49 49 50 53 54 56 57 57 57 59 61 62 62 64 65 65 65 66 68 69 70 71 71 71 72 73 75 76 80 82 83 85 86 86 87 87 87 88 88 89 89 91 91 93 93 93 96 98 100] */
试验一下这句话:
When sorting floating-point numbers,NaNs areordered before other values.// 创建一个包含浮点数的切片 numbers := []float64{3.14, 1.0, math.NaN(), 2.0} // 打印排序前的切片 fmt.Println("Before sorting:", numbers) // 使用 Sort 函数对切片进行排序 slices.Sort(numbers) // 打印排序后的切片 fmt.Println("After sorting:", numbers) /*结果 Before sorting: [3.14 1 NaN 2] After sorting: [NaN 1 2 3.14] */
还可以通过sort包的Sort方法实现自定义排序
但是前提是数据类型必须实现了sort包中,Interface定义的三个方法。
type Interface interface { // Len is the number of elements in the collection. Len() int // Less reports whether the element with index i // must sort before the element with index j. // // If both Less(i, j) and Less(j, i) are false, // then the elements at index i and j are considered equal. // Sort may place equal elements in any order in the final result, // while Stable preserves the original input order of equal elements. // // Less must describe a transitive ordering: // - if both Less(i, j) and Less(j, k) are true, then Less(i, k) must be true as well. // - if both Less(i, j) and Less(j, k) are false, then Less(i, k) must be false as well. // // Note that floating-point comparison (the < operator on float32 or float64 values) // is not a transitive ordering when not-a-number (NaN) values are involved. // See Float64Slice.Less for a correct implementation for floating-point values. Less(i, j int) bool // Swap swaps the elements with indexes i and j. Swap(i, j int) }
ok,来实践一下
package main import ( "fmt" "math/rand" "sort" ) // 定义一个自定义类型 type People struct { name string age int32 address string } // 定义 Person 切片类型 type Person []People // 实现 sort.Interface 接口的 Len 方法 func (p Person) Len() int { return len(p) } // 实现 sort.Interface 接口的 Less 方法 func (p Person) Less(i, j int) bool { if p[i].age != p[j].age { return p[i].age >= p[j].age } if p[i].name != p[j].name { return p[i].name <= p[j].name } return p[i].address <= p[j].address } // 实现 sort.Interface 接口的 Swap 方法 func (p Person) Swap(i, j int) { p[i], p[j] = p[j], p[i] } func main() { // 创建一个 Person 切片 //person := make([]Person, 5) person := Person{ {"Amy", 20, "chengdu"}, {"Job", 20, "shanghai"}, {"Sam", 32, "shanghai"}, {"Sam", 32, "beijing"}, {"Adam", 50, "shanghai"}, } sort.Sort(person) fmt.Println(person) } /*结果 [{Adam 50 shanghai} {Sam 32 beijing} {Sam 32 shanghai} {Amy 20 chengdu} {Job 20 shanghai}] */
