Go基础语法(二)

75 阅读8分钟

控制结构

if-else for swith select 并发相关

if else

块作用域

func fn(start int , end int) string {
   if distance := end - start; distance > 100 {
      retrun "太远了"
   }else {
     retrun "Yeah"  
   }
   //print(distance)
  }

for循环

func fn1(){
  for i:=0;i<10;i++{
     print(i)
  }
}
func fn2(){
  for i:=0;i<10;{
     print(i)
     i++
  }
}
func fn3(){
  i:=0;
  for i<10;{
     print(i)
     i++
  }
}
//不跟控制 代表后边为true  无限循环
func fn3(){
  for {
     print("hello")
  }
}

for range循环

for range 循环可以用来遍历数组,切片和map 也可用于 channel 遍历(并发控制)

map就是映射 一个key映射到一个值  
solidity中表示的更加的浅显  
复习solidity: 
mapping(uint8 => string) name
<---use--->
name[1] => "胖胖"

Go示例

package main

func main() {
    test5()
}

// 数组
func test() {
    arr := [3]string{"A", "B", "C"}
    for index, value := range arr {
       println(index, value)
    }
}

// 切片
func test2() {
    arr := []string{"A", "B", "C"}
    for index, value := range arr {
       println(index, value)
    }
}

// 忽略下标
func test3() {
    arr := []string{"A", "B", "C"}
    for _, value := range arr {
       println(value)
    }
}
func test4() {
    arr := []string{"A", "B", "C"}
    for index := range arr {
       println(index, arr[index])
    }
}

// 遍历map  映射
func test5() {
    m := map[string]string{
       "1": "A",
       "2": "B",
       "3": "C",
    }
    for key, value := range m {
       println(key, value)
    }
}

go也支持 break 与 continue

break立即终止循环

continue终止本次循环 继续下次循环

go 在switch中是不需要break的

switch也可以没有value 这种情况下case后边跟bool表达式 写法少见 应该做到case的每一个条件都是互斥的

switch {
  case age >= 18
       println("成年人")
  case age < 18
       println("未成年人")
}

Go的内置类型

数组 切片 map channel

数组

[cap]type

  1. 初始化要指定的长度(cap)
func main() {
   //直接初始化容量为3的数组,大括号只能少不能多
   a1 := [3]int{1, 2, 3}
   fmt.Printf("a1:%v,len:%d,cap:%d", a1, len(a1), cap(a1))
   println()
   //缺少的默认为 类型的默认值
   a2 := [3]int{1, 2}
   fmt.Printf("a2:%v,len:%d,cap:%d", a2, len(a2), cap(a2))
   println()
   var a3 [3]int
   fmt.Printf("a3:%v,len:%d,cap:%d", a3, len(a3), cap(a3))
   println()
   //数组不支持 append操作
   //下标越界会报错 出现 panic
}
  1. 可以直接初始化
  2. arr[i]下标的形式访问元素
  3. len和cap操作用户获取数组长度
  4. 使用for range来循环

切片

可以想象成 动态 数组 语法:[]type

  1. 直接初始化
  2. make初始化:make([]type,length,capacity)
  3. arr[i]式访问元素
  4. append最佳元素
  5. len获取元素数量
  6. cap获取七篇容量
  7. 推荐写法:s1 := make([]type,0,capacity)
  8. 使用for range来遍历
  9. 切片操作有限 不支持随机增删
  10. 直接子切片操作 共享底层数组

最佳实践:在初始化切片时要预估容量

package main

import "fmt"

