Go 语言程序设计-笔记

作者: 暗黑破坏球嘿哈 | 来源:发表于2016-06-30 15:48 被阅读484次

    第一章五个程序

    都很好!
    但是初学编程/没有其他语言基础的不容易看懂。

    记一遍不熟悉的东西:

    1. who = strings.Join(os.Args[1:], " ")
      //一个string类型sliece和一个分隔符为参数输入,返回一个分隔符把slice中所有字符串连接在一起的新字符串
      //把slice中所有元素拼接成一个字符串的神器

    2. //os.Args[0]存放的是程序名字,可以打印看看

    3. //filepath.base 返回传入路径的基础名=文件名
      //Base函数返回路径的最后一个元素。在提取元素前会去掉末尾的路径分隔符。如果路径是"",会返回".";如果路径是只有一个斜杆构成,会返回单个路径分隔符。

    4. os.Exit(1)
      //0正确,非0 错误

    5. 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,中间量

    1. 文件读写,截一段代码
    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
        }
    ...(未完)
    

    //这个程序还用了闭包,闭包可以保留之前的状态,从而把之前的文档中词语做特换

    1. goroutine
      polarCoord := <-questions这里阻塞

    第二章 布尔&数值类型

    1. _不是新值,不初始
    2. 二元逻辑符 短路逻辑,比如a||b,a为真,b不用判断
    3. slice不能比较,可以通过reflect.DeepEqual()完成
    4. 类型转换string(a),CONST
    5. byte==uint 8
    6. 异或(XOR),不同为1,相同为0,与非(ANDNOT),先与后非
    7. 大整数,math/big包,这里对应用开发来说不是重点,可以略过,需要用的地方再回来查
    8. statistics的例子,用到了net/http包里的一些方法,,homepage里用到,书里讲解很详细,好评。

    第三章 字符串

    1. 字符串只包含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]))
        }   
    }
    
    1. “可解析字符串字面量”、`原生字符串字面量(可包含任何字符)`
    2. strconv.Itoa(i), 返回int类型i的字符串表示 和一个错误值,如果i=65, 返回“65”,nil
    3. 一个以个的方式追加字符串
    var buffer bytes.Buffer
    for{
      if piece,ok:=getNextValidString(); ok{
        buffer.WriteString(piece)
      }else{
    break
    }
    }
    fmt.Print(buffer.String(),"\n")
    
    1. 讲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等,输出到一个字符串

    1. bufio.Reader.ReadString('\n')读,遇到\n停
    2. 调试可能会用到的格式化输出方法:
    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,会使每个字符串都是可识别的
    
    1. strings包,很好玩的一个包
      举个🌰
    fmt.Printf("%q\n", strings.SplitAfterN("a,b,c", ",", 2))
    //Output:["a," "b,c"]返回的slice最多有2个string
    
    1. 进阶版分割,可以用
    //非符号的字段,一段一段拿出来
    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"]
    
    1. string.replac(要执行replace的string,“被替换string”,“替换成string”,“-1”) -1表示全部替换,其他数字表示替换几次;strings.Repeat 画三角塔神器,strings里很多方法都经常用,不过也很简单,不写了,用的时候去查库就可以
    2. strconv所有strconv转换函数都是返回一个结果,一个error,转换成功,结果为nil
    3. unicode ,utf8,regexp(compile,为了安全,编译通过返回一个*regexp)马住,用到再来

    第四章 集合类型

    1. chan,func,method,map,slice 引用
    2. 指针,*
    z:=17
    pi:=&z
    ppi:=&pi
    fmt.Print(z,*pi,*ppi)
    **ppi++
    //==(*(*ppi))++, *(*ppi)++
    fmt.Print(z,*pi,*ppi)
    //output:171717
    181818
    
    1. *也是类型修饰符,第一张stack那里有解释过,那里是指一个结构体指针,用的时候是不需要解引用的,很方便
    2. new(type)==&type{(可以直接初始化赋值)}
      这两种都分配一个type类型的空值,同时返回一个指向该值的指针
    3. 添加一个子切片:
    s:=append(s,t...)添加slice t中所有值
    s:=append(s,u[2,5]...)添加一个子切片
    //把一个字符串字节添加到一个字节切片中
    a:="abc"
    b:=[]byte{'u','v'}
    b=append(b,a...)
    
    1. 一个黑科技(我知道是我没见过世面,不过很神奇啊)
    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打出来
    }
    
    1. 在src slice的index位置插入insert slice
    func InsertStringSlice(src,insert []string,index int) []string{
      retrun append(slice[:index],append(insertion,slice[index:]...)...)
    }
    
    1. 删除slice中间的几个元素s:=append(s[:1],s[5:]...)删除了s1:5
    2. sort.Search的用法here
      官网例子很好玩,看书看累了可以试一试,嗯。
    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的简介和第一章说过的一些特性

    1. 要避免使用影子变量,比如for循环中声明的变量不要和外面的同名
    2. 类型转换,提到很多次了string(a)
    3. 断言:resultOfType, boolean:=expression.(Type)安全类型断言;resultOfType:=expression.(Type)非安全型断言,失败会panic()
    4. switch后没有跟表达式,编译器会默认将其设置为true;类型开关用法:switch x.(type)
    5. 万能for,用法简单,其他笔记里记过了,,下一个
    6. 并发,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
    
    1. select,哪个chan通就执行哪个,很类似switch
    2. panic和err,总的来说,如果是逻辑错误,或者是希望一旦有错就强制崩溃,用panic,配合recover食用效果更佳;err的话是首先想确保程序健康执行,(公司大牛专门讲过:是程序员的问题,panic;启动的时候可以panic;逻辑问题不知道为什么出错,panic;想不清楚要不要panic就不要panic,┑( ̄Д  ̄)┍;数据错误不要panic
    3. 自定义函数讲了一下参数和返回值的基本写法和比较优雅的写法,变成很有用看起来,然而我至今没用到过
    4. init和mian,�可以不传参没有返回值,导入包的时候会执行init然后main,如果导入包的时候用_当别名,那就是只执行导入包的init函数
    5. 闭包,可以保留和他在统一作用于的变量的状态和值,所有的匿名函数都是闭包。另一种形式是return一个闭包函数这样的写法真的是看多了就好了,好处是这个闭包里是可以使用return所在的函数体的一些变量的。
    6. 递归,除了大家熟悉的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])
    }
    
    1. 动态函数,其实也是init的一种用法,init中可以做一个判断并协议中处理方式,如果符合,有限进行该处理,不符合就在包里继续找方法

    2. 泛型函数,返回一个interface{},然后通过调用的时候断言:example().(type)从而把我们需要的值转为我们需要的值,不过是很麻烦的一种写法

    3. switch的一个小技巧,如果每个case中有重复代码,可以直接在switch处加上一个if change,每个case上设置一个bool,eg:change=true

    差不多就到这里了,struct和interface都是项目里特别常用的,看书好像不如实战效果好。

    ----分隔符----

    第六章

    1. go不支持重载方法,不可以定义两个名字相同但是签名不同的方法,但可以设置变参,这样方法相当于可用性更高
    2. 可以自定义类型,给自定义的类型自定义方法
    3. 可通过结构体嵌套方式实现类似继承的功能
    4. 注意传递值还是传递指针,传递指针才能改变原始数据的值,另外传指针比传值更高效
    5. 方法表达式(6.2.1.2)这里没看懂,高人私我
    6. New***这类的方法可以看做是显式的构造函数,用来确保可以生成一个合法的某类型的实例
    7. 接口:声明一个或多个方法,完全抽象,不能实例化,但可以创建一个实现了该接口的类型,这个类型可以被实例化。命名最好以er结尾(传统)
      *第六章这种讲解方式有点看不下去。。。我 去第七章了,我惭愧

    第七章

    1. main就是一个goroutine
    2. 小坑1:需要注意主goroutine退出的同时其他goroutine也会退出,所以我们得先确定其他goroutine已经退出了,再退出主goroutine
    3. 小坑2:可能发生死锁,用通道即可避免
    4. 解决方法1:常见的是让主goroutine在一个done的channel上等,根据接收工作是否完成来觉得退出的时间
    5. 解决方法2:sync.WaitGroup,让每个goroutine报告自己的完成状态,但是sync.WaitGroup本身也可能会产生死锁,特别是当所有工作的goroutine都处于锁定状态的时候调用sync.WaitGroup.Wait()
    6. channel为并发运行的goroutine之间提供了一种无锁通信方式,当一个通道发生通信时,发送通道和接受通道都处于同步状态
    7. 单向通道,比如只为了传递参数的时候
    chan<- //类型是只允许发送
    <-chan //类型是只允许接收
    
    1. channel里发生boolean, int, float64都是安全的,因为都是值传递,复制的方式,string也安全,因为go不允许改string
    2. 指针和引用类型不行,不安全,对这类值必须是串行访问,可以使用互斥量;或者是设定一个规则,一个指针或一个引用发送之后发送方就不会再访问他,让接受者来访问和释放指针或是引用的值;再或者让所有导出方法不能修改指针和引用的值,可以修改的都不能导出。但比如*regexp.Regexp可以被多个goroutine访问,因为这个指针指向的值的所有方法都不会改变这个值的状态
    3. 一段简单的代码,
    go func(){
      for _,job:=range jobList{
      jobs<-job  //阻塞,等待channel里有值传过来
    }
    close(jobs)
    }()
    
    1. 一般发送端关闭channel而不是接收端
    2. 一段棒棒的代码,
    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
            }
        }
    }
    
    1. 后面讲的是几个safeMap的例子,以代码讲解为主,就不记了,书上讲解详细的很

    第八章

    (之前写过一点文件处理的代码,是archive和unarchive,当然也涉及到读写,所以会跳过这部分相关的内容,记相对不太熟悉的内容)

    1. 主要在讲encode/json包func Marshal(v interface{}) ([]byte, error)func Unmarshal(data []byte, v interface{}) error的用法

    2. 额,没其他了好像。。

    事实证明struct和interface包括func这类都适合边做项目边学或者边看项目边学,比干看书效率高一点,当然书上的例子也不错,跟着看或者边看边做也会很好。

    所以此篇应该完结了,二刷这本书的可能性不大,毕竟书是借阅的。┑( ̄Д  ̄)┍

    相关文章

      网友评论

        本文标题:Go 语言程序设计-笔记

        本文链接:https://www.haomeiwen.com/subject/dnjxjttx.html