文章74
标签2
分类7

章节6_04_匿名函数

一.匿名函数

  • 匿名函数就是没有名称的函数
  • 正常函数可以通过名称多次调用,而匿名函数由于没有函数名,所以大部分情况都是在当前位置声明并立即调用(函数变量除外)
  • 匿名函数声明完需要调用,在函数结束大括号后面紧跟小括号
func (){
  
}()//括号表示调用
  • 匿名函数都是声明在其他函数内部

二. 匿名函数演示

  • 无参数匿名函数
  func main(){
     func(){
        fmt.Println("这是匿名函数")
     }()//括号表示调用
  }
  • 有参数匿名函数
func main() {
   func(s string) {
      fmt.Println(s, "这是匿名函数")
   }("传递参数") //调用时传递参数
}
  • 有参数有返回值匿名函数
func main() {
    r := func(s string) int {
        fmt.Println(s, "这是匿名函数")
        return 110
    }("传递参数") //调用时传递参数
    fmt.Println(r)
}

章节6_03_可变参数函数

一. 可变参数函数

  • Go语言支持可变参数函数
  • 可变参数指调用参数时,参数的个数可以是任意个
  • 可变参数必须在参数列表最后的位置,在参数名和类型之间添加三个点表示可变参数函数
func 函数(参数,参数,名称 ... 类型 ){
    //函数体
}
  • 输出语句就是可变参数函数,源码如下
func Println(a ...interface{}) (n int, err error) {
    return Fprintln(os.Stdout, a...)
}
  • 声明函数时,在函数体把可变参数当作切片使用即可

二.代码示例

  • 声明可变参数声明与调用
func main() {
    demo("看书", "写代码", "看佳明哥视频")
}

func demo(hover ... string) {
    for a, b := range hover {
        fmt.Println(a, b)
    }
}
  • 可变参数必须存在其他参数后面,一个函数不能有多个可变参数.

    • 因为前面普通参数个数是确定的,编译器知道,哪个实参给哪个形参
func main() {
    demo("张三", "看书", "写代码", "看佳明哥视频")
}

func demo(name string, hover ... string) {
    fmt.Println(name, "的爱好是")
    for a, b := range hover {
        fmt.Println(a, b)
    }
}

章节6_02_多返回值函数

一. 多返回值函数

  • 在Go语言中每个函数声明时都可以定义成多返回值函数
  • Go语言中所有的错误都是通过返回值返回的
  • 声明多返回值函数的语法
func 函数名(参数列表) (返回值,返回值){
  //函数体
}
  • 调用函数的语法
变量,变量:=函数名(参数)
  • 调用函数时如果不想接收可以使用下划线占位
变量,_:=函数名(参数)
  • 理论上函数返回值个数可以无限多个,但是一般不去定义特别多个返回值(用结构体代替多返回值)

二.代码演示

  • 函数的返回值可以不接收,表示执行函数
  • 函数的返回值如果接收,用于接收返回值的变量个数与返回值个数相同
  • 不想接收的使用占位符(_)占位
func main() {
    //不接收函数返回值
    demo()

    //每个返回值都接收
    a, b := demo()
    fmt.Println(a, b)

    //不希望接收的返回值使用下划线占位
    c, _ := demo()
    fmt.Println(c)
}

func demo() (string, int) {
    return "smallming", 17
}
  • 多返回值函数也可以给返回值定义变量,return后面就不需要编写内容
func demo() (name string, age int) {
    name = "smallming"
    age = 17
    return
}

章节6_01_函数的声明与调用

一. 函数

  • 函数:一段代码块
  • 所有的流程控制代码和业务代码都只能写在函数内部
  • 为什么使用函数:

    • 把一个功能提出作为一个函数便于后期维护,结构清晰
  • 函数声明的语法

    • 函数声明后不会执行,必须调用后才会执行
func 函数名(参数列表) 返回值{
  //函数体
}
  • 函数调用的语法
返回值:=函数名(参数)

二. 无参数无返回值函数

  • 函数可以有参数也可以没有参数,可以有返回值也可以没有返回值