func main() {
    //直接初始化 4个元素的切片
    s1 := []int{1, 2, 3, 4}
    fmt.Printf("s1:%v,len:%d,cap:%d \n", s1, len(s1), cap(s1))
    // 直接初始化 长度len(已有元素个数)3 容量为4
    s2 := make([]int, 3, 4)
    fmt.Printf("s2:%v,len:%d,cap:%d \n", s2, len(s2), cap(s2))
    //追加元素  但是没有扩容
    s2 = append(s2, 7)
    fmt.Printf("s2:%v,len:%d,cap:%d \n", s2, len(s2), cap(s2))
    //    追加元素扩容
    s2 = append(s2, 8)
    fmt.Printf("s2:%v,len:%d,cap:%d \n", s2, len(s2), cap(s2))
    // 只传入一个参数 len为4 cap为4
    s3 := make([]int, 4)
    fmt.Printf("s3:%v,len:%d,cap:%d \n", s3, len(s3), cap(s3))
    //    按照下标索引
    fmt.Printf("s3[2]:%d \n", s3[2])
    // 超出下标  返回panic
    fmt.Printf("s3[99]:%d", s3[99])
}

ChatGpt

在 Go 语言中,"slice"(切片)是一种数据结构,用于提供对数组的灵活、强大的视图。切片并不存储任何数据,它们只是对现有数组的引用。

切片的基本特性:

  1. 引用类型:切片是对底层数组的引用,意味着当你修改切片时,实际上是修改它底层的数组。
  2. 动态大小:切片的大小不是固定的,可以动态地增长或缩短。这是通过内建的 append 函数实现的。
  3. 灵活性:切片比数组更灵活,能够处理可变大小的数据集。

切片的组成:

  • 指针:指向数组中切片开始位置的指针。
  • 长度:切片中的元素数量。
  • 容量:从切片开始位置到底层数组结尾的元素数量。

创建和初始化切片:

  1. 从数组创建

    goCopy code
    arr := [5]int{1, 2, 3, 4, 5}
    slice := arr[1:4]  // 创建一个包含元素 2, 3, 4 的切片
    
  2. 使用 make 函数

    goCopy code
    slice := make([]int, 5)  // 创建一个长度和容量都是5的切片
    
  3. 字面量

    goCopy code
    slice := []int{1, 2, 3}  // 创建并初始化一个切片
    

操作切片:

  • 索引:就像数组一样,你可以通过索引来访问切片的元素。
  • 追加:使用 append 来增加切片的元素。
  • 切片:可以通过切片操作符来创建一个新的切片视图。

示例代码:

goCopy code
package main

import "fmt"

func main() {
    // 创建一个数组
    arr := [5]int{1, 2, 3, 4, 5}
    
    // 创建一个切片,包含数组的一部分
    slice := arr[1:4]

    // 打印切片内容
    fmt.Println(slice)  // 输出: [2 3 4]

    // 修改切片
    slice[0] = 20
    // 注意:这同样修改了原数组
    fmt.Println(arr)  // 输出: [1 20 3 4 5]
}

在这个例子中,我们创建了一个整型数组,然后从这个数组创建了一个切片。注意,修改切片时也会影响到原数组,因为切片是对数组的引用。

子切片

数组和切片都可以通过[start:end]的形式来获取子切片 左闭右开

  1. arr[start:end]
  2. arr[:end] 0 --> end
  3. arr[start:] [start,len(arr)]
package main

import "fmt"

func main() {
    originalSlice := []int{1, 2, 3, 4, 5}
    subSlice := originalSlice[0:3]
    subSlice[1] = 7
    println(originalSlice)
    println(subSlice)
    fmt.Printf("originalSlice: %d", originalSlice)
    fmt.Printf("subSlice: %d", subSlice)
}
//内存地址相同  共享底层数组
//[5/5]0xc00000e420
//[3/5]0xc00000e420  记住这里的子切片 也是指向的 同一个数组地址  而且 3/5 即len3 cap5 子切片cap超过5就会扩容
//originalSlice: [1 7 3 4 5]
//subSlice: [1 7 3]

核心:共享数组 子切片和切片会不会互相影响,就根据一点:他们是不是共享数组

如果他们的结构没有发生变化,就是共享结构变化就可能不是共享

结构变化:扩容

package main

