第一章五個程序
都很好!
但是初學編程/沒有其他語言基礎的不容易看懂。
記一遍不熟悉的東西:
who = strings.Join(os.Args[1:], " ")
//一個string類型sliece和一個分隔符為參數(shù)輸入,返回一個分隔符把slice中所有字符串連接在一起的新字符串
//把slice中所有元素拼接成一個字符串的神器//os.Args[0]存放的是程序名字,可以打印看看//filepath.base 返回傳入路徑的基礎名=文件名
//Base函數(shù)返回路徑的最后一個元素。在提取元素前會去掉末尾的路徑分隔符。如果路徑是"",會返回".";如果路徑是只有一個斜桿構成,會返回單個路徑分隔符。os.Exit(1)
//0正確,非0 錯誤stacker的例子是很好的鴨子類型的例子,棧先進后出,
func (stack *Stack) Pop() (interface{}, error) {
theStack := *stack
if len(theStack) == 0 {
return nil, errors.New("can't Pop() an empty stack")
}
x := theStack[len(theStack)-1]
*stack = theStack[:len(theStack)-1]
return x, nil
}
//receiver類型是*Stack,因為要修改原stack,theStack,中間量
- 文件讀寫,截一段代碼
func americanise(inFile io.Reader, outFile io.Writer) (err error) {
reader := bufio.NewReader(inFile)
writer := bufio.NewWriter(outFile)
//一個延遲函數(shù),等緩沖區(qū)的內(nèi)容全部完成讀寫
defer func() {
if err == nil {
err = writer.Flush()
}
}()
//定義replacer是為了不重復命名err如果這里不給replacer,下一句就是if replacer, err:= 這樣err就是個影子變量,因為返回值那里已經(jīng)命名過了
var replacer func(string) string
if replacer, err = makeReplacerFunction(britishAmerican); err != nil {
return err
}
...(未完)
//這個程序還用了閉包,閉包可以保留之前的狀態(tài),從而把之前的文檔中詞語做特換
- goroutine
polarCoord := <-questions這里阻塞
第二章 布爾&數(shù)值類型
- _不是新值,不初始
- 二元邏輯符 短路邏輯,比如a||b,a為真,b不用判斷
- slice不能比較,可以通過reflect.DeepEqual()完成
- 類型轉換string(a),CONST
- byte==uint 8
- 異或(XOR),不同為1,相同為0,與非(ANDNOT),先與后非
- 大整數(shù),math/big包,這里對應用開發(fā)來說不是重點,可以略過,需要用的地方再回來查
- statistics的例子,用到了net/http包里的一些方法,,homepage里用到,書里講解很詳細,好評。
第三章 字符串
- 字符串只包含7位的ASCII字符,才能被字節(jié)索引,不過go的一個字符一個字符迭代更實用
//一個無聊的測試,可運行
func main() {
fmt.Println("Hello, playground")
a := "abcdefg"
//a是只有ASCII字符的string,可以直接用索引的方式得到每一個字符
//系統(tǒng)會把string按[]byte來處理,所以直接輸出a[1]會得到一個0-255之間的數(shù)字
//string()強轉了a[1]的類型,所以可以輸出b
fmt.Println("hi,a", string(a[1]))
//difference存儲的是中文,中文一個字符可能不止存在一個索引指向的位置上,所以雖然difference只有8字符,但是索引大于7的地方也可能存了值
difference := "皇家葫蘆娃,go"
fmt.Println("difference[8]", string(difference[8]))
for i, qw := range difference {
//用索引取值不能輸出中文字符,直接輸出value可以,
fmt.Println("hi,", string(difference[i]))
fmt.Println("hi中文,", string(qw))
}
//非ASCII轉成rune類型可以直接通過索引輸出漢字
r := []rune(difference)
for i := 0; i < len(r); i++ {
fmt.Println("r[", i, "]=", r[i], "string=", string(r[i]))
}
}
- “可解析字符串字面量”、`原生字符串字面量(可包含任何字符)`
- strconv.Itoa(i), 返回int類型i的字符串表示 和一個錯誤值,如果i=65, 返回“65”,nil
- 一個以個的方式追加字符串
var buffer bytes.Buffer
for{
if piece,ok:=getNextValidString(); ok{
buffer.WriteString(piece)
}else{
break
}
}
fmt.Print(buffer.String(),"\n")
- 講fmt包,有個講包的github也講了這個,里面有一段
為避免以下這類遞歸的情況:
type X string
func (x X) String() string { return Sprintf("<%s>", x) }
需要在遞歸前轉換該值:
func (x X) String() string { return Sprintf("<%s>", string(x)) }
print等,輸出到控制臺
Fprint等,輸出到一個writer
Sprint等,輸出到一個字符串
- bufio.Reader.ReadString('\n')讀,遇到\n停
- 調(diào)試可能會用到的格式化輸出方法:
i:=5
fmt.Printf("|%p->%d|",&i,i)
//結構體
fmt.Printf("%v\n",[]float64{1.00,2.00,3.00})
fmt.Printf("%#v\n",[]float64{1.00,2.00,3.00})
//輸出結果:|address->5|
//[1.00,2.00,3.00]
//[]float64{1.00,2.00,3.00}加上#修飾符,可以輸出結構體類型那部分(map也可以),行話叫:以編程的形式輸出Go語言代碼
//%q適合輸出slice,會使每個字符串都是可識別的
- strings包,很好玩的一個包
舉個??
fmt.Printf("%q\n", strings.SplitAfterN("a,b,c", ",", 2))
//Output:["a," "b,c"]返回的slice最多有2個string
- 進階版分割,可以用
//非符號的字段,一段一段拿出來
f := func(c rune) bool {
return !unicode.IsLetter(c) && !unicode.IsNumber(c)
}
fmt.Printf("Fields are: %q", strings.FieldsFunc(" foo1;bar2,baz3...", f))
Output: Fields are: ["foo1" "bar2" "baz3"]
- string.replac(要執(zhí)行replace的string,“被替換string”,“替換成string”,“-1”) -1表示全部替換,其他數(shù)字表示替換幾次;strings.Repeat 畫三角塔神器,strings里很多方法都經(jīng)常用,不過也很簡單,不寫了,用的時候去查庫就可以
- strconv所有strconv轉換函數(shù)都是返回一個結果,一個error,轉換成功,結果為nil
- unicode ,utf8,regexp(compile,為了安全,編譯通過返回一個*regexp)馬住,用到再來
第四章 集合類型
- chan,func,method,map,slice 引用
- 指針,*
z:=17
pi:=&z
ppi:=&pi
fmt.Print(z,*pi,*ppi)
**ppi++
//==(*(*ppi))++, *(*ppi)++
fmt.Print(z,*pi,*ppi)
//output:171717
181818
- *也是類型修飾符,第一張stack那里有解釋過,那里是指一個結構體指針,用的時候是不需要解引用的,很方便
- new(type)==&type{(可以直接初始化賦值)}
這兩種都分配一個type類型的空值,同時返回一個指向該值的指針 - 添加一個子切片:
s:=append(s,t...)添加slice t中所有值
s:=append(s,u[2,5]...)添加一個子切片
//把一個字符串字節(jié)添加到一個字節(jié)切片中
a:="abc"
b:=[]byte{'u','v'}
b=append(b,a...)
- 一個黑科技(我知道是我沒見過世面,不過很神奇?。?/li>
type Product struct {
name string
}
func (product Product) String() string {
return fmt.Sprintf("%s", product.name)
}
func main() {
products:=[]*Product{{"a"},{"b"},{"c"}}
fmt.Println(products)
//這里會調(diào)用String方法,雖然傳入的是個*Product類型的slice,但是打印的時候會解引用,把整個slice打出來
}
- 在src slice的index位置插入insert slice
func InsertStringSlice(src,insert []string,index int) []string{
retrun append(slice[:index],append(insertion,slice[index:]...)...)
}
func GuessingGame() {
var s string
fmt.Printf("Pick an integer from 0 to 100.\n")
answer := sort.Search(100, func(i int) bool {
fmt.Printf("Is your number <= %d? ", i)
fmt.Scanf("%s", &s)
return s != "" && s[0] == 'y'
})
fmt.Printf("Your number is %d.\n", answer)
}
第五章 過程式編程
核心思想是介紹了go的語法,突出展示了go的簡介和第一章說過的一些特性
- 要避免使用影子變量,比如for循環(huán)中聲明的變量不要和外面的同名
- 類型轉換,提到很多次了string(a)
- 斷言:
resultOfType, boolean:=expression.(Type)安全類型斷言;resultOfType:=expression.(Type)非安全型斷言,失敗會panic() - switch后沒有跟表達式,編譯器會默認將其設置為true;類型開關用法:switch x.(type)
- 萬能for,用法簡單,其他筆記里記過了,,下一個
- 并發(fā),go func(),這個func是個已有函數(shù)或是臨時創(chuàng)建的匿名函數(shù),被調(diào)用的時候會立即執(zhí)行,但是在另一個goroutine,(有點類似多線程),當前的goroutine執(zhí)行會從下一條語句中立即恢復。所以,執(zhí)行一個go語句之后肯定至少有兩個goroutine在并發(fā)執(zhí)行。
??
func main() {
ca := createCounter(2)
cb := createCounter(102)
for i := 0; i < 5; i++ {
a := <-ca
fmt.Printf("A %d \nB %d \n", a, <-cb)
}
}
func createCounter(start int) chan int {
next := make(chan int)
go func(i int) {
for {
next <- i
i++
}
}(start)
return next
}
//輸出是a 2 b102 a3 b103 ... a6 b106
- select,哪個chan通就執(zhí)行哪個,很類似switch
- panic和err,總的來說,如果是邏輯錯誤,或者是希望一旦有錯就強制崩潰,用panic,配合recover食用效果更佳;err的話是首先想確保程序健康執(zhí)行,(公司大牛專門講過:是程序員的問題,panic;啟動的時候可以panic;邏輯問題不知道為什么出錯,panic;想不清楚要不要panic就不要panic,┑( ̄Д  ̄)┍;數(shù)據(jù)錯誤不要panic
- 自定義函數(shù)講了一下參數(shù)和返回值的基本寫法和比較優(yōu)雅的寫法,變成很有用看起來,然而我至今沒用到過
- init和mian,?可以不傳參沒有返回值,導入包的時候會執(zhí)行init然后main,如果導入包的時候用_當別名,那就是只執(zhí)行導入包的init函數(shù)
- 閉包,可以保留和他在統(tǒng)一作用于的變量的狀態(tài)和值,所有的匿名函數(shù)都是閉包。另一種形式是return一個閉包函數(shù)這樣的寫法真的是看多了就好了,好處是這個閉包里是可以使用return所在的函數(shù)體的一些變量的。
- 遞歸,除了大家熟悉的Fibonacci數(shù)以外還有個好玩的
//判斷一個單詞是否是一個回文詞,比如PULLUP,ROTOR
func IsPalindrome(word string) bool {
if utf8.RuneCountInString(word)<=1{
return true
}
first, sizeOfFirst:=utf8.DecodeRuneCountInString(word)
last,sizeOfLast:=utf8.DecodeLastRuneCountInString(word)
if first!=last{return false}
return IsPalindrome(word[sizeOfFirst:len(word)-sizeOfLast])
}
動態(tài)函數(shù),其實也是init的一種用法,init中可以做一個判斷并協(xié)議中處理方式,如果符合,有限進行該處理,不符合就在包里繼續(xù)找方法
泛型函數(shù),返回一個interface{},然后通過調(diào)用的時候斷言:example().(type)從而把我們需要的值轉為我們需要的值,不過是很麻煩的一種寫法
switch的一個小技巧,如果每個case中有重復代碼,可以直接在switch處加上一個if change,每個case上設置一個bool,eg:change=true
差不多就到這里了,struct和interface都是項目里特別常用的,看書好像不如實戰(zhàn)效果好。
----分隔符----
第六章
- go不支持重載方法,不可以定義兩個名字相同但是簽名不同的方法,但可以設置變參,這樣方法相當于可用性更高
- 可以自定義類型,給自定義的類型自定義方法
- 可通過結構體嵌套方式實現(xiàn)類似繼承的功能
- 注意傳遞值還是傳遞指針,傳遞指針才能改變原始數(shù)據(jù)的值,另外傳指針比傳值更高效
- 方法表達式(6.2.1.2)這里沒看懂,高人私我
- New***這類的方法可以看做是顯式的構造函數(shù),用來確保可以生成一個合法的某類型的實例
- 接口:聲明一個或多個方法,完全抽象,不能實例化,但可以創(chuàng)建一個實現(xiàn)了該接口的類型,這個類型可以被實例化。命名最好以er結尾(傳統(tǒng))
*第六章這種講解方式有點看不下去。。。我 去第七章了,我慚愧
第七章
- main就是一個goroutine
- 小坑1:需要注意主goroutine退出的同時其他goroutine也會退出,所以我們得先確定其他goroutine已經(jīng)退出了,再退出主goroutine
- 小坑2:可能發(fā)生死鎖,用通道即可避免
- 解決方法1:常見的是讓主goroutine在一個done的channel上等,根據(jù)接收工作是否完成來覺得退出的時間
- 解決方法2:sync.WaitGroup,讓每個goroutine報告自己的完成狀態(tài),但是sync.WaitGroup本身也可能會產(chǎn)生死鎖,特別是當所有工作的goroutine都處于鎖定狀態(tài)的時候調(diào)用sync.WaitGroup.Wait()
- channel為并發(fā)運行的goroutine之間提供了一種無鎖通信方式,當一個通道發(fā)生通信時,發(fā)送通道和接受通道都處于同步狀態(tài)
- 單向通道,比如只為了傳遞參數(shù)的時候
chan<- //類型是只允許發(fā)送
<-chan //類型是只允許接收
- channel里發(fā)生boolean, int, float64都是安全的,因為都是值傳遞,復制的方式,string也安全,因為go不允許改string
- 指針和引用類型不行,不安全,對這類值必須是串行訪問,可以使用互斥量;或者是設定一個規(guī)則,一個指針或一個引用發(fā)送之后發(fā)送方就不會再訪問他,讓接受者來訪問和釋放指針或是引用的值;再或者讓所有導出方法不能修改指針和引用的值,可以修改的都不能導出。但比如*regexp.Regexp可以被多個goroutine訪問,因為這個指針指向的值的所有方法都不會改變這個值的狀態(tài)
- 一段簡單的代碼,
go func(){
for _,job:=range jobList{
jobs<-job //阻塞,等待channel里有值傳過來
}
close(jobs)
}()
- 一般發(fā)送端關閉channel而不是接收端
- 一段棒棒的代碼,
func waitAndProcessResults(done <-chan struct{}, results <-chan Result) {
for working := workers; working > 0; {
select { // Blocking
case result := <-results:
fmt.Printf("%s:%d:%s\n", result.filename, result.lino,
result.line)
case <-done:
working--
}
}
DONE:
for {
select { // Nonblocking
case result := <-results:
fmt.Printf("%s:%d:%s\n", result.filename, result.lino,
result.line)
default:
break DONE
}
}
}
- 后面講的是幾個safeMap的例子,以代碼講解為主,就不記了,書上講解詳細的很
第八章
(之前寫過一點文件處理的代碼,是archive和unarchive,當然也涉及到讀寫,所以會跳過這部分相關的內(nèi)容,記相對不太熟悉的內(nèi)容)
主要在講encode/json包func Marshal(v interface{}) ([]byte, error)和func Unmarshal(data []byte, v interface{}) error的用法
額,沒其他了好像。。
事實證明struct和interface包括func這類都適合邊做項目邊學或者邊看項目邊學,比干看書效率高一點,當然書上的例子也不錯,跟著看或者邊看邊做也會很好。
所以此篇應該完結了,二刷這本書的可能性不大,畢竟書是借閱的。┑( ̄Д  ̄)┍