第六届字节跳动青训营第一课 | 豆包MarsCode AI 刷题

29 阅读8分钟

Go语言学习之路

Day1

Go语言基础语法

变量类型

常见变量类型包括 :字符串 整形 浮点型 布尔型,字符串可以通过加号拼接,也可以通过等号直接进行比较。

声明方式

var name string = '' '' 注:string所在位置为声明变量类型

变量 := 等于值

在Go语言中,使用:=操作符进行变量声明和赋值是一种简短的语法形式,称为短变量声明。它可以在函数内部声明变量并同时进行赋值操作。在这个特定的代码片段中

常量

将上文var改成const,且值得一说其没有确定的类型,会根据上下文进行推断。

if-else

与java类似感觉,但是条件判断中不用加括号,并且后面运行语句必须有大括号。

循环

go里面是没有while循环的,for循环后面什么也不写就是一个死循环,可以用break跳出,也可以正常操作‘。值得一提的是,虽然没有while但是效果是一样的。

switch

这个功能比较通俗易懂,不仅可以使用任意类型的变量,还可以直接在switch代码块前不加任何变量,里面进行case条件分支。

array数组

主要声明方式为如下

var a [5]int
b := [5]int{1,2,3,4,5}
slice 切片

切片可以随意更改长度,可以使用make来创建一个

s := make([]String,3)
s[0] = "a"

也可以用append来追加元素,但是要注意的是要将append的结果赋值给原数组

s = append(s,"d")

进行切片操作,与python操作类似,只是不支持负数索引

fmt.println(s[2,5])

切片的创建分为两种:一种是make函数进行或是使用切片字面量,前者同上,而后者则是直接指定了切片的元素如下:

good := []String{"g","o","o","d"} 

这样更加直观。

map

创建方式

m := make(map[String]int)//String为key,int为value
m["one"] = 1
m["two"] = 2
fmt.println(m)//map[one:1,two:2]
m2 := map[string]int{"one":1,"two":2}
var m3 := map[string]int{"one":1,"two":2}

可以用delete方法删除

另外在拓展一点make函数,一搬来说,通常用来创建切片,映射,通道这三种数据结构,使用make创建可以初始化一个新的映射并指定键值的类型以及初始容量,但是也可以使用映射字面量来创建并进行初始化映射·,可以不需要make来创建。

m2 := map[String]int{"one":1,"two":2}
var m3 := map[String]int{"one":1,"two":2}

最后可以使用 := 操作符和ok变量检查

r,ok := m["unknow"]
fmt.println(r,ok)//0,false
range遍历

对于一个map或者是slice,range可以快速遍历

nums := []int{1,2,3,4}//首先建立一个切片
sum := 0
for i,num := range,nums{
    sum += num
    if num == 2{
        fmt.println("index=",i,"num=",num)
    }
}
fmt.println(sum) //将打印出10
//注:如果i在代码块中未出现的话,for后面那个i要用_代替

对于数组会返回两个值,第一个是索引,第二个是对应位置的值,如果不需要索引,那么可以用下划线来代替。

对于map来说

m := map[string]int{"A":1,"B":2}
for k,v := range m{
    fmt.println(k,v)//A 1 B 2
}
foe k := range m{
    fmt.println("key",k)//key 1 key 2
}
函数

golang和许多语言不一样的是,变量类型是后置的,函数的原生支持返回多个值,但是几乎所有的函数都返回两个值,第一个是真正的结果,第二个是错误信息。

func add(a,b int) int {
    return a+b
}

上面这个返回值为int

func exists(m[string]string,k string)(v string,ok bool){
    v,ok = m[k]
    return v,ok
}

在这个函数中,函数的返回值可以通过函数签名中指定返回值的类型来定义。exists 函数的返回值被定义为 (v string, ok bool),这意味着函数将返回两个值:一个字符串 v 和一个布尔值 ok。在函数体中,v, ok = m[k] 这行代码尝试从映射 m 中获取键为 k 的值,并将其赋值给 vok。如果键 k 存在于映射 m 中,ok 将被设置为 true,否则 ok 将被设置为 false

在Go语言中,当函数返回多个值时,不需要在每个返回值的变量名前添加冒号 :。相反,你只需要在函数签名中指定返回值的类型,并在函数体中使用这些变量名来接收返回值。

因此,这里不加入冒号是因为Go语言的语法规定,当返回多个值时,不需要在变量名前添加冒号。

这样直接给一段代码看看区别

var r int
var ok bool
r, ok = m["unknow"]
fmt.Println(r, ok) // 0 false

r, ok := m["unknow"]
fmt.Println(r, ok) // 0 false

前者不需要加冒号,但是需要先声明变量r和ok,然后在进行赋值。在Go语言中,使用:=操作符进行变量声明和赋值是一种简短的语法形式,称为短变量声明。它可以在函数内部声明变量并同时进行赋值操作。

另外就是函数使用上,有时候像下面的传入参数为map和一个字符串,返回的类型为string和bool函数对应。