func main() {
    demo1()
}
func demo1(){
    fmt.Println("执行demo1函数")
}

//上面代码等同于
//func main(){
//    fmt.Println("执行demo1函数")
//}

三.有参数函数

  • 函数的参数可以有多个,且每个参数类型都可以不同
  • 参数表示调用函数方想要给函数内部传递的值,给函数使用的.
  • 声明函数时的参数叫做形参数,调用函数时参数叫做实参
func main() {
    i:=5
    s:="smallming"
    show(s,i)
}
func show(name string,age int){
    fmt.Println("姓名:",name,"年龄",age)
}

四.有返回值函数

  • 函数的返回值是给调用方返回的数据,给调用方使用的.
  • 具有返回值的函数,必须要有return
func main() {
    a, b := 1, 2
    fmt.Println(add(a,b))
}
func add(c, d int) int {
    return c + d
}
  • 也可以在返回值类型前面添加变量,return关键字后不写内容,表示变量是什么返回值什么
func main() {
    a, b := 1, 2
    fmt.Println(add2(a,b))
}

func add2(c, d int) (sum int) {
    sum = c + d
    return
}

章节5_17_双向循环链表

一.双向循环链表

  • 循环链表特点是没有节点的指针域为nil,通过任何一个元素都可以找到其他元素
  • 环形链表结构如下
graph LR
    A -->B    
    B -->C    
    C -->D    
    D -->E
    E -->D
    A -->E
    E -->A
    D -->C
    C -->B
    B -->A    
  • 双向循环链表和双向链表区别

    • 双向循环链表没有严格意义上的头元素和尾元素
    • 没有元素的前连接和后连接为nil
    • 一个长度为n的双向循环链表,通过某个元素向某个方向移动,在查找最多n-1次后一定会找到另一个元素

二.Go语言中的双向循环链表

  • 在container/ring包下结构体Ring源码如下

    • 官方明确说明了Ring是循环链表的元素,又是环形链表.
    • 实际使用时Ring遍历就是环形链表第一个元素
// A Ring is an element of a circular list, or ring.
// Rings do not have a beginning or end; a pointer to any ring element
// serves as reference to the entire ring. Empty rings are represented
// as nil Ring pointers. The zero value for a Ring is a one-element
// ring with a nil Value.
//
type Ring struct {
    next, prev *Ring
    Value      interface{} // for use by client; untouched by this library
}
  • Go语言标准库中对container/ring包提供的API如下
    type Ring
        //实例化长度为n的环形链表
        func New(n int) *Ring
        //长度
        func (r *Ring) Len() int
        //下一个元素
        func (r *Ring) Next() *Ring
        //上一个元素
        func (r *Ring) Prev() *Ring
        //移动n次,支持负数
        func (r *Ring) Move(n int) *Ring
        //合并s和r
        func (r *Ring) Link(s *Ring) *Ring
        //删除r后面n%r.Len()元素,删除多个,当前元素前面的不删除
        func (r *Ring) Unlink(n int) *Ring
        //循环遍历,i是当前元素的值
        func (r *Ring) Do(f func(interface{}))

三.代码演示

  • 实例化、赋值、遍历
    r := ring.New(3)
    for i := 0; i < r.Len(); i++ {
        r.Move(i).Value = i
    }
    r.Do(func(i interface{}) {
        fmt.Println(i)
    })
  • 实例化后的r就是链表中第一个创建的元素.可以找到元素的前后元素
    fmt.Println(r.Next().Value)//输出:1
    fmt.Println(r.Next().Next().Value)//输出:2
    fmt.Println(r.Next().Next().Next().Value)//输出:0
    fmt.Println(r.Move(-1).Value)//输出:2
    fmt.Println(r.Prev().Value)//输出:2
  • 可以向环形链表添加或删除链表
    s := ring.New(1)
    s.Value = 13
    //r是哪个元素,就把新的链表添加到哪个元素后面
    r.Link(s)
    r.Do(func(i interface{}) {
        fmt.Print(i, " ")
    })
    fmt.Println("")
    //从r元素向后,n/r.Len()个元素被删除,当前元素和前面的保留
    r.Unlink(1)
    r.Do(func(i interface{}) {
        fmt.Print(i, " ")
    })

