Day02:豆包MarsCode 技术训练营第二课Go语言入门指南 | 豆包MarsCode AI 刷题

83 阅读6分钟

1.1 进阶语法——函数

package main

import "fmt"

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

func exists(m map[string]string, k string) (v string, ok bool) {
	v, ok = m[k]
	return v, ok
}
func main() {
	fmt.Println(add(2, 10))
	fmt.Println(exists(map[string]string{"a": "A"}, "a"))
}

1.2 进阶语法——指针

  • 和C艹一样,如果想通过形参修改实参,只能通过指针操作更改地址的内容
package main

import "fmt"

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

func main() {
	n := 5
	add(n)
	fmt.Println(n)
	add2ptr(&n)
	fmt.Println(n)
}

1.3 进阶语法——结构体

package main

import "fmt"

type user struct {
	name     string
	password string
}

func main() {
	a := user{name: "wang", password: "1024"}
	b := user{"wang", "1024"}
	c := user{name: "wang"}
	c.password = "1024"
	var d user
	d.name = "wang"
	d.password = "1024"

	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 checkPassword(u user, password string) bool {
	return u.password == password
}

func checkPassword2(u *user, password string) bool {
	return u.password == password
}

1.3 进阶语法——结构体方法

package main

import "fmt"

type user struct {
	name     string
	password string
}

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
}
  • 把u user从形参列移动到func关键字后面,就变成了类成员函数

1.3 进阶语法——错误处理

package main

import (
	"errors"
	"fmt"
)

type user struct {
	name     string
	password string
}

func findUser(users []user, name string) (v *user, err error) {
	for _, u := range users {
		if u.name == name {
			return &u, nil
		}
	}
	return nil, errors.New("not found")
}
func main() {
	u, err := findUser([]user{{"wang", "123"}, {"li", "321"}}, "li")
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(u.name)
	if u, err := findUser([]user{{"wang", "123"}, {"li", "321"}}, "lijie"); err != nil {
		fmt.Println(err)
		return
	} else {
		fmt.Println(u.name)
	}
}
  • GO语言的异常检测可以在函数返回值中进行指定,可以很容易地知道哪一块的错误
  • 在findUser函数中,形参是增加了指针的user,是因为如果发生异常,返回值需要返回nil,结构体对象是值类型,所以需要增加指针。
  • 在这段代码中,最后的if语句和其他语言不太一样,更像for循环的语法一样,其中有个封号“;”。这是Go语言的一个特性,允许在条件部分声明并赋值变量,然后在条件判断之后使用这些变量。

1.4 进阶语法——字符串

package main

import (
	"fmt"
	"strings"
)

func main() {
	a := "hello"
	fmt.Println(strings.Contains(a, "ll"))                // 字符串是否被包含
	fmt.Println(strings.Count(a, "l"))                    // 查找指定字符串的数目
	fmt.Println(strings.HasPrefix(a, "hell"))             // 字符串是否以某个字符串打头
	fmt.Println(strings.Index(a, "ll"))                   // 查找字符串的位置索引
	fmt.Println(strings.Join([]string{"he", "llo"}, "+")) // 拼接列表
	fmt.Println(strings.Repeat(a, 3))                     // 重复多个字符串
	fmt.Println(strings.Replace(a, "e", "E", -1))         // 替换字符,-1表示替换所有的指定字符
	fmt.Println(strings.Split("a-2-3-6", "-"))            // 根据指定符号将字符串分割为列表
	fmt.Println(strings.ToUpper(a))                       // 字符串转大写
	fmt.Println(strings.ToLower(a))                       // 字符串转小写
	fmt.Println(len((a)))                                 // 输出字符串长度
}

1.4 进阶语法——字符串格式化

package main

import "fmt"

type point struct {
	x, y int
}

func main() {
	s := "hello"
	n := 123
	p := point{1, 2}
	fmt.Println(s, n) // hello 123
	fmt.Println(p)    // {1 2} 点数据类型

	fmt.Printf("s=%v\n", s)  // s=hello
	fmt.Printf("n=%v\n", n)  // n=123
	fmt.Printf("p=%v\n", p)  // p={1 2}
	fmt.Printf("p=%+v\n", p) // p={x:1 y:2}
	fmt.Printf("p=%#v\n", p) // p=main.point{x:1, y:2}

	f := 3.141592653
	fmt.Println(f)          // 3.141592653
	fmt.Printf("%.2f\n", f) // 3.14
}

  • go语言可以用%v格式化所有数据,不像c和py需要区分,采用“%+v”和“&#v”会格式化输出更详细的内容

1.5 进阶语法——JSON处理

package main

import (
	"encoding/json"
	"fmt"
)

// 属性名首字母需要大写
type userInfo struct {
	Name  string
	Age   int `json:"age"`
	Hobby []string
}