func main() {
	v, ok := exists(map[string]string{"a": "A"}, "a")
	fmt.Println(v, ok) // A True
}
指针

go的指针主要是对于传入参数进行修改

func add2(n int) {
	n += 2
}

func add2ptr(n *int) {
	*n += 2
}

func main() {
	n := 5
	add2(n)
	fmt.Println(n) // 5
	add2ptr(&n)
	fmt.Println(n) // 7
}
结构体

结构体是带类型的字段的集合,感觉就是Java里的对象

type user struct{
    name string
    password string
}
func main(){
    a := user{name : "111",password : "123"}
    b := user{"111","123"}
    c := user{name :"111"}
    c.password = "123"
    var d user
    d.name = "111"
    d.password = "123"
    fmt.Println(a, b, c, d)                 
    // {wang 1024} {wang 1024} {wang 1024} {wang 1024}
	fmt.Println(checkPassword(a, "haha"))   // false
	fmt.Println(checkPassword2(&a, "haha")) // false,
}
func checkPassword1(u user, password string) bool{
    return u.password == password
}
func checkPassword2(u *user, password string) bool{
    return u.password == password
}

注意&在代码中意思为取址操作符,意味在传递变量a的内存地址给函数checkPassword2。

函数1和函数2具体区别就在参数上,

  1. 使用方式
    • 调用checkPassword函数时,需要传递一个user结构体的实例和一个密码字符串。
    • 调用checkPassword2函数时,需要传递一个指向user结构体的指针和一个密码字符串。
  2. 功能差异
    • checkPassword函数只能检查传入的user结构体实例的密码是否与给定的密码匹配,它不能修改传入的user结构体实例。
    • checkPassword2函数不仅可以检查传入的user结构体实例的密码是否与给定的密码匹配,还可以修改传入的user结构体实例的密码字段,因为它接受的是一个指向user结构体的指针。

例如,如果你想在检查密码后重置用户的密码,你可以这样做:

func resetPassword(u *user, newPassworrd string){
    if checkPassword2(u,oldPassword){
        u.password = newPassword
    }else{
        fmt.printLn("cuowumima")
    }
}

结构体方法

就像是java中的成员方法一样,在实现结构体方法的时候有两种写法,就是带指针和不带指针,前者可以修改这个结构体,而后者仅仅是拷贝了一份,无法进行修改。具体操作方式就是在函数名前加一个括号,里面填写第一个参数

func (u user) checkPassword(password string) bool {
	return u.password == password
}

func (u *user) resetPassword(password string) {
	u.password = password
}

func main() {
	a := user{name: "wang", password: "1024"}
	a.resetPassword("2048")
	fmt.Println(a.checkPassword("2048")) // true
}
错误处理

在go语言中与Java的异常处理是不一样的,go的语言处理可以很清晰的知道哪个函数错了,并用简单的if-else来进行处理。在函数里的返回值类型加一个error,代表这个函数可能会返回错误,那么在函数返回需要同时返回两个值,要么出现错误的话,返回return nil和一个error,没有就正常结果和nil。

nil首先我们要来了解一下,是一个预定义的标识符,用于表示指针、接口、切片、映射、通道和函数类型的零值。具体来说:

  • 对于指针类型,nil 表示一个不指向任何有效内存地址的指针。

  • 对于接口类型,nil 表示一个不包含任何值的接口。

  • 对于切片、映射和通道类型,nil 表示一个未初始化的变量,即它还没有被赋予一个有效的实例。

  • 对于函数类型,nil 表示一个不指向任何函数的函数类型变量。

    func findUser(users []user,name string)(v *user,err error){
        for _,u := range users{
             if u.name == name{
            return &u,nli
           }
        }
        return nil,errors.New("not found")
    }
    func main(){
        u,err := findUser([]user{{"wang","123"}},"wang")
        if err != nil{
          fmt.println(err)
            return
        }
        fmt.println(u.name)
        if u, err := findUser([]user{{"wang", "1024"}}, "li"); err != nil {
    		fmt.Println(err) // not found
    		return
    	} else {
    		fmt.Println(u.name)
    	}
    }
    

    另外在拓展一下New这个内置函数,用于分配内存并返回一个指向新分配的类型为 T 的零值的指针。这里的 T 可以是任何类型,包括结构体、切片、映射等。

    例如,如果你有一个结构体类型 user,你可以使用 new 来创建一个该结构体的新实例,并返回一个指向它的指针:

    u := new(user)
    

    这将创建一个新的 user 结构体实例,并将其地址赋值给变量 u。此时,u 的值为 &user{},即一个指向结构体 user 的零值的指针。

    代码中,errors.New("not found") 调用了 errors 包中的 New 函数,该函数用于创建一个新的错误对象。errors.New 接受一个字符串参数作为错误消息,并返回一个指向新创建的错误对象的指针。这个错误对象可以被用来表示函数执行过程中发生的错误。