章节5_16_list代码演示

一.操作List

  • 直接使用container/list包下的New()新建一个空的List
    mylist := list.New()
    fmt.Println(mylist)       //输出list中内容
    fmt.Println(mylist.Len()) //查看链表中元素的个数
    fmt.Printf("%p", mylist)  //输出地址
  • Go语言标准库中提供了很多向双向链表中添加元素的函数
    //添加到最后,List["a"]
    mylist.PushBack("a")
    //添加到最前面,List["b","a"]
    mylist.PushFront("b") 
    //向第一个元素后面添加元素,List["b","c","a"]
    mylist.InsertAfter("c", mylist.Front()) 
    //向最后一个元素前面添加元素,List["b","c","d","a"]
    mylist.InsertBefore("d", mylist.Back()) 
  • 取出链表中的元素
    fmt.Println(mylist.Back().Value)  //最后一个元素的值
    fmt.Println(mylist.Front().Value) //第一个元素的值

    //只能从头向后找,或从后往前找,获取元素内容
    n := 5
    var curr *list.Element
    if n > 0 && n <= mylist.Len() {
        if n == 1 {
            curr = mylist.Front()
        } else if n == mylist.Len() {
            curr = mylist.Back()
        } else {
            curr = mylist.Front()
            for i := 1; i < n; i++ {
                curr = curr.Next()
            }
        }
    } else {
        fmt.Println("n的数值不对")
    }
    //遍历所有值
    for e := mylist.Front(); e != nil; e = e.Next() {
        fmt.Println(e.Value)
    }
  • 移动元素的顺序
    mylist.MoveToBack(mylist.Front()) //把第一个移动到后面
    mylist.MoveToFront(mylist.Back()) //把最后一个移动到前面
    mylist.MoveAfter(mylist.Front(),mylist.Back())//把第一个参数元素,移动到第二个参数元素后面
    mylist.MoveBefore(mylist.Front(),mylist.Back())//把第一个参数元素,移动到第二个参数元素前面
  • 删除元素
mylist.Remove(mylist.Front())

章节5_15_双向链表

一.双向链表概述

  • 双向链表结构如下
    双向链表
  • 双向链表结构中元素在内存中不是紧邻空间,而是每个元素中存放上一个元素和后一个元素的地址

    • 第一个元素称为头(head)元素,前连接(前置指针域)为nil
    • 最后一个元素称为尾(foot)元素,后连接(后置指针域)为nil
  • 双向链表的优点:

    • 在执行新增元素或删除元素时效率高,获取任意一个元素,可以方便的在这个元素前后插入元素
    • 充分利用内存空间,实现内存灵活管理
    • 可实现正序和逆序遍历
    • 头元素和尾元素新增或删除时效率较高
  • 双向链表的缺点

    • 链表增加了元素的指针域,空间开销比较大
    • 遍历时跳跃性查找内容,大量数据遍历性能低

二. 双向链表容器List

  • 在Go语言标准库的container/list 包提供了双向链表List
  • List结构体定义如下

    • root表示根元素
    • len表示链表中有多少个元素
// List represents a doubly linked list.
// The zero value for List is an empty list ready to use.
type List struct {
    root Element // sentinel list element, only &root, root.prev, and root.next are used
    len  int     // current list length excluding (this) sentinel element
}
  • 其中Element结构体定义如下

    • next表示下一个元素,使用Next()可以获取到
    • prev表示上一个元素,使用Prev()可以获取到
    • list表示元素属于哪个链表
    • Value表示元素的值,interface{}在Go语言中表示任意类型
  // Element is an element of a linked list.
  type Element struct {
      // Next and previous pointers in the doubly-linked list of elements.
      // To simplify the implementation, internally a list l is implemented
      // as a ring, such that &l.root is both the next element of the last
      // list element (l.Back()) and the previous element of the first list
      // element (l.Front()).
      next, prev *Element

      // The list to which this element belongs.
      list *List

      // The value stored with this element.
      Value interface{}
  }

