日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

自己读Go程序设计语言的一些总结(更新ing...)

發(fā)布時(shí)間:2023/12/2 编程问答 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 自己读Go程序设计语言的一些总结(更新ing...) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Go

本筆記用于記錄在閱讀Go程序設(shè)計(jì)語言的一些重要的知識(shí)點(diǎn)!并不完全!

第一章(入門)

命令行參數(shù)

在Go語言中可以直接go run 文件,也可以go build后會(huì)生成一個(gè)可執(zhí)行文件,直接使用該可執(zhí)行文件可以運(yùn)行g(shù)o文件。

#第一種 go run file.go #第二種 go build file.go ./file

下面用一個(gè)程序來理解如何獲得命令行參數(shù)

package chapter1import ("fmt""os" )/** 該程序用來模擬linux的echo,輸入后面帶的參數(shù) */ func main() {args := os.Argsvar s stringfor i := 1; i < len(args); i++ {s += args[i] + " "}fmt.Println(sy }

運(yùn)行效果

PS D:\goTrip\chapter1> go build .\echo1.go PS D:\goTrip\chapter1> .\echo1 aa bb cc aa bb cc

補(bǔ)充(for range)

上面的代碼沒有任何問題,但是go語言的for循環(huán)其實(shí)不止這個(gè)用法,他和java不同,不用死板的i++,而是可以像java的for(:)這樣。

首先我們得直到os.Args是一種string的切片,由于要實(shí)現(xiàn)函數(shù)的功能,我們得從index=1開始遍歷,所以我們只用取切片的[1:],現(xiàn)在來實(shí)現(xiàn)函數(shù)。

package mainimport ("fmt""os" )/** 該程序用來模擬linux的echo,輸入后面帶的參數(shù) */ func main() {args := os.Args var s stringfor _, val := range args[1:] {s += val + " "}fmt.Println(s) }

前面的下劃線是什么?

這個(gè)在go中代表忽略該值,因?yàn)間o語言創(chuàng)建了一個(gè)值但是不使用,會(huì)報(bào)錯(cuò),用了_,就可以不用也不會(huì)報(bào)錯(cuò)。

被省略的是index!

補(bǔ)充(i++)

go中的i++對i+1,他等價(jià)于i += 1,也等價(jià)于i = i + 1.但是這些是語句,并不是表達(dá)式,所以j = i++不是合法語句。

優(yōu)化(echo1)

在上面我們是通過循環(huán)通過追加舊的字符串來拼接命令行參數(shù)的,會(huì)將新的內(nèi)容賦值給s,然后舊的內(nèi)容垃圾回收。如果有大量的數(shù)據(jù)處理,這種代價(jià)會(huì)比較大,一種簡單并且高效的方式。

/** 該程序用來模擬linux的echo,輸入后面帶的參數(shù) */ func main() {args := os.Argsjoin := strings.Join(args[1:], " ")fmt.Println(join) }

找出重復(fù)行

有點(diǎn)像unix的dup,輸出輸入中次數(shù)大于1的行。

方式1(map+scanner)

第一個(gè)實(shí)現(xiàn)方式使用map+scanner實(shí)現(xiàn)。

package mainimport ("bufio""fmt""os" )func main() {record := make(map[string]int)s := bufio.NewScanner(os.Stdin)for s.Scan() {record[s.Text()] += 1}for k, v := range record {if v > 0 {fmt.Printf("dup line is %s\n", k)}} }

scanner是bufio包下的掃描器,它可以讀取輸入,以行或者單詞為單位斷開。有點(diǎn)像java的scanner,簡直一毛一樣。

方式2(流)

第二個(gè)實(shí)現(xiàn)方式用流的方式讀入,并且可以讀入文件或者鍵盤鍵入的文字。(通過命令行參數(shù)控制)

大概使用一個(gè)count函數(shù),參數(shù)1是*File,參數(shù)2是記錄的map

