Go语言入门 小白 教程详解

Go的变量与函数声明方式

//函数调用方法

a,b := swap(...)

//函数编写格式

func name(x, y string) (int, string){

    do something...

}

func name(x, y string) (c1 int, c2 string){ 

    return //此时return会返回 c1和c2

}

//变量声明方法
var i int
var i, j int = 1, 2
var i, f, s = 1, 1.23, "hello"
i, f, s := 1, 1.23, "hello"

//基本类型
bool, string, int, uint, float32, float64, complex64, complex 128

//!不同类型需要显式类型转换

//常量命名
const Pi = 3.1415926

Go的for循环,if, switch, defer语句

//for循环格式
for i:=0; i<10; i++{
    fmt.println(i)
}

//for循环缺省格式
for ;sum<1000;{
    sum+=sum
}

//Go的for循环缺省格式可以去掉‘;’,可以看作Go语言的while循环
sum := 1
for sum<1000{
    sum += sum
}

//Go的无限循环
for{
    do something
}

//if格式
if x<0{
    do something....
}

//if语句可在条件表达式前执行一个简单语句,该语句声明的变量作用域仅在if内
if v:=math.Pow(x,n); x<lim{
    do someting//v只能在次作用域内使用
}

//switch只能用于特定的Case,且每个分支后自动添加break语句
switch os:=runtime.GOOS; os{
    case "darwin":
        do something
    case "linux":
        do something
    default:
        do something
}

//没有条件的switch相当于switch true
//此种方法可以逻辑更清晰的表示一长串if...else...语句
switch{
    case t.Hour()<12:
        do something
    case t.Hour()<17:
        do something
    default:
        do something
}

//defer语句将函数推迟到外层函数返回之后执行,推迟调用的函数的参数立刻求值,但知道外层函数返回前,该函数都不会被调用
func main(){
    i := 5
    defer fmt.Println(i)
    i++
    fmt.Println("hello") //输出5
}

//!有多个推迟函数时,推迟函数会被依次压入一个栈中,外层函数返回后按照后进先出的顺序执行

//go语言指针与C语言相似
var p *int
i := 5
p = &i
*p = 12
fmt.Println(i)

Go结构体,数组,切片

//Go结构体定义
type Vertex struct{
    X int
    Y int
}
var(
    v1 = Vertex{1,2}
    v2 = Vertex{X:1}  //Y=0
    v3 = Vertex{}  //X=0, Y=0
    p = &Vertex{1,2}
)
func main(){
    fmt.Println(v1, (*p).X, v2, v3)
}

//Go数组定义
var a [2]string
primes := [6]int{2,3,4,5,6,7}
var s []int = primes[1:4] //[1,4)切片,其实相当于C语言里的数组指针
//切片并不储存任何数据,只相当于一个试图
//更改切片数据会修改底层数组中对应的元素,与它共享底层数据的切片也会随之变化

s:=[]struct{
    i int
    b bool
}{
    {2, true}
    {3, true}
    {5, false}
}

//切片长度 
len(s)
//切片容量
//切片容量是从切片的第一个元素开始数,到底层数组元素末尾的个数
cap(s)

//切片文法与数组文法不同
[3] bool{true, true, false} //数组文法
[] bool{true, true, false} //切片文法
//上述切片文法其实相当于构建了一个数组,然后构建一个引用它的切片
func printSlice(s []int)只能传切片文法

//切片零值为nil
var s []int   //s == nil

//make函数分配一个元素为零值的数组并返回一个引用了它的切片
a := make([]int, 5) //len(a)==5
a := make([]int, 0, 5) //len(a)==0, cap(a)==5

//切片中可以存切片,相当于c语言二维数组
board := [][]string{
    []string{"_", "_", "_"},
    []string{"_", "_", "_"},
}

//向切片追加元素
var s []int
s = append(s,0)
s = append(s,2,3,4)

Range

//for循环的range形式可以便利切片或映射
//当使用for循环遍历切片时,每次迭代会返回两个值,第一个为当前元素的下标,第二个值是所对应元素的一个副本
var pow = []int{1, 2, 4, 8}
func main(){
    for i, v:= range pow{
        fmt.Println(i,v)
    }
}

//只需要key的情况
for i := range pow{
    fmt.Println(i)
}
//只需要value的话,可以使用‘_‘来忽略它
for _, value := range pow{
    fmt.Println(value)
} 

映射

//Go的映射就是python的字典,c++的map
type Vertex struct{
    Lat, Long float64
}
var m map[string] Vertex //定义一个映射,现在这个映射为nil,没有键,也不能添加键
func main(){
    //make函数返回给定类型的映射,初始化备用
    m = make(map[string]Vertex)
    m["Bell"] = Vertex{
        3.8, 32.44,
    }
    fmt.Println(m["Bell"])
}
//map文法,此m不是nil,可以直接添加键
var m = map[string]Vertex{
	"Bell Labs": Vertex{
		40.68433, -74.39967,
	},
	"Google": Vertex{
		37.42202, -122.08408,
	},
}
//如果顶级类型只是一个类型名,可以在文法元素中忽略它
var m = map[string]Vertex{
	"Bell Labs": {40.68433, -74.39967},
	"Google":    {37.42202, -122.08408},
}
//插入或修改元素
m[key] = elem
//获取元素
elem = m[key]
//删除元素
delete(m,key)
//检查某个键是否存在
//如果key在m中,ok为true
//如果不在,ok为false,elem为映射元素的零值
elem, ok := m[key]

函数指针

func compute(fn func(float64, float64) float64) float64 {
	return fn(3, 4)
}

func main() {
	hypot := func(x, y float64) float64 {
		return math.Sqrt(x*x + y*y)
	}
	fmt.Println(compute(hypot))
	fmt.Println(compute(math.Pow))
}