import "fmt"

func main() {
    originalSlice := []int{1, 2, 3, 4, 5}
    subSlice := originalSlice[0:3]
    subSlice[1] = 7
    println(originalSlice)
    println(subSlice)
    fmt.Printf("originalSlice: %d \n", originalSlice)
    fmt.Printf("subSlice: %d \n", subSlice)
    subSlice = append(subSlice, 90)
    subSlice = append(subSlice, 10)
    subSlice = append(subSlice, 900)
    subSlice[0] = 17
    println(originalSlice)
    println(subSlice)
    fmt.Printf("originalSlice: %d \n", originalSlice)
    fmt.Printf("subSlice: %d", subSlice)

}
[5/5]0xc00000e420 
[3/5]0xc00000e420 -------3/5------      
originalSlice: [1 7 3 4 5]       | 
subSlice: [1 7 3]                |---> 子切片扩容
[5/5]0xc00000e420                | 
[6/10]0xc000012370 <-----6/10-----
originalSlice: [1 7 3 90 10]
subSlice: [17 7 3 90 10 900]
如果是父切片发生扩容也是一样的效果 

ChatGpt

在 Go 语言中,切片共享底层数组的机制可能在特定情况下会“失效”,即原切片和子切片不再共享同一个底层数组。这种情况通常发生在对切片进行扩容操作时。

何时会发生扩容

当你向切片添加元素,而这个切片的当前容量不足以容纳更多元素时,Go 会自动扩容这个切片。这意味着它会创建一个新的、容量更大的底层数组,并将现有元素复制到这个新数组中。

扩容导致共享失效的情况

  1. 使用 append 函数:当你对一个切片使用 append 函数,并且这个操作超出了切片的容量,会触发扩容。在这种情况下,append 会返回一个新的切片,这个新切片引用一个新的底层数组,而原切片仍然引用旧的数组。

    goCopy code
    originalSlice := []int{1, 2, 3}
    newSlice := append(originalSlice, 4, 5) // 如果 originalSlice 没有足够的容量,会创建一个新数组
    

    这里 newSlice 可能会有一个不同的底层数组,这取决于 originalSlice 的容量是否足够。

  2. 明确的扩容操作:通过使用 make 函数和拷贝操作来扩展切片的容量也会导致切片引用不同的底层数组。

    goCopy code
    originalSlice := []int{1, 2, 3}
    biggerSlice := make([]int, len(originalSlice), 10) // 创建更大容量的切片
    copy(biggerSlice, originalSlice) // 复制元素
    

    在这个例子中,biggerSlice 有自己的底层数组,不再与 originalSlice 共享数据。

如何避免共享失效

  • 要保持共享,必须避免对原切片或子切片进行可能导致扩容的操作。
  • 如果你需要修改切片并保持与原始切片的独立性,应该首先复制整个切片到一个新的底层数组。

结论

了解何时发生扩容及其对切片共享底层数组的影响,对于有效地使用 Go 中的切片机制是非常重要的。正确管理切片的容量和长度可以帮助你避免意外的数据共享问题,从而编写出更加可靠和高效的代码。

map (映射 key => value)

初始化:make方法 直接初始化 赋值使用中括号[]

m1 := map[string]string{"key":"value"}
m2 := make(map[string]string,4)  // 4 --> cap   也会扩容  
m2["key"] = "value"

有两个返回值:值 && 元素是否存在

如果只用一个返回值就是对应的元素

元素不存在就是对应类型的零值

m1 := map[string]string{"key":"value"}
val,ok := m1["key"]
val2,ok2 := m1["胖胖"]
// val2 就是string  默认类型 ""

len获取长度 遍历使用for 但是map的遍历时随机的 每次结果都不一样 删除元素使用delete

m2 := make(map[string]string,4)
m2["key2"] = "vaulue2"
prienln(len(m2))
for key ,value := rang m2{
  println(key,value)
}
delete(m2,"key2")