章节5_14_map

一. map

  • map以散列表方式存储键值对集合
  • map中每个元素都是键值对
map[key]Value
  • key是操作map的唯一标准.可以通过key对map中元素进行增加/删除/修改/查看
  • key是唯一的,添加重复的key会覆盖之前的元素.
  • map是值类型,只声明时为空指针(nil)
    var m map[string]int
    fmt.Println(m == nil) //输出:true
    fmt.Printf("%p", m)   //输出:0x0
  • map读写数据时并不是并发安全的,可以结合RWMutex保证并发安全(RWMutex在后面讲解)

二.实例化map的几种方式

  • 使用make函数实例化一个没有初始值的map
    m := make(map[string]string)
    fmt.Println(m==nil)//输出:false
    fmt.Printf("%p", m)//输出:内存地址
  • 可以在声明map时直接给map赋初始值.注意初始值在一行和在多行写时的语法区别

    • map中元素键值对语法满足: key:value
    • key和value的类型必须和map[key]value类型严格对应
    m := map[string]string{"name": "smallming", "address": "北京海淀"}
    m1 := map[string]string{
        "name":     "smallming",
        "addresss": "北京海淀",
    }
    fmt.Println(m, m1)

三.操作map中的元素

  • 使用key判断,如果key不存在向map中新增数据,如果key存在会覆盖map中元素
    m := make(map[string]int)
    m["money"] = 5
    fmt.Println(m) //输出:map[money:5]
    m["money"] = 6
    fmt.Println(m) //map[money:6]
  • Go语言标准库中提供了对map元素删除的函数,使用顶层delete()即可完成删除

    • 如果key存在执行删除元素
    • 如果key不存在,map中内容不变,也不会有错误
    m := make(map[string]int)
    m["money"] = 5
    delete(m, "没有的key")
    fmt.Println(m) //输出:map[money:5]
    delete(m, "money")
    fmt.Println(m) //输出:map[]
  • 获取map中指定key对应的值

    • 使用:map变量[key]获取key对应的值
    • 如果key不存在返回map[key]Value中Value类型的默认值.例如:Value是string类型就返回""
    • 返回值可以是一个,也可以是两个.

      • 一个表示key对应的值
      • 两个分别表示:key对应的值和这个key是否存在
    m := map[string]string{"name": "smallming", "address": "北京海淀"}
    fmt.Println(m["name"]) //输出:smallming
    fmt.Println(m["age"])  //输出:空字符串
    value, ok := m["age"]
    fmt.Println(value, ok) //输出:空字符串 false
  • 如果希望把map中所有元素都遍历,可以使用for结合range实现
    m := map[string]string{"name": "smallming", "address": "北京海淀"}
    //range遍历map时返回值分别表示key和value
    for key, value := range m {
        fmt.Println(key, value)
    }

章节5_13_sort包

一.sort包

  • Go语言标准库中sort提供了排序API
  • sort包提供了多种排序算法,这些算法是内部实现的,每次使用sort包排序时,会自动选择最优算法实现

    • 插入排序
    • 快速排序
    • 堆排
  • sort包中最上层是一个名称为Interface的接口,只要满足sort.Interface类型都可以排序
// A type, typically a collection, that satisfies sort.Interface can be
// sorted by the routines in this package. The methods require that the
// elements of the collection be enumerated by an integer index.
type Interface interface {
    // Len is the number of elements in the collection.
    Len() int
    // Less reports whether the element with
    // index i should sort before the element with index j.
    Less(i, j int) bool
    // Swap swaps the elements with indexes i and j.
    Swap(i, j int)
}
  • Go语言标准库默认提供了对int、float64、string进行排序的API
  • 很多函数的参数都是sort包下类型,需要进行转换.