package mainimport ("bufio""fmt""io""os" ) func main() {args := os.Argsrecord := make(map[string]int)//若長度為1,說明后面沒有追加文件名字if len(args) == 1 {count2(os.Stdin, record)} else {for _, v := range args[1:] {open, err := os.Open(v)if err != nil {continue}count1(open, record)//記得關(guān)閉!!!open.Close()}}for k, v := range record {if v > 1 {fmt.Printf("%s:%d\n", k, v)}} } func count1(stream *os.File, record map[string]int) {reader := bufio.NewReader(stream)for {line, _, err := reader.ReadLine()if err == io.EOF {break}record[string(line)] += 1}return } func count2(stream *os.File, record map[string]int) {reader := bufio.NewScanner(stream)for reader.Scan() {if reader.Text() == "exit" {break}record[reader.Text()] += 1}return }

獲得多個(gè)URL

用http來get請求即可。

package mainimport ("fmt""io/ioutil""net/http""os" )func main() {for _, url := range os.Args[1:] {get, err := http.Get(url)if err != nil {fmt.Printf("err: %v\n", err)return}body := get.Bodyall, err := ioutil.ReadAll(body)if err != nil {fmt.Printf("err: %v\n", err)return}fmt.Println(string(all))} }

小作業(yè)1,用io.copy將b文件讀入到控制臺(tái)

package mainimport ("fmt""io""net/http""os" )func main() {for _, url := range os.Args[1:] {get, err := http.Get(url)if err != nil {fmt.Printf("err: %v\n", err)return}body := get.Bodyio.Copy(os.Stdout, body)if err != nil {fmt.Printf("err: %v\n", err)return}} }

并發(fā)獲得多個(gè)URL

管道是用來go協(xié)程間通信的,這個(gè)管道很有意思,他默認(rèn)需要兩個(gè)協(xié)程操作,比如一個(gè)協(xié)程對他進(jìn)行數(shù)據(jù)操作了,他會(huì)阻塞直到另一個(gè)協(xié)程對他進(jìn)行數(shù)據(jù)的寫入或者讀取。

package mainimport ("fmt""io""net/http""os""time" )func main() {c := make(chan string)start := time.Now()for _, url := range os.Args[1:] {go fetch(url, c)}//如果main先到則會(huì)阻塞等待,如果go協(xié)程先到則會(huì)將數(shù)據(jù)寫入channel,如果main沒取,也會(huì)阻塞for range os.Args[1:] {fmt.Println(<-c)}since := time.Since(start)fmt.Printf("%.2f has passed", since.Seconds()) }func fetch(url string, c chan string) {now := time.Now()get, err := http.Get(url)if err != nil {c <- fmt.Sprint(err)fmt.Printf("error,err:%v\n", err)}byte, err := io.Copy(io.Discard, get.Body)get.Body.Close()if err != nil {c <- fmt.Sprint(err)fmt.Printf("error,err:%v\n", err)}since := time.Since(now).Seconds()c <- fmt.Sprintf("%.2f %7d %s", since, byte, url) }

一個(gè)Web服務(wù)器

我們這邊通過http的listen方法,啟動(dòng)一個(gè)服務(wù)器

package mainimport ("fmt""net/http" )func main() {//路由http.HandleFunc("/", handler)//監(jiān)聽一個(gè)端口http.ListenAndServe("localhost:8000", nil) }func handler(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "url=%v\n", r.URL.Path) } PS D:\goTrip\chapter1> go build .\serve.go PS D:\goTrip\chapter1> ./serve PS D:\goTrip\chapter1> ./fetch http://localhost:8000/ url=/

可以看到我們上面發(fā)送的get請求,我們的服務(wù)器成功的收到了。

計(jì)數(shù)器

我們?yōu)檫@個(gè)服務(wù)器加上了一個(gè)計(jì)數(shù)器

package mainimport ("fmt""net/http""sync" )var count int var lock sync.Mutexfunc main() {//路由http.HandleFunc("/", handler)http.HandleFunc("/count", counts)//監(jiān)聽一個(gè)端口http.ListenAndServe("localhost:8000", nil) }func handler(w http.ResponseWriter, r *http.Request) {lock.Lock()count++lock.Unlock()fmt.Fprintf(w, "url=%v\n", r.URL.Path) }func counts(w http.ResponseWriter, r *http.Request) {lock.Lock()fmt.Fprintf(w, "count=%v\n", count)lock.Unlock() }

第二章(程序結(jié)構(gòu))

flag包

有以下兩種常用的定義命令行flag參數(shù)的方法。

flag.Type()

基本格式如下:

flag.Type(flag名, 默認(rèn)值, 幫助信息)*Type 例如我們要定義姓名、年齡、婚否三個(gè)命令行參數(shù),我們可以按如下方式定義:

name := flag.String("name", "張三", "姓名") age := flag.Int("age", 18, "年齡") married := flag.Bool("married", false, "婚否") delay := flag.Duration("d", 0, "時(shí)間間隔")

需要注意的是,此時(shí)name、age、married、delay均為對應(yīng)類型的指針。

flag.TypeVar()

基本格式如下: flag.TypeVar(Type指針, flag名, 默認(rèn)值, 幫助信息) 例如我們要定義姓名、年齡、婚否三個(gè)命令行參數(shù),我們可以按如下方式定義:

var name string var age int var married bool var delay time.Duration flag.StringVar(&name, "name", "張三", "姓名") flag.IntVar(&age, "age", 18, "年齡") flag.BoolVar(&married, "married", false, "婚否") flag.DurationVar(&delay, "d", 0, "時(shí)間間隔")

通過以上兩種方法定義好命令行flag參數(shù)后,需要通過調(diào)用flag.Parse()來對命令行參數(shù)進(jìn)行解析。

支持的命令行參數(shù)格式有以下幾種:

  • -flag xxx (使用空格,一個(gè)-符號(hào))
  • --flag xxx (使用空格,兩個(gè)-符號(hào))
  • -flag=xxx (使用等號(hào),一個(gè)-符號(hào))
  • --flag=xxx (使用等號(hào),兩個(gè)-符號(hào))

代碼

package mainimport ("flag""fmt""strings" )var (n = flag.Bool("n", false, "忽略結(jié)尾的換行符")sep = flag.String("sep", " ", "替換輸出參數(shù)時(shí)候用的分隔符") )func main() {flag.Parse()fmt.Print(strings.Join(flag.Args(),*sep))if !*n{fmt.Println()} }

可以發(fā)現(xiàn)我們使用flag所解析的參數(shù)*,因?yàn)閒lag.Bool和flag.String是返回的指針,我們需要取指針的值要用到 *

new

new用于產(chǎn)生某一個(gè)類型的指針,他是預(yù)定義的函數(shù),不是關(guān)鍵字,所以如果在某一函數(shù)指定了名為new的變量,我們就用不了new函數(shù)了。

func demo(new int){//用不了new }

變量的生命周期

如果是包范圍的變量,他的生命周期和我們程序的生命周期一樣。

如果是局部變量,它從被聲明創(chuàng)建開始,直到無法被訪問。垃圾回收器怎么知道它是否無法被訪問呢?有點(diǎn)像Java的GC Root,將每一個(gè)包級(jí)別的變量和每一個(gè)函數(shù)的局部變量,都當(dāng)成一個(gè)源頭,以該通過指針和其他引用方式找到變量,如果變量沒有路徑能夠到達(dá),說明無法被訪問。

即分析變量的可到達(dá)性。

變量逃逸分析

在go語言中,一般沒有發(fā)生逃逸的話,局部變量會(huì)申請??臻g,包級(jí)別的變量會(huì)申請堆空間,但是函數(shù)內(nèi)的局部變量可能發(fā)生逃逸。

例如下面的情況,最終go會(huì)通過逃逸分析將d分配到堆空間上。

type Data{//... } func escape() *Data{d := &Data{}return d }

包的別名

可以在函數(shù)上方對包進(jìn)行一個(gè)別名,如下方例子,雖然Celsius和Fahrenheit底層都是Float64,但是他們是無法相互轉(zhuǎn)換的。

也不能一起計(jì)算,得做類型轉(zhuǎn)型。

package main //攝氏度 type Celsius float64 //華氏度 type Fahrenheit float64const(AbsoluteZero Celsius = -273.15Freezing Celsius = 0Boiling Celsius = 100 )func CToF(c Celsius) Fahrenheit{//不可以//var f Fahrenheit = Freezingreturn Fahrenheit(c*9/5 + 32) }func FToC(f Fahrenheit) Celsius{return Celsius((f-32)*5/9) }func main() {}

作用域

一般會(huì)分為系統(tǒng)預(yù)留的值比如int,true,包級(jí)別:別入在函數(shù)外定義的var,局部:函數(shù)內(nèi)或者語句塊內(nèi)定義的。

go在查找變量的引用,會(huì)從內(nèi)層(局部->系統(tǒng))去查找,所以如果函數(shù)內(nèi)定義了一個(gè)val,但是又有一個(gè)全局的val會(huì)優(yōu)先使用局部變量。

對于for循環(huán)很有意思,他會(huì)創(chuàng)建兩個(gè)語法塊,一個(gè)是循環(huán)體的顯示語法塊,一個(gè)是隱式塊,包含了初始化的變量i,i++等。

所以他會(huì)出現(xiàn)一個(gè)很奇怪的現(xiàn)象,就是一個(gè)for里面可能有兩個(gè)i

作用域也會(huì)導(dǎo)致許多奇怪的問題,比如有一個(gè)包級(jí)別的屬性叫val,我們調(diào)用某個(gè)函數(shù)也會(huì)得到val,詳細(xì)見下圖。

這里我們其實(shí)想讓全局的val初始化,但是err和val都沒在函數(shù)內(nèi)聲明,只能用:=,不然err會(huì)爆紅,表示找不到這個(gè)err,此時(shí)我們可以這樣

第三章(基本數(shù)據(jù))

整數(shù)

Go具有有符號(hào)整數(shù)和無符號(hào)整數(shù)。并且每個(gè)整數(shù)都有8,16,32,64位,分別對應(yīng)int8,int16,int32,int64(無符號(hào)整數(shù) uint8,uint16,uint32,uint64).

對于n位有符號(hào)整數(shù),他的取值范圍位-(2^(n-1) - 1) ~ 2^(n-1) - 1.

即對于int8他的取值范圍是-128~127

對于n位無符號(hào)整數(shù),他的取值范圍是0~2^n - 1

即對于uint8他的取值范圍是0~255

由于整數(shù)有他的數(shù)值范圍,所以完全是可能溢出的,我們以范圍最小的uint8來演示一下溢出的情況。

位運(yùn)算

直接看程序吧。

package mainimport "fmt"func main() {var x uint8 = 1<<1 | 1<<5 //"10001"var y uint8 = 1<<1 | 1<<2 //"00011"fmt.Printf("%08b\n", x)fmt.Printf("%08b\n", y)fmt.Printf("%08b\n", x&y)fmt.Printf("%08b\n", x|y)fmt.Printf("%08b\n", x^y)//就等同于x&(^y)fmt.Printf("%08b\n", x&^y)for i := uint8(0); i < 8; i++ {//統(tǒng)計(jì)x的二進(jìn)制形式有幾個(gè)1if (x>>i)&1 != 0 {fmt.Println(i)}} }

關(guān)于go的一些內(nèi)置函數(shù)為什么返回有符號(hào)整數(shù)

比如go的len函數(shù)就是返回有符號(hào)整數(shù),為什么要這么做,因?yàn)槲覀冎纔int的數(shù)值范圍是0~?,所以按道理來說無符號(hào)整數(shù)應(yīng)該恒大于等于0,那在開發(fā)中我們經(jīng)常會(huì)寫出這種代碼。

for i := len(list);i >= 0;i--{//logic }

假設(shè)返回的是uint,那么i也隨之變成uint,那i–一輩子不可能小于0,會(huì)陷入死循環(huán)。因此無符號(hào)整數(shù)常常只用于位運(yùn)算或者算術(shù)運(yùn)算符。

浮點(diǎn)數(shù)

go中支持兩種大小的浮點(diǎn)數(shù)float32,float64.

字符串

字符串是不可變的字節(jié)序列。它可以包含任何數(shù)據(jù),包括零值字節(jié)。

例如下面的代碼,可以看到雖然str改變了,但是t持有的str的舊值仍沒有改變

由于str不可變,所以str內(nèi)部的值也不能變

不可能出現(xiàn)str[0] = '0’這種情況。

字符串字面量

字符串的值可以直接寫成字符串字面量,形式上就是代雙引號(hào)的字節(jié)序列。

string的幾個(gè)標(biāo)準(zhǔn)包

4個(gè)標(biāo)準(zhǔn)包對字符串操作特別重要:bytes,strings,strconv,unicode

byetes包內(nèi)有用于操作字節(jié)slice的方法。由于字符串不可變,因此按增量方式構(gòu)建字符串會(huì)導(dǎo)致多次內(nèi)存分配,用bytes.Buffer會(huì)更高效。

strconv包含將字符串轉(zhuǎn)換的函數(shù),課轉(zhuǎn)換為布爾值,整數(shù),浮點(diǎn)數(shù)等函數(shù)。

unicode具有判別文字符號(hào)值特性的函數(shù),比如isDigit(是否是數(shù)字),isLetter(是否是字母)

strings包中具有一些操作字符串的函數(shù),類似于bytes。

basename demo

首先不借助任何庫。

func basename(str string) string {//從后往前找到最后一個(gè)/for i := len(str) - 1; i >= 0; i-- {if str[i] == '/' {str = str[i+1:]break}}var index intfor i := len(str) - 1; i >= 0; i-- {if str[i] == '.' {index = ibreak}}return str[:index] }

借助lastIndex

func basename1(str string) string {//從后往前找到最后一個(gè)/index := strings.LastIndex(str, "/")end := strings.LastIndex(str, ".")return str[index+1 : end] }
對每三個(gè)數(shù)字就插入一個(gè),

不用庫函數(shù),遞歸

package mainimport "fmt"func main() {fmt.Println(foo("123456789")) }func foo(str string) string {if len(str) <= 3 {return str}return str[:3] + "," + foo(str[3:]) }
模擬打印數(shù)組

有點(diǎn)像java的數(shù)組的tostirng,在這里是用bytes的buf接受字符串,[]和數(shù)字,這里值得注意的是fpringf的writer得傳一個(gè)指針。+

package mainimport ("bytes""fmt" )func intsToString(arr []int) string {var buf bytes.Bufferbuf.WriteString("[")for i, v := range arr {if i > 0 {buf.WriteString(", ")}fmt.Fprintf(&buf, "%d", v)}buf.WriteString("]")return buf.String() }func main() {ints := make([]int, 0, 3)ints = append(ints, 1)ints = append(ints, 2)ints = append(ints, 3)fmt.Println(intsToString(ints)) }
字符串轉(zhuǎn)數(shù)字

數(shù)字—》字符串

i := 1 s1 := fmt.Sprintf("%d", i) s2 := strconv.Itoa(i)

字符串–》數(shù)字

atoi, err := strconv.Atoi("123") parseInt, err := strconv.ParseInt("123", 10, 64)

parseInt第二個(gè)參數(shù)代表進(jìn)制,第三個(gè)參數(shù)代表位數(shù)。

第四章(復(fù)合數(shù)據(jù)類型)

數(shù)組

數(shù)組的長度是固定的,所以在go中很少使用數(shù)組。更多的會(huì)使用切片。

var a [3]int var a [3]int = {1,2,3} a := [...]int{1,2,3}

這里可以注意,如果此時(shí)用print(“%T\n”,a) 得到的類型會(huì)是[3]int,但是[3]int和[4]int是不一樣的。

如果數(shù)組的元素是可以比較的,那么數(shù)組也是可以比較的。

因?yàn)閿?shù)組如果容量過大,作為方法的參數(shù),會(huì)復(fù)制一個(gè)副本,這是十分消耗資源的。此時(shí)可以用指針,傳的就是引用。

切片

切片是一種可變長度的序列。切片中的類型是唯一的,例如[]T,切片中都是T類型。

切片有三個(gè)屬性,指針,長度,容量。長度是指slice中的元素的個(gè)數(shù),它不能大于容量。容量是一般是第一個(gè)到最后一個(gè)元素的長度。

但是值得注意的是切片不能用==來比較,對于字節(jié)切片,可以直接調(diào)用bytes.equal,但是其他的就需要我們自己寫函數(shù)來比較了。

對于切片,如何判斷是一個(gè)空的切片,要用len(切片),如果用切片==nil,會(huì)發(fā)生一種情況,比如剛剛make出來的切片,此時(shí)是沒有元素的,但是不等于nil,他本來是空的。

通過make創(chuàng)建切片,可以有兩種方法。

make([]int,len) make([]int,len,cap)

對于第一種返回的是整個(gè)數(shù)組的引用,而第二種是數(shù)組中l(wèi)en元素的引用,但是cap會(huì)為以后的slice留出空間。

append

growslice

首先將當(dāng)前容量進(jìn)行一個(gè)double,判斷是否大于舊容量。

若小于,則說明沒有初始化,所以直接將newCap賦值給數(shù)組即可。

如果當(dāng)前的容量小于1024,會(huì)進(jìn)行一個(gè)double,否則會(huì)進(jìn)行一個(gè)1+1/4的擴(kuò)容。

由于我們調(diào)用append并不知道是否會(huì)發(fā)生內(nèi)存重分配,即并不知道是返回的原數(shù)組還是新數(shù)組,所以我們一般使用append函數(shù)都會(huì)將返回的切片賦值給原來的切片。

這句話可以由下面代碼很清楚的看出來,下面代碼大概是取出一個(gè)string切片的所有空字符串

所以建議用下面這種方式

或者直接操作切片

remove掉切片中index的值

可以用copy直接覆蓋掉index后的值,然后返回len-1的切片即可,詳細(xì)可以看代碼

func remove(s []string, i int) []string {copy(s[i:], s[i+1:])return s[:len(s)-1] }

reverse

package mainimport "fmt"func reverse(arr *[]int) {A := *arrl1 := len(*arr) - 1l := len(*arr)/2 - 1for i := 0; i <= l; i++ {A[i], A[l1-i] = A[l1-i], A[i]} }func main() {arr := []int{1, 2, 3, 4, 5}fmt.Println(arr)reverse(&arr)fmt.Println(arr) }

map

map是一種key,val鍵值對的存儲(chǔ)結(jié)構(gòu),其中的key和val都是可以用比較的類型,所以在加入map的時(shí)候可以通過判斷map中是否有key。

值得注意的是,我們無法取map的地址,當(dāng)我們用&map[‘bob’]的時(shí)候會(huì)編譯報(bào)錯(cuò),為什么呢?

我認(rèn)為是因?yàn)閙ap中的元素并不是永久的,可能隨著map的擴(kuò)容,導(dǎo)致該位置上的元素rehash到了其他位置,可能會(huì)讓存儲(chǔ)地址無效。

如何有序的遍歷map

比如我們map是map[string]int,此時(shí)有一個(gè)需求,需要按照字典的順序讀取map,如何做呢?

看下面代碼,可以先對key排序,然后根據(jù)排序后的key去字典中取

package mainimport ("fmt""sort" )func main() {dict := make(map[string]int)dict["b"] = 1dict["a"] = 1dict["c"] = 1l := make([]string, 0, 3)for k := range dict {l = append(l, k)}sort.Strings(l)for _, v := range l {fmt.Printf("%s:%d\n", v, dict[v])} }

如何判斷map中是否有元素?

比如我們map是map[string]int,如果去到一個(gè)不存在的key,會(huì)自動(dòng)返回默認(rèn)值,但是萬一有一個(gè)key剛好val=0呢?那他明明存在,我們的邏輯判斷成了不存在。

所以得用下面的方式判斷:

if _, ok := dict["d"]; !ok {fmt.Println("不存在") }

我想要切片作為key怎么辦

上面說過,對于map來說key是需要能通過==來判斷相等的,那萬一我就想要讓切片作為key呢?

我們可以間接的完成這個(gè)需求

dict := make(map[string]int)//先創(chuàng)建一個(gè)string為key的map func trans(s []string)string{return fmt.Sprintf("%q",s) //將切片轉(zhuǎn)換成string類型 } func Add(list []string){dict[trans(list)] += 1 }

基本上所有的需求,都可以用上面的方法完成。當(dāng)然也不一定非要是字符串類型,任何可以得到想要結(jié)果的可以使用==的結(jié)構(gòu)都可以。

map是支持這種的map[string]map[string]string.

結(jié)構(gòu)體

結(jié)構(gòu)體將零個(gè)或者多個(gè)任意類型的命名變量組合在一起成為一個(gè)聚合的數(shù)據(jù)類型。

type Book struct{Name string //...... } var b book

對于b,我們可以通過 . 來取出屬性,例如b.Name = ‘Go入門’,在go中也可以對指針類型的結(jié)構(gòu)體使用 .

var b *book = &Book{Name: 'aaa'} b.Name = 'bbb' //等價(jià)于(*b).Name = 'bbb'

需要注意的地方

加入某個(gè)函數(shù)能夠返回結(jié)構(gòu)體,必須得返回結(jié)構(gòu)體指針才可以對結(jié)構(gòu)體的屬性進(jìn)行操作,否則會(huì)找不到變量

正解:

結(jié)構(gòu)體無法包含他自己,但是可以是指針

當(dāng)我們學(xué)習(xí)算法的時(shí)候,經(jīng)常需要自己手寫一個(gè)簡單的二叉樹數(shù)據(jù)結(jié)構(gòu)。下方會(huì)報(bào)錯(cuò)

換成指針即可。

寫一個(gè)小demo試試,二叉樹排序

package mainimport "fmt"type Tree struct {val intleft, right *Tree }func sort(vals []int) {var root *Treefor _, v := range vals {root = add(root, v)}s := make([]int, 0, len(vals))s = TreeToSlice(s[:0], root)fmt.Println(s) }func add(node *Tree, val int) *Tree {if node == nil {t := new(Tree)t.val = valreturn t} else {if node.val < val {node.right = add(node.right, val)} else {node.left = add(node.left, val)}return node} }func TreeToSlice(s []int, root *Tree) []int {if root != nil {s = TreeToSlice(s, root.left)s = append(s, root.val)s = TreeToSlice(s, root.right)}return s }func main() {sort([]int{3, 1, 2, 4, 5}) }

如果結(jié)構(gòu)體屬性首字母是小寫,別的包無法引用

比如下面這種情況

a,b都是不可導(dǎo)出的,雖然上面代碼沒有顯示的引用a,b,但是他們被隱式的引用了,這也是不允許的。

結(jié)構(gòu)體之間的比較

如果結(jié)構(gòu)體的所有屬性都是可以比較的,那么結(jié)構(gòu)體也是可以比較的。

既然結(jié)構(gòu)體是可以比較的,說明結(jié)構(gòu)體是可以當(dāng)成map的key的。

匿名成員

比如我們模擬一下人類這個(gè)類。

如果我們把所有屬性寫在一個(gè)結(jié)構(gòu)體,例如

type Human struct {Name stringAge intSchoolName stringWorkPlace string }

這樣結(jié)構(gòu)會(huì)比較不清晰,我們可以把一些部分抽出來。

type Human struct {Name stringAge intSchoolWork } type School struct {SchoolName string } type Work struct {WorkPlace string }

Go允許我們定義不帶名字的結(jié)構(gòu)體成員,只需要指定他的類型。這些結(jié)構(gòu)體成員叫做匿名成員。

第五章(函數(shù))

在GO中是值傳遞,即如果實(shí)參傳入的是非指針的類型,函

數(shù)會(huì)創(chuàng)建一個(gè)副本,對副本進(jìn)行修改不會(huì)影響到本體,但是如果是引用類型,那么函數(shù)使用形參可能會(huì)間接修改本體。

異常

函數(shù)所發(fā)生的異常我們必須考慮,有以下幾個(gè)方法

1.遇到異常,返回異常打印異常

if err != nil{return nil,fmt.Errorf("%v",err) }

2.設(shè)置超時(shí)時(shí)間并且重試

const timeount = 1 * time.Minute deadline := time.Now().Add(timeout) //重試邏輯 for tris:= 0;time.Now().Before();treis++{//邏輯if err == nil{break;}log.printf("err")//等待一定時(shí)間,指數(shù)退避策略time.Sleep(time.Second << uint(tries)) }

3.輸出錯(cuò)誤,優(yōu)雅的結(jié)束

if err != nil{fmt.Fprintf(os.Stderr,"err : %v",err)os.exit(1) }

4.日志記錄,繼續(xù)運(yùn)行

if err != nil{log.printf(os.Stderr,"err : %v",err) }

匿名函數(shù)

首先得知道什么是匿名函數(shù),一般的函數(shù)是這樣的

func 函數(shù)名(...)(返回)

匿名函數(shù)就是沒有函數(shù)名,例如

func (...)(返回)

閉包

Go 語言支持匿名函數(shù),可作為閉包。匿名函數(shù)是一個(gè)"內(nèi)聯(lián)"語句或表達(dá)式。匿名函數(shù)的優(yōu)越性在于可以直接使用函數(shù)內(nèi)的變量,不必申明。

以下實(shí)例中,我們創(chuàng)建了函數(shù) getSequence() ,返回另外一個(gè)函數(shù)。該函數(shù)的目的是在閉包中遞增 i 變量,代碼如下:

課程拓?fù)渑判?#xff08;dfs)
package mainimport ("fmt""sort" )var prereqs = map[string][]string{"algorithms": {"data structures"},"calculus": {"linear algebra"},"compilers": {"data structures","formal languages","computer organization",},"data structure": {"discrete math"},"discrete math": {"intro to programming"},"databases": {"data structures"},"formal languages": {"discrete math"},"networks": {"operating systems"},"operating systems": {"data structures", "computer organization"},"programming languages": {"data structures", "computer organization"}, }func topoSort(m map[string][]string) []string {var order []stringseen := make(map[string]bool)var findAll func(s []string)findAll = func(s []string) {for _, v := range s {if !seen[v] {seen[v] = truefindAll(m[v])order = append(order, v)}}}var keys []stringfor k, _ := range m {keys = append(keys, k)}sort.Strings(keys)findAll(keys)return order }func main() {for i, v := range topoSort(prereqs) {fmt.Printf("%d\t%s\n", i, v)} }

注意得先聲明var findAll func(s []string),不然在匿名函數(shù)中無法調(diào)用自己。

函數(shù)的作用域陷阱

看看下面這段代碼,大概意思是先打印所有的tempdir,然后會(huì)有一個(gè)匿名函數(shù),匿名函數(shù)的作用也是打印位于循環(huán)內(nèi)結(jié)構(gòu)的d。看似沒什么問題把,看看打印結(jié)果

func main() {var rmdirs []func()for _, d := range os.TempDir() {fmt.Println(d)rmdirs = append(rmdirs, func() {fmt.Print("func:")fmt.Println(d)})}for _, dir := range rmdirs {dir()} } 67 58 92 85 115 101 114 115 92 77 121 72 111 112 101 92 65 112 112 68 97 116 97 92 76 111 99 97 108 92 84 101 109 112 func:112 func:112 func:112 func:112 func:112 func:112 func:112 func:112 func:112 func:112 func:112 func:112 func:112 func:112 func:112 func:112 func:112 func:112 func:112 func:112 func:112 func:112 func:112 func:112 func:112 func:112 func:112 func:112 func:112 func:112 func:112 func:112 func:112 func:112

為啥匿名函數(shù)的輸出全是112?

func的輸出似乎都被for的最后一個(gè)元素給覆蓋了,為什么?

因?yàn)槲覀冎篱]包可以用上面的局部變量,可以知道當(dāng)我們在下面的for循環(huán)中使用的局部變量是for循環(huán)中最后一次的。

正確的代碼?

package mainimport ("fmt""os" )func main() {var rmdirs []func()for _, d := range os.TempDir() {dir := dfmt.Println(dir)rmdirs = append(rmdirs, func() {fmt.Print("func:")fmt.Println(dir)})}for _, dir := range rmdirs {dir()} }

變長函數(shù)

變長函數(shù)可以接受長度變化的參數(shù)。

package mainimport "fmt"func test(vals ...int) {fmt.Println(vals) }func main() {test(1)test(1, 2)test(1, 2, 3)test([]int{4, 5, 6}...) }

延遲調(diào)用

defer語句可以用來調(diào)用一個(gè)復(fù)雜的函數(shù),即在函數(shù)的入口和出口設(shè)置調(diào)試行為。

當(dāng)下面代碼:

package mainimport ("log""time" )func slow(){defer trace("hello")()time.Sleep(10 * time.Second) }func trace(msg string) func(){start := time.Now()log.Printf("enter %s",msg)return func() {log.Printf("exit %s(%s)",msg,time.Since(start))} }func main() {slow() }

可以完成對trace的return函數(shù)的延時(shí)調(diào)用,一定不要忘記defer的函數(shù)后面要帶個(gè)小括號(hào)。

如果沒帶就不會(huì)調(diào)用返回的函數(shù),而是會(huì)在slow函數(shù)結(jié)束后直接調(diào)用trace。

再看另外一個(gè)例子。

func double(x int)(result int){defer func() {fmt.Printf("double(%d) = %d\n",x,result)}()time.Sleep(10 * time.Second)return x + x }

這一次雖然是defer了一個(gè)函數(shù),但仍然等到了double結(jié)束了才進(jìn)入函數(shù),所以可以推斷出,如果defer的函數(shù)會(huì)返回一個(gè)函數(shù),先回進(jìn)入函數(shù)取到那個(gè)返回的函數(shù),然后等待主程序return,去延時(shí)調(diào)用返回函數(shù),才會(huì)出現(xiàn)上面日志記錄的情況。

第六章(方法)

方法

func f(...)(返回) //普通方法 func (f *function)f(...)(返回) //方法屬于function

?

總結(jié)

以上是生活随笔為你收集整理的自己读Go程序设计语言的一些总结(更新ing...)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 五月情婷婷 | 亚洲黄色三级视频 | av超碰在线观看 | 亚洲精品激情 | 精品无码一区二区三区电影桃花 | 午夜合集 | 欧美黄网在线观看 | 午夜精品一区二区三区免费视频 | 极品毛片| 一区视频 | www成人在线 | 日本xxxxxwwwww| 亚洲91精品 | 亚州av网站 | 国产女大学生av | 让男按摩师摸好爽 | 色香蕉视频 | 青青草草 | 我们俩电影网mp4动漫官网 | 日韩精品欧美在线 | 91亚洲精品在线 | 免费观看成人鲁鲁鲁鲁鲁视频 | 亚洲AV无码精品自拍 | 97人人射| 日日操夜夜操天天操 | 一区免费视频 | 素人一区二区三区 | 又爽又黄又无遮挡 | 91精品视频在线免费观看 | 欧美性生活免费视频 | 一级免费av | 色就是色亚洲色图 | 黄网站免费在线 | 中文字幕电影一区 | 午夜福利啪啪片 | 精品福利一区二区三区 | 欧美一级黄色片 | 国产精品污www一区二区三区 | 轻轻色在线观看 | 污的视频在线观看 | 久久亚洲熟女cc98cm | 欧美女优一区二区 | 奇米狠狠 | wwwxx国产| 成人av电影免费观看 | www.国产精品视频 | 色之久久综合 | 中日黄色片 | 亚洲大胆视频 | 国产精品二区在线观看 | 亚洲男人天堂视频 | 西西444www大胆无视频 | 精品亚洲乱码一区二区 | 美女福利视频网 | 婷婷91| 茄子av| 中国特级毛片 | 亚洲清纯唯美 | 婷婷综合色 | 精品免费视频一区二区 | 中文字幕97 | 爱福利视频一区 | 奇米影视第四色首页 | 卡一卡二视频 | 欧美a∨亚洲欧美亚洲 | 亚洲欧美一区二区精品久久久 | 午夜在线观看一区 | 人妻无码中文久久久久专区 | 一区二区三区精品视频在线观看 | 韩国一级一片高清免费观看 | 中文字幕少妇在线三级hd | 99久久精品国产一区色 | 国产a黄 | 台湾佬在线 | 少妇无码av无码专区在线观看 | 国产性―交一乱―色―情人 | 齐天大性床战铁扇公主 | 亚洲av无码国产精品永久一区 | 欧洲一区二区三区四区 | 日韩成人在线视频观看 | 韩国电影一区 | 九色91视频 | 久久综合亚洲 | 小视频在线 | 亚洲黄色一区二区 | 免费观看的av | 黄色三级三级三级三级 | 免费毛片一级 | 国产精品一卡二卡三卡 | 电影91久久久 | 免费成人精品 | 黄色免费在线播放 | aa一级黄色片 | aaa影院 | 日韩av在线播放网址 | 久久六六| 国产婷婷一区二区三区 | 国内成人自拍 | 韩国bj大尺度vip福利网站 |