函数闭包

//Go 函数可以是一个闭包。闭包是一个函数值,它引用了其函数体之外的变量。该函数可以访问并赋予其引用的变量的值,换句话说,该函数被“绑定”在了这些变量上。
func adder() func(int) int {
	sum := 0
	return func(x int) int {
		sum += x
		return sum
	}
}
//其实这就相当于static变量,pos函数公用一个sum变量,neg函数共用一个变量
func main() {
	pos, neg := adder(), adder()
	for i := 0; i < 10; i++ {
		fmt.Println(
			pos(i),
			neg(-2*i),
		)
	}
}

Go的方法

//Go中没有类,但是可以为结构体类型定义方法
//方法是带接收者参数的函数,位于func关键字和方法名之间
type Vertex struct {
	X, Y float64
}

func (v Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
	v := Vertex{3, 4}
	fmt.Println(v.Abs())
}
/*其实这个函数与
func Abs(Vertex v) float64{
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
没有区别*/

//指针接收者,这个就基本相当于C++类中的成员函数了,会直接更改成员变量的值
func (v Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func (v *Vertex) Scale(f float64) {
	v.X = v.X * f
	v.Y = v.Y * f
}
func main() {
	v := Vertex{3, 4}
	v.Scale(10)
	fmt.Println(v.Abs())
}
//注意v.Scale(10)这个函数,go编译时会将其变成(&v).Scale(10)
// p := &Vertex{3, 4}
// p.Scale(10)
//上面两行代码与v.Scale(10)在结果上没有什么区别

Go的接口

type Abser interface {
	Abs() float64
}

func main() {
	var a Abser
	f := MyFloat(-math.Sqrt2)
	v := Vertex{3, 4}

	a = f  // a MyFloat 实现了 Abser
	fmt.Println(a.Abs())
	
	a = &v // a *Vertex 实现了 Abser
	//由于a Vertex 没有实现 Abser,所以不能使 a = v
	fmt.Println(a.Abs())
	
}

type MyFloat float64

func (f MyFloat) Abs() float64 {
	if f < 0 {
		return float64(-f)
	}
	return float64(f)
}

type Vertex struct {
	X, Y float64
}

func (v *Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

//接口值,接口也是值,可以像其他值一样传递
//接口值可以用作函数的参数或者返回值
//在内部,接口值可以看作包含值和具体类型的元组(value, type)
type I interface {
	M()
}

type T struct {
	S string
}

func (t *T) M() {
	fmt.Println(t.S)
}

type F float64

func (f F) M() {
	fmt.Println(f)
}

func main() {
	var i I

	i = &T{"Hello"}
	describe(i)
	i.M()

	i = F(math.Pi)
	describe(i)
	i.M()
}

func describe(i I) {
	fmt.Printf("(%v, %T)\n", i, i)
}
/*OUTPUT
(&{Hello}, *main.T)
Hello
(3.141592653589793, main.F)
3.141592653589793
*/



//底层值为nil的接口值
//C语言,如果一个类指针为空,调用这个指针的方法会报错
//但是在Go语言中,仍然可以成功调用,不过需要特殊处理一下
//注意,保存了nil底层类型的接口,其本身并不是nil
func (t *T) M() {
	if t == nil {
		fmt.Println("This is <nil>")
		return
	}
	fmt.Println("yesyes:=")
}

func main() {
	var i I

	var t *T
	i = t
	describe(i)
	i.M()
}
/*OUTPUT
(<nil>, *main.T)
This is <nil>
*/

//如果接口值为nil,即没有指向任何变量。
//这时如果调用方法,会产生错误


//空接口可以保存任何类型的值
//空接口  interface{}
//空接口常用来处理未知类型的值
var i interface{}
i = 42
describe(i)



//类型断言
//类型断言提供了访问接口值底层具体值的方法
//t := i.(Type)
type T struct{
	a,b int	
}
func main() {
	var i interface{} = T{3,5}
	s, ok := i.(int)
	fmt.Println(s, ok)

	f, ok := i.(T)
	fmt.Println(f, ok)

	//f = i.(float64) // 报错(panic)
	//fmt.Println(f)
}


//接口类型选择
func do(i interface{}) {
	switch v := i.(type) {
	case int:
		fmt.Printf("Twice %v is %v\n", v, v*2)
	case string:
		fmt.Printf("%q is %v bytes long\n", v, len(v))
	default:
		fmt.Printf("I don't know about type %T!\n", v)
	}
}



/*fmt包中定义了Stringer接口
type Stringer interface {
    String() string
}
Stringer是一个可以用字符串描述自己的类型
*/
type Person struct {
	Name string
	Age  int
}

func (p Person) String() string {
	return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}

func main() {
	a := Person{"Arthur Dent", 42}
	z := Person{"Zaphod Beeblebrox", 9001}
	fmt.Println(a, z)
}
/*OUTPUT
Arthur Dent (42 years) Zaphod Beeblebrox (9001 years)
*/

//重写error输出
//定义自己的error类型
type ErrNegativeSqrt float64
//编写fmt.Println(error)
func (e ErrNegativeSqrt) Error() string {
	return fmt.Sprint(float64(e))
}
//在需要的时候抛出自己的error类型
func Sqrt(x float64) (float64, error) {
	if x >= 0{
		return 0, nil
	} else{
		return 22, ErrNegativeSqrt(88.77)
	}
}



//如果error为结构体类型,可以抛出error指针
type MyError struct {
	When time.Time
	What string
}
func (e *MyError) Error() string {
	return fmt.Sprintf("at %v, %s",
		e.When, e.What)
}
func run() error {
	return &MyError{
		time.Now(),
		"it didn't work",
	}
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值