二.排序实现

  • 对int类型切片排序
    num := [] int{1, 7, 5, 2, 6}
    sort.Ints(num) //升序
    fmt.Println(num)
    sort.Sort(sort.Reverse(sort.IntSlice(num))) //降序
    fmt.Println(num)
  • 对float64类型切片排序
    f := [] float64{1.5, 7.2, 5.8, 2.3, 6.9}
    sort.Float64s(f) //升序
    fmt.Println(f)
    sort.Sort(sort.Reverse(sort.Float64Slice(f))) //降序
    fmt.Println(f)
  • 对string类型切片排序

    • 按照编码表数值进行排序
    • 多字符串中按照第一个字符进行比较
    • 如果第一个字符相同,比较第二个字符
    s := []string{"我", "我是中国人", "a", "d", "国家", "你", "我a"}
    sort.Sort(sort.StringSlice(s)) //升序
    fmt.Println(s)
    //查找内容的索引,如果不存在,返回内容应该在升序排序切片的哪个位置插入
    fmt.Println(sort.SearchStrings(s, "你是"))
    sort.Sort(sort.Reverse(sort.StringSlice(s)))
    fmt.Println(s)

章节5_12_copy函数

一.copy函数

  • 通过copy函数可以把一个切片内容复制到另一个切片中
  • Go语言标准库源码定义如下

    • 第一个参数是目标切片,接收第二个参数内容
    • 第二个参数是源切片,把内容拷贝到第一个参数中
// The copy built-in function copies elements from a source slice into a
// destination slice. (As a special case, it also will copy bytes from a
// string to a slice of bytes.) The source and destination may overlap. Copy
// returns the number of elements copied, which will be the minimum of
// len(src) and len(dst).
func copy(dst, src []Type) int
  • 拷贝时严格按照脚标进行拷贝.且不会对目标切片进行扩容

二.代码示例

  • 把短切片拷贝到长切片中
    s1:=[]int {1,2}
    s2:=[]int {3,4,5,6}
    copy(s2,s1)
    fmt.Println(s1)//输出:[1 2]
    fmt.Println(s2)//输出:[1 2 5 6]
  • 把长切片拷贝到短切片中
    s1:=[]int {1,2}
    s2:=[]int {3,4,5,6}
    copy(s1,s2)
    fmt.Println(s1)//输出:[3 4]
    fmt.Println(s2)//输出:[3 4 5 6]
  • 把切片片段拷贝到切片中
    s1:=[]int {1,2}
    s2:=[]int {3,4,5,6}
    copy(s1,s2[1:])
    fmt.Println(s1)//输出:[4 5]
    fmt.Println(s2)//输出:[3 4 5 6]

三.使用copy完成删除元素

  • 使用copy函数可以保证原切片内容不变
    s := []int{1, 2, 3, 4, 5, 6, 7}
    n := 2 //要删除元素的索引
    newSlice := make([]int, n)
    copy(newSlice, s[0:n])
    newSlice = append(newSlice, s[n+1:]...)
    fmt.Println(s)        //原切片不变
    fmt.Println(newSlice) //删除指定元素后的切片

章节5_11_通过数组产生切片_删除切片内容

一.通过数组产生切片

  • 定义数组后,取出数组中一个片段,这个片段就是切片类型
    names := [3]string{"老张", "佳明哥", "smallming"}
    s := names[0:2]     //包前不包后
    fmt.Printf("%T", s) //输出:[]string
    fmt.Println(s)      //输出:[老张 佳明哥]
  • 切片是指针,指向数组元素地址,修改切片的内容,数组的内容会跟随变化
    names := [3]string{"老张", "佳明哥", "smallming"}
    s := names[0:2] //包前不包后
    fmt.Printf("%p %p",s,&names[0])//输出的地址是相同的
    s[0] = "Go语言"
    fmt.Println(s)     //输出:[Go语言 佳明哥]
    fmt.Println(names) //输出:[Go语言 佳明哥 smallming]
  • 当切片内容在增加时

    • 如果增加后切片的长度没有超出数组,修改切片也是在修改数组
    • 如果增加后切片的长度超出数组,会重新开辟一块空间放切片的内容
    • 通过下面代码也正面了切片中内容存在一块连续空间(和数组一样)
    names := [3]string{"老张", "佳明哥", "smallming"}
    s := names[0:2] //包前不包后
    fmt.Printf("%p %p\n",s,&names[0])
    s[0] = "Go语言"
    s=append(s,"区块链")
    fmt.Println(s)     //输出:[Go语言 佳明哥 区块链]
    fmt.Println(names) //输出:[Go语言 佳明哥 区块链]
    fmt.Printf("%p %p\n",s,&names[0])//地址相同

    s=append(s,"超出了数组长度")
    fmt.Println(s)     //输出:[Go语言 佳明哥 区块链 超出了数组长度]
    fmt.Println(names) //输出:[Go语言 佳明哥 区块链]
    fmt.Printf("%p %p\n",s,&names[0])//切片地址改变

