go example之旅(上)

Introduce

这是来自于go by example的例子,花了几天的时间写完了这些例子,感觉对我的帮助很大,对于初学者来说,我的建议还是先找本go的书从头到尾看一下,然后再来看这些例子,每个例子都手敲一遍,对你的帮助还是很大的。在敲这些例子的过程中,有一些疑问,也有一些知识的扩充,因此总结了本文。

你不知道的打印输出

在go中fmt包功能很强大,里面提供了Print,Println等打印方法,支持类似于C语言的格式化输出,最重要的是fmt包可以识别任意类型功能很强大。下面是利用fmt来打印一个自定义的结构体。

package main
import "fmt"


type custom_struct struct{
    name string
    age int
}


func main() {
    man := custom_struct{"zhang",10}
    vars := []string{"first","second","last"}
    fmt.Println(man)
    fmt.Println(vars)
}

上面的代码可以直接打印custom_struct,还可以直接打印数组等,功能还是很强大的,但是却很少有人知道,go语言在内部也有一套print,println方法用来打印输出,用的人很少,这些打印方法一般用于go内部debug使用,不支持打印自定义类型。

大容量的常量

在go语言中,数字可以直接写成指数形式,例如3e20,而且go中的常量可以接收任意精度的常量表达的值,例如下面这个例子。

package main
import "fmt"


func main() {
    const n = 5000
    const d = 3e100 / n
    fmt.Println(d)
}

常量d可以接收任意精度的值,除此之外常量还有另外一个特点就是无类型,可以通过显式的转换赋予其类型,或者通过上下文赋予常量类型,例如赋值操作,函数调用,那么常量的类型就变成了赋值操作符左侧的类型,或者变成了函数调用的形参类型了。

无所不能的for

在go中,for循环可以是C中的while循环,for循环,一个关键字完成所有的循环操作,还支持类似python中的range的循环操作。

func main() {
    i := 1
    nums := []int{2,3,4}
    //while循环
    for i <= 3 {
        fmt.Println(i)
        i = i + 1
    }

    //普通的for循环
    for j := 7; j <= 9; j++ {
        fmt.Println(j)
    }

    //基于range的循环
    for i,num := range nums {
        if num == 3 {
            fmt.Println("index:",i)    
        }    
    }

    //死循环
    for {
        fmt.Println("loop")
        break
    }
}

if还可以这样子

if语句大体上来看,和C中的if在使用上差别不大,只不过go语言中的if支持在条件的前面加上一个赋值表达式,刷新了我的三观,并且在go中没有像C中的那个问号表达式了,下面是带赋值表达式的if

func main() {
    //这里是可以前置一个赋值表达式的
    if num := 9; num < 0 {
        fmt.Println(num,"is negative")
    } else if num < 10 {
        fmt.Println(num,"has 1 digit")
    } else {
        fmt.Println(num,"has multiple digits")
    }
}

不一样的switch

自从学习了golang,不断刷新我三观,这和我之前接触的C/C++,Python差别不是一般的大啊,在go中switch也同时兼备了正常和不正常的地方正常使用的情况和C中的switch基本一样,但是go中的switch没有穿透的概念,意思就是说,匹配到指定的项后就结束停止了,不再向下。go中的switch是不需要显示的break的。除此之外在和C中的switch别无差别,但是这只是go switch正常的一面,不正常的一面则是go的switch支持多条件匹配,支持表达式的匹配。


    switch time.Now().Weekday() {
    case time.Saturday,time.Sunday:     //用逗号分割的多个条件,匹配任意一个都可
        fmt.Println("it's the weekend")
    default:
        fmt.Println("it's a weekday")
    }

    //类似与if/else的感觉
    t := time.Now()
    switch {
    case t.Hour() < 12:     //为true就执行下面的语句,为false就直接跳到default分支
        fmt.Println("it's before noon")
    case true:              //继续执行,如果这里是false,那么就跳转指向default分支
        fmt.Println("continue")
    default:
        fmt.Println("it's after noon")
    }

arrays和slice不是一回事

go 中的arrays和C中的数组是一样的概念,而slice和python的列表是一个概念,只不过go中的slice必须是同一类型的而已。arrays是固定长度而slice则不是固定长度,可以通过append方法向其追加元素,但是在go中却没有单独给slice配备一个关键字,这导致有的时候不知道怎么区分arrays和slice。

arrays := [5]int{1,2,3,4,5} //这是一个数组,指明长度了
slices := []int{1,2,3}      //这是一个slice
slices2 := make([]int,3)    //这样也可以是一个slice

那么在go中slice是如何实现的呢?在讨论这个问题的时候,让我们来先看看go中的数组的一些实现细节,因为slice就是对array的抽象。go中的数组和C中的数组类似,但还是有一些不一样的地方,比如go的数组变量代表的就是整个数组,而不是像像C那样是一个指针,指向数组的首元素,因此在go中赋值和传递一个数组变量会导致对数组中的全部元素进行拷贝。数组在定义的时候需要指定长度,也可以让编译器帮我们计算,通过如下代码实现:

b := [...]string{"test","dwqd"}

slice不需要指定长度,这是和数组最明显的区别,那么这个slice内部到底是什么样子的呢,是和c++中的vector一样吗? slice其实就是对array的一个包装和抽象,每个slice内部都会对应一个数组,这个slice维护一个指针指向这个数组的首元素,维护这个数组的长度len,还会记录数组的最大长度capacity。那么这个len和capacity是什么关系呢? 和C++的vector中的capacity一个意思吗?,恰恰不是。限于篇幅这里我不再过多介绍slice的内部实现细节了,给大家推荐一篇博客go-slice-and-internals

多返回值的奇妙体验

限于我之前一直写C,常常需要返回多个值的时候,通常都是借助与函数参数来实现,好在go提供了多返回值的能力。这样就极大了简化了代码,在返回结果的同时,也返回出现的error,但是需要记住的是,只能返回两个值哦,千万不要贪多。

受限的指针

之所以选择学习go,主要是感觉go是一个类C的语言把,熟悉的struct,熟悉的指针,但是go中的指针是受限的,不支持算术运算因为需要支持垃圾回收。通过指针作为函数参数,在函数内部修改会影响到原始的变量,默认情况下是值拷贝的,在函数内部修改不会影响原始变量。

像class的struct

go中的struct也是用来自定义数据类型的,需要需要以面向对象的方法来编程,就需要将方法和数据封装在一起,C中通过在struct中包含函数指针来实现,go则不然,原生支持,直接给struct定义方法。

package main

import "fmt"

type rect struct {
    width,height int
}

//给rect自定义的数据类型,定义了两个方法
func (r *rect) area() int {
    return r.width * r.height
}

func (r rect) perim() int {
    r.width = 100;
    return 2 * r.width + 2*r.height
}


func main() {
    r := rect{width: 10,height: 5}
    fmt.Println("area: ",r.area()) //r 是一个值类型,可是area的参数类型是指针啊,这居然可以调用 
    fmt.Println("perlim: ",r.perim())
    fmt.Println(r)

    rp := &r
    fmt.Println("area: ",rp.area())
    fmt.Println("perlim: ",rp.perim())

}

在上面的例子中给rect定义了两个方法,一个是指针作为第一个参数,另一个则是值作为参数,但是在使用的时候居然可以不加区别的使用。这其实都是go在背后帮我们做了转换,但是不影响最终的效果,指针作为参数的,在函数内部操作会直接影响到原有的变量。

©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值