func main() {
	u := userInfo{Name: "LIHUA", Age: 18, Hobby: []string{"eat", "sleep", "play"}}
	buf, err := json.Marshal(u)
	if err != nil {
		panic(err)
	}
	fmt.Println(buf)
	fmt.Println(string(buf))

	buf, err = json.MarshalIndent(u, "", "\t")
	if err != nil {
		panic(err)
	}
	fmt.Println(string(buf))

	var b userInfo
	err = json.Unmarshal(buf, &b)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%#v\n", b) // main.userInfo{Name:"wang", Age:18, Hobby:[]string{"Golang", "TypeScript"}}
}
  • GO语言JSON转换可以直接在结构体中定义字段,然后利用json.Marshal将结构体转为JSON16进制的列表流,转为string后即转为了JSON字段,这个思路我很是喜欢。
  • 要注意,在结构体中定义属性的时候,首字母需要大写,否则转为JSON时,该属性值会消失。
  • 如果非要修改json的key,可以在定义结构体的时候,增加类似java的@JsonField的字符,简单好用。

1.6 进阶语法——时间处理

package main

import (
	"fmt"
	"time"
)

func main() {
	now := time.Now()
	fmt.Println(now)

	t := time.Date(2022, 3, 27, 1, 25, 36, 0, time.UTC)
	t2 := time.Date(2022, 3, 27, 2, 30, 36, 0, time.UTC)
	fmt.Println(t)
	fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second())
	fmt.Println(t.Format("2006-01-02 15:04:05"))
	diff := t2.Sub(t)
	fmt.Println(diff)

	t3, err := time.Parse("2006-01-02 15:04:05", "2022-03-27 01:25:36")
	if err != nil {
		panic(err)
	}
	fmt.Println(t3 == t)
	fmt.Println(now.Unix())
}

  • t.Format()方法内可以列举一个时间,然后t就可以转为该格式(批量操作很适用)
  • t.now()方法可以制造一个时间戳,一般可以作为防止重复命名的标记

1.6 进阶语法——数字解析

package main

import (
	"fmt"
	"strconv"
)

func main() {
	f, _ := strconv.ParseFloat("3.14159", 64)
	fmt.Println(f)
	n, _ := strconv.ParseInt("111", 10, 64) // 参数:字符串,进制,大小
	fmt.Println(n)
	n, _ = strconv.ParseInt("0x1000", 0, 64)
	fmt.Println(n) // 4096
	n2, _ := strconv.Atoi("123")
	fmt.Println(n2) // 123

	n2, err := strconv.Atoi("AAA")
	fmt.Println(n2, err)
}
  • strconv的包,可以理解为自
  • Atoi 函数用于将字符串转换为 int 类型,Itoa 函数则用于将 int 类型转换为字符串类型。

1.6 进阶语法——进程信息

package main

import (
	"fmt"
	"os"
	"os/exec"
)

func main() {
	fmt.Println(os.Args)
	fmt.Println(os.Getenv("PATH"))
	buf, err := exec.Command("grep", "127.0.0.1", "etc/hosts").CombinedOutput()
	if err != nil {
		panic(err)
	}
	fmt.Print(string(buf))
}

1.7 goto有益论

在60年代末和70年代初,关于GOTO语句的用法的争论比较激烈。主张从高级程序语言中去掉GOTO语句的人认为,GOTO语句是对程序结构影响最大的一种有害的语句,他们的主要理由是:GOTO语句使程序的静态结构和动态结构不一致,从而使程序难以理解,难以查错。去掉GOTO语句后,可直接从程序结构上反映程序运行的过程。这样,不仅使程序结构清晰,便于理解,便于查错,而且也有利于程序的正确性证明。

  • 我惊奇地发现,在go语言中居然有和C艹一样的goto语句,goto语句在专家看来会增加程序结构,增加代码阅读的难度,但是对于做题党来说:谁说这goto有害鸭?这goto可太棒啦!平时写算法题和一些功能脚本来说,goto尊嘟是省了太多事了。例如下面这个例子中,可以体现出goto的强大,可以随时跳转到程序的任何部位,包括已经执行过的程序的位置。
package main

import (
	"fmt"
	"time"
)

func main() {
	num:=10
	var input int
	gotoLabel:
	fmt.Scanf("%d", &input)
	if input == 1 {
		fmt.Println("input is 1")
		for i:=0;i<num;i++{
			//循环输入一组数字,判断是否是斐波那契数列,如果不是,则提示输入不合法,重新输入
		}
		if 结果不满足数列{
		fmt.Println("输入不合法,请重新输入")
		time.Sleep(2*time.Second)
		goto gotoLabel
	}
	} else if input == 2 {
		//循环输入一组数字,判断是否是等差数列,如果不是,则提示输入不合法,重新输入	
		//……………………
	}

	}