二.删除实现

  • Go语言标准库中没有提供删除的函数
  • 切片也可以取其中的一段形成子切片,利用这个特性可以实现删除效果
    num := []int {0,1,2,3,4,5,6}
    //要删除脚标为n的元素
    n:= 2
    num1 :=num[0:n]
    num1= append(num1,num[n+1:]...)
    fmt.Println(num1)

章节5_10_切片长度_容量_append

一.make函数

  • Go语言中可以使用make函数创建slice 、 map、 channel、 interface
  • 使用make函数定义无内容,但是不是nil的切片,意味着切片已经申请了内存空间

    • make(类型,初始长度[,初始容量])
    • 初始容量可以省略,默认和长度相等
    slice := make([]string, 0)     //长度为0的切片,没有第三个参数表示容量和长度相等
    slice1 := make([]string, 0, 2) //长度为0,容量为2
    fmt.Println(slice, slice1)
  • 长度表示切片中元素的实际个数,容量表示切片占用空间大小,且切片容量成倍增加.当增加到1024后按照一定百分比增加.

    • len(slice) 查看切片的长度
    • cap(slice) 查看切片的容量
    slice := make([]string, 0)     //长度为0的切片,没有第三个参数表示容量和长度相等
    slice1 := make([]string, 0, 3) //长度为0,容量为2
    fmt.Println(len(slice), cap(slice))
    fmt.Println(len(slice1), cap(slice1))

二.append()函数

  • append()在Go语言标准库中源码如下
// The append built-in function appends elements to the end of a slice. If
// it has sufficient capacity, the destination is resliced to accommodate the
// new elements. If it does not, a new underlying array will be allocated.
// Append returns the updated slice. It is therefore necessary to store the
// result of append, often in the variable holding the slice itself:
//    slice = append(slice, elem1, elem2)
//    slice = append(slice, anotherSlice...)
// As a special case, it is legal to append a string to a byte slice, like this:
//    slice = append([]byte("hello "), "world"...)
func append(slice []Type, elems ...Type) []Type
  • 可以向切片中添加一个或多个值,添加后必须使用切片接收append()函数返回值
    s := make([]string, 0)
    fmt.Println(len(s), cap(s))//输出:0 0
    s = append(s, "老张", "佳明哥")
    fmt.Println(len(s), cap(s))//输出:2 2
    s = append(s, "smallming")
    fmt.Println(len(s), cap(s))//输出:3 4
  • 如果添加一次添加多个值,且添加后的长度大于扩容一次的大小,容量和长度相等.等到下次添加内容时如果不超出扩容大小,在现在的基础上进行翻倍
    s := make([]string, 0)
    fmt.Println(len(s), cap(s)) //输出:0 0
    s = append(s, "老张", "佳明哥")
    fmt.Println(len(s), cap(s)) //输出:2 2
    s = append(s, "smallming")
    fmt.Println(len(s), cap(s)) //输出:3 4
    s = append(s, "4", "5", "6", "7", "8", "9")
    fmt.Println(len(s), cap(s)) //输出:9 9
    s = append(s,"10")
    fmt.Println(len(s), cap(s)) //输出:10 18
  • 也可以把一个切片的内容直接添加到另一个切片中.需要注意语法中有三个点
    s := make([]string, 0)
    s1 := []string{"smallming", "佳明哥"}
    s = append(s, s1...) //注意此处,必须有三个点
    fmt.Println(s)

