第一章五个程序
都很好!
但是初学编程/没有其他语言基础的不容易看懂。
记一遍不熟悉的东西:
-
who = strings.Join(os.Args[1:], " ")
//一个string类型sliece和一个分隔符为参数输入,返回一个分隔符把slice中所有字符串连接在一起的新字符串
//把slice中所有元素拼接成一个字符串的神器 -
//os.Args[0]存放的是程序名字,可以打印看看
-
//filepath.base 返回传入路径的基础名=文件名
//Base函数返回路径的最后一个元素。在提取元素前会去掉末尾的路径分隔符。如果路径是"",会返回".";如果路径是只有一个斜杆构成,会返回单个路径分隔符。 -
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)
//一个延迟函数,等缓冲区的内容全部完成读写
defer func() {
if err == nil {
err = writer.Flush()
}
}()
//定义replacer是为了不重复命名err如果这里不给replacer,下一句就是if replacer, err:= 这样err就是个影子变量,因为返回值那里已经命名过了
var replacer func(string) string
if replacer, err = makeReplacerFunction(britishAmerican); err != nil {
return err
}
...(未完)
//这个程序还用了闭包,闭包可以保留之前的状态,从而把之前的文档中词语做特换
- goroutine
polarCoord := <-questions
这里阻塞
第二章 布尔&数值类型
- _不是新值,不初始
- 二元逻辑符 短路逻辑,比如a||b,a为真,b不用判断
- slice不能比较,可以通过reflect.DeepEqual()完成
- 类型转换string(a),CONST
- byte==uint 8
- 异或(XOR),不同为1,相同为0,与非(ANDNOT),先与后非
- 大整数,math/big包,这里对应用开发来说不是重点,可以略过,需要用的地方再回来查
- statistics的例子,用到了net/http包里的一些方法,,homepage里用到,书里讲解很详细,好评。
第三章 字符串
- 字符串只包含7位的ASCII字符,才能被字节索引,不过go的一个字符一个字符迭代更实用
//一个无聊的测试,可运行
func main() {
fmt.Println("Hello, playground")
a := "abcdefg"
//a是只有ASCII字符的string,可以直接用索引的方式得到每一个字符
//系统会把string按[]byte来处理,所以直接输出a[1]会得到一个0-255之间的数字
//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停
- 调试可能会用到的格式化输出方法:
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(要执行replace的string,“被替换string”,“替换成string”,“-1”) -1表示全部替换,其他数字表示替换几次;strings.Repeat 画三角塔神器,strings里很多方法都经常用,不过也很简单,不写了,用的时候去查库就可以
- strconv所有strconv转换函数都是返回一个结果,一个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]...)添加一个子切片
//把一个字符串字节添加到一个字节切片中
a:="abc"
b:=[]byte{'u','v'}
b=append(b,a...)
- 一个黑科技(我知道是我没见过世面,不过很神奇啊)
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)
//这里会调用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循环中声明的变量不要和外面的同名
- 类型转换,提到很多次了string(a)
- 断言:
resultOfType, boolean:=expression.(Type)
安全类型断言;resultOfType:=expression.(Type)
非安全型断言,失败会panic() - switch后没有跟表达式,编译器会默认将其设置为true;类型开关用法:switch x.(type)
- 万能for,用法简单,其他笔记里记过了,,下一个
- 并发,go func(),这个func是个已有函数或是临时创建的匿名函数,被调用的时候会立即执行,但是在另一个goroutine,(有点类似多线程),当前的goroutine执行会从下一条语句中立即恢复。所以,执行一个go语句之后肯定至少有两个goroutine在并发执行。
🌰
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通就执行哪个,很类似switch
- panic和err,总的来说,如果是逻辑错误,或者是希望一旦有错就强制崩溃,用panic,配合recover食用效果更佳;err的话是首先想确保程序健康执行,(公司大牛专门讲过:是程序员的问题,panic;启动的时候可以panic;逻辑问题不知道为什么出错,panic;想不清楚要不要panic就不要panic,┑( ̄Д  ̄)┍;数据错误不要panic
- 自定义函数讲了一下参数和返回值的基本写法和比较优雅的写法,变成很有用看起来,然而我至今没用到过
- init和mian,�可以不传参没有返回值,导入包的时候会执行init然后main,如果导入包的时候用_当别名,那就是只执行导入包的init函数
- 闭包,可以保留和他在统一作用于的变量的状态和值,所有的匿名函数都是闭包。另一种形式是return一个闭包函数这样的写法真的是看多了就好了,好处是这个闭包里是可以使用return所在的函数体的一些变量的。
- 递归,除了大家熟悉的Fibonacci数以外还有个好玩的
//判断一个单词是否是一个回文词,比如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])
}
-
动态函数,其实也是init的一种用法,init中可以做一个判断并协议中处理方式,如果符合,有限进行该处理,不符合就在包里继续找方法
-
泛型函数,返回一个interface{},然后通过调用的时候断言:example().(type)从而把我们需要的值转为我们需要的值,不过是很麻烦的一种写法
-
switch的一个小技巧,如果每个case中有重复代码,可以直接在switch处加上一个if change,每个case上设置一个bool,eg:change=true
差不多就到这里了,struct和interface都是项目里特别常用的,看书好像不如实战效果好。
----分隔符----
第六章
- go不支持重载方法,不可以定义两个名字相同但是签名不同的方法,但可以设置变参,这样方法相当于可用性更高
- 可以自定义类型,给自定义的类型自定义方法
- 可通过结构体嵌套方式实现类似继承的功能
- 注意传递值还是传递指针,传递指针才能改变原始数据的值,另外传指针比传值更高效
- 方法表达式(6.2.1.2)这里没看懂,高人私我
- New***这类的方法可以看做是显式的构造函数,用来确保可以生成一个合法的某类型的实例
- 接口:声明一个或多个方法,完全抽象,不能实例化,但可以创建一个实现了该接口的类型,这个类型可以被实例化。命名最好以er结尾(传统)
*第六章这种讲解方式有点看不下去。。。我 去第七章了,我惭愧
第七章
- main就是一个goroutine
- 小坑1:需要注意主goroutine退出的同时其他goroutine也会退出,所以我们得先确定其他goroutine已经退出了,再退出主goroutine
- 小坑2:可能发生死锁,用通道即可避免
- 解决方法1:常见的是让主goroutine在一个done的channel上等,根据接收工作是否完成来觉得退出的时间
- 解决方法2:sync.WaitGroup,让每个goroutine报告自己的完成状态,但是sync.WaitGroup本身也可能会产生死锁,特别是当所有工作的goroutine都处于锁定状态的时候调用sync.WaitGroup.Wait()
- channel为并发运行的goroutine之间提供了一种无锁通信方式,当一个通道发生通信时,发送通道和接受通道都处于同步状态
- 单向通道,比如只为了传递参数的时候
chan<- //类型是只允许发送
<-chan //类型是只允许接收
- channel里发生boolean, int, float64都是安全的,因为都是值传递,复制的方式,string也安全,因为go不允许改string
- 指针和引用类型不行,不安全,对这类值必须是串行访问,可以使用互斥量;或者是设定一个规则,一个指针或一个引用发送之后发送方就不会再访问他,让接受者来访问和释放指针或是引用的值;再或者让所有导出方法不能修改指针和引用的值,可以修改的都不能导出。但比如*regexp.Regexp可以被多个goroutine访问,因为这个指针指向的值的所有方法都不会改变这个值的状态
- 一段简单的代码,
go func(){
for _,job:=range jobList{
jobs<-job //阻塞,等待channel里有值传过来
}
close(jobs)
}()
- 一般发送端关闭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,当然也涉及到读写,所以会跳过这部分相关的内容,记相对不太熟悉的内容)
-
主要在讲encode/json包func Marshal(v interface{}) ([]byte, error)和func Unmarshal(data []byte, v interface{}) error的用法
-
额,没其他了好像。。
事实证明struct和interface包括func这类都适合边做项目边学或者边看项目边学,比干看书效率高一点,当然书上的例子也不错,跟着看或者边看边做也会很好。
所以此篇应该完结了,二刷这本书的可能性不大,毕竟书是借阅的。┑( ̄Д  ̄)┍
网友评论