章节5_09_切片声明与定义

一.切片

  • 切片的英文名称slice
  • 切片:具有可变长度相同类型元素序列.
  • 由于长度是可变,可以解决数组长度在数据个数不确定情况下浪费内存的问题.
  • 切片和数组声明时语法最主要的区别就是长度
    var slice []string  //切片
    var array [3]string //数组
  • 切片只声明时为nil,没有开辟内存空间,不能直接操作切片,需要先初始化

    • 注意:切片只能和nil进行判断是否相等
    var slice []string  //切片
    fmt.Println(slice==nil)//输出:true
    fmt.Printf("%p",slice)//输出:0x0

二.定义切片

  • 通过直接指定初始值定初始化一个切片变量
    names := []string{"smallming", "佳明哥"}
    fmt.Println(names)
  • 定义完切片后就可以通过切片对象[脚标]取出或修改切片中元素内容.语法和数组相同

三.切片是引用类型

  • 引用类型在变量之间赋值时传递的是地址.引用类型变量就是这个类型的指针.切片就是引用类型
  • 值类型在变量之间赋值时传递的是值的副本
    names := []string{"smallming", "佳明哥"}
    names1 := names
    names1[0] = "张"
    fmt.Println(names, names1)//输出:[张 佳明哥] [张 佳明哥]
    fmt.Printf("%p %p",names,names1)//地址相同

章节5_08_goto和label

一.goto

  • goto是Go语言中的一个关键字
  • goto让编译器执行时跳转到特定位置

    • Loop是标记名(Label),名称任意,习惯上名称为Loop
    fmt.Println("执行程序")
    i := 6
    if i == 6 {
        goto Loop
    }
    fmt.Println("if下面输出")
Loop:
    fmt.Println("loop")
  • 可以有多个,但是标签(Labal)定义了就必须使用
    fmt.Println("执行程序")
    i := 6
    if i == 6 {
        goto Loop
    }
    fmt.Println("if下面输出")
Loop:
    fmt.Println("loop")
Loop1: //报错:label Loop1 defined and not used
    fmt.Println("Loop1")
  • goto也可以用于跳出循环,执行指定标签位置代码
    for i := 0; i < 5; i++ {
        fmt.Println(i)
        if i == 2 {
            goto abc
        }
    }
    fmt.Println("for循环执行结束")
abc:
    fmt.Println("abc")
    fmt.Println("程序结束")

章节5_07_break和continue在for循环中的使用

一.continue

  • continue关键字控制结束本次 循环体 结束,执行表达式三.
    for i := 0; i < 5; i++ {
        fmt.Println("开始")
        if i == 2 || i == 3 {
            continue
        }
        fmt.Println("结束")
    }
  • 在双重for循环中continue默认影响最内侧循环,与最外层循环无关
func main() {
    for k := 0; k < 2; k++ {
        for i := 0; i < 3; i++ {
            if i == 1 {
                continue
            }
            fmt.Println(k, i, "结束")
        }
    }
  • Go语言执行标签写法,可以通过定义标签让continue控制影响哪个for循环
    myfor:for k := 0; k < 2; k++ {
        for i := 0; i < 3; i++ {
            if i == 1 {
                continue myfor
            }
            fmt.Println(k, i, "结束")
        }
    }

二. break

  • break可以中断for循环,无论for循环还有几次执行,立即停止
    for i := 0; i < 5; i++ {
        if i == 2 {
            break
        }
        fmt.Println(i)
    }
  • 在双重for循环中,break默认也影响到最近的for循环
    for i := 0; i < 2; i++ {
        for j := 0; j < 2; j++ {
            if j == 1 {
                break
            }
            fmt.Println(i, j)
        }
    }
  • break也可以通过定义标签,控制break对哪个for循环生效
    myfor:for i := 0; i < 2; i++ {
        for j := 0; j < 2; j++ {
            if j == 1 {
                break myfor
            }
            fmt.Println(i, j)
        }
    }

章节5_06_双重for循环实现冒泡排序

一. 双重for循环

  • 可以在循环中执行循环,称为双重for循环
  • 代码示例
    for i := 0; i < 2; i++ {
        for j := 0; j < 2; j++ {
            fmt.Println(i, j)
        }
    }
  • 上面代码中注意:

    • 只有当子循环完全执行结束才能再次执行外侧循环.因为循环体执行完才能执行表达式3

二.冒泡排序

  • 排序就是把一组数据按照特定的顺序重新排列.可以是升序,降序等
  • 冒泡排序利用双重for循环把最大(小)的值移动到一侧,每次可以判断出一个数据,如果有n个数组,执行n-1次循环就可以完成排序
  • 排序代码(升序还是降序主要是看if判断是大于还是小于)
    arr := [5]int{1, 7, 3, 6, 2}
    for i := 0; i < len(arr)-1; i++ {
        for j := 0; j < len(arr)-i-1; j++ {
            if arr[j] > arr[j+1] {
                arr[j], arr[j+1] = arr[j+1], arr[j]
            }
        }
    }
    fmt.Println(arr)

章节5_05_for循环

一.for循环

  • 循环:让程序多次执行相同的代码块
  • for循环是Go语言中唯一一个循环结构
  • for循环经典语法

    • 先执行表达式1
    • 执行表达式判断是否成立,如果成立执行循环体
    • 循环体执行完成后,执行表达式3
    • 再次执行表达式2,判断是否成立.
for 表达式1;表达式2;表达式3{
  //循环体
}
  • for循环用的最多的地方就是遍历数组或切片等

二.代码示例

  • 经典for循环结构中 , for关键字后面有三个表达式,且每个表达式都可以省略
    for i := 0; i < 5; i++ {
        fmt.Println(i)
    }
    //等价于
    j := 0
    for ; j < 5; {
        fmt.Println(j)
        j++
    }
  • for关键字后面也可以只有一个表达式,表示如果条件成立执行循环体代码
    for i := 0; i < 5; i++ {
        fmt.Println(i)
    }
    //等价于
    j := 0
    for j < 5 {
        fmt.Println(j)
        j++
    }

三.使用for循环遍历数组

  • 可以通过数组的长度判断循环结束条件遍历整个数组
    arr := [3]string{"smallming", "张", "佳明哥"}
    for i := 0; i < len(arr); i++ {
        fmt.Println(arr[i])
    }
  • for循环遍历数组等时常与range结合使用

    • range函数返回两个值,第一个是脚标,第二个是内容
    arr := [3]string{"smallming", "张", "佳明哥"}
    for i, n := range arr {
        //其中n=arr[i]
        fmt.Println(i, n)
    }

章节5_04_多维数组

一.二维数组

  • 二维数组表示一个数组变量中每个元素又是一个数组变量
  • 声明二维数组的语法
var arr [n][m]int
  • 使用 arr[n]时返回一个 arr[m]类型的数组
  • 图示
    二维数组

二.二维数组的使用

  • 声明并创建二维数组
    //写在同一行,最后一个元素后面不需要有逗号
    //arr6:=[3][3]int{{1,2,3},{4,5,6},{7,8,9}}
    arr := [3][3]int{
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}, //每个元素分行写,最后一个必须有逗号
    }
    arr0 := arr[0]
    fmt.Println(arr0[0], arr0[1], arr[2])
    //上面等效于下面的写法.习惯上多下下面的方式
    fmt.Println(arr[0][0], arr[0][1], arr[0][2])

三.多维数组

  • 理论上支持无限多维数组(只要内存够用)
  • 三维数组可以理解成,一维数组中每个元素都是二维数组.四维数组理解成,一个一维数组中每个元素都是三维数组
  • 无论是几维数组,都符号元素类型是相同的
  • 实际开发中多维数组使用并不多,在一定程度上,程序员使用多维数组很容易出错
    arr := [2][2][2]int{
        {
            {1, 2},
            {3, 4},
        },
        {
            {5, 6},
            {7, 8},
        },
    }
    fmt.Println(arr)
    //几维数组在操作元素时,数组变量后面就有几个[]
    fmt.Println(arr[0][1][1])