美文网首页Go语言GolangGo 语言学习专栏
『Go 语言学习专栏』-- 第十三期

『Go 语言学习专栏』-- 第十三期

作者: 谢小路 | 来源:发表于2018-06-04 08:27 被阅读235次
    go-13.png 13.png

    大家好,我叫谢伟,是一名程序员。

    这个选题我认真思考了很久,决定把现在的方案分享出来,即:如何从 Github 的开源代码中学习?(中级版本)

    下文介绍的方法是我目前的做法,但我希望能不断的进行迭代,达到更佳的效果

    如果你跟着这个栏目,进行了学习,私底下也花了些时间,不管是看了更多的书籍,学习了更多的教程,还是写了更多的示例。今天的主题便是带你突破:即如何从入门选手达到中级选手。

    假设,你已经大概掌握了Go 语言的基本语法。能独立写一些代码。实现一些基本的需求,即已经入门Go。

    那么如何进入到中级水平?

    有人说,做项目。

    确实,项目确实能够引领你达到中级水平。但问题是,初级阶段,你可能不知道该选择一些什么项目。

    • 哪些项目刚刚好是你需要掂一掂脚刚好够得着?
    • 哪些项目是你还需要更多点的知识才能够的着?
    • 从项目中学什么?
    • 怎么从开源项目中学?梳理流程?抄代码?复现一遍?

    这些都是问题。

    下面给出我的策略,希望对你有所启发。

    1. 评估自己的水平

    首先第一点,需要评估自己的水平,知道自己的水平,你才能对一些开源的项目,在一定程度上理解项目的难易复杂度。

    比如 Go 里面的明星产品Docker, 是一个非常热门的开源项目,作为初学者的你,你能凭一己之力,就开始学习或者模仿它吗?

    答案是可以,但是很难。很容易就打击你的信心,初学者,信心很重要的,不然轻而易举的容易放弃。

    那如何评估自己的水平?自己能评估出自己的水平吗?

    这个问题,我准备从下面几点来进行评估。首先,确保你对自己真诚。如实的反馈自己的水平。

    • 代码量
    • 写代码的速度
    • 对内置库的使用程度

    1.1 代码量

    从代码量的角度来看,一定程序上能反映出,你到底写了多少行代码。当然这里的代码量不是粘贴复制,写重复的代码,而是有一定水平的代码,至少完成了某些功能、需求。

    你可能没什么概念,一般来说,如果你是在企业内,一天8小时的工作量,连续写代码的时间不会超过 2小时,意味着,一天的核心代码量大概会是 200 行(数据仅供参考),如果你是学生,自己学习,那我也不太相信,你一天能连续写代码超过 2 小时,能一天写出几千行代码。一天的核心代码量大概也就几百行的量级,更何况在学校,你完成的代码,可能都不是一些略带难度,完成一定需求的代码。

    我从入门一门编程语言的代码量衡量角度来说,大概需要5000行左右。(个人估算:200 * 5 * 5)

    1.2 写代码的速度

    这点来说,不是越快越好,一般的写代码,前期的规划、思考、构建等都极其的重要,这些决定了你编码实现的具体分工以及实现的可能性。

    那怎么评估写代码的速度?

    我认为,第一:不过分依赖现有网上的实现方案。即你上网查阅的比较少,即可实现你的需求;第二:运行出错都能根据IDE 提示解决。而不是频繁的依赖于 搜素引擎。

    1.3 内置库的使用

    内置库可以说是你编写程序的基石,不一定对内置库的用法了然于胸,但是需要根据文档或者内置的库代码,知道最基础的用法。能快速的知道用哪个库。

    比如你需要执行操作系统的一些命令,那么你要清楚知道需要使用的os/exec库。

    知道这个命令相关的知识:

    • 指定目录下运行
    • 带什么参数
    • 命令存在环境变量中的目录
    • 是否存在该命令等

    即:需要对内置库有一定的使用经验。

    2. 找感兴趣的项目

    正确评估自己的水平之后,需要寻找到一些自己感兴趣的项目。

    我推荐两个平台吧:

    • Github
    • 掘金

    Github 几乎是最佳的选择, 掘金这个平台,最近我发现也是挺好的,虽然也是属于技术聚合类的平台,但质量或者颜值还算可以。但是存在一个问题。即自己感兴趣的项目如何能寻找到?

    我推荐下面的方法:

    • 热门
    • 趋势:Github Trending

    即合理使用 Github Trending :


    go-2018-06-03.png

    你可以选择你感兴趣的编程语言,查阅到最佳热门的项目都有哪些。继而可以关注感兴趣的作者。通过感兴趣的作者,继而可以发现一些好玩的项目。

    另外一个是使用掘金插件,可以尽可能多的机会发现好玩的项目。

    掘金-2018-06-03.png

    比如,我可能比较关注后端这块,这些侧边栏可以发现很多的分享者,进而可能关注到项目和源代码。

    总之,一句话,合理使用平台,尽可能创造机会发现感兴趣的项目。

    大概,你现在知道如何发现感兴趣的项目了。

    那如何评估,是否适合你来学习呢?

    • 看源代码

    第一步我们已经评估了自己的水平,达到了一定的水平,那么看源代码就是检阅你的时候了,第一,你需要看懂。第二,根据自己的水平,尝试是否进行花时间练习。

    3. 实现最小代码

    如果你根据源代码之后,决定花时间在这个项目上,那么下一步便是学习。学习这个项目的实现方法。不一定任何方面都值得你学习,我认为,比你更好的实现方式都值得学习。借鉴过来,有些是参考具体的实现方式,有些是参考技术的选择。

    比如这个:抖音机器人
    具体的实现其实不是太复杂,包括这个项目 微信跳一跳小助手

    两者本质上选择的技术方案都是一致的:Python + ADB

    无外乎使用编程和接口完成对手机的操作。不管是截图还是图像识别。

    如果你之前不知道有这样的技术方案能对手机进行操作。那么你就增加了技术广度,知道面对这样的场景,应该选择的方案是 Python + ADB ,当然 Python 不是必须的,编程语言只是工具,你也可以使用 Go + ADB 来实现,都可以。

    具体怎么从源代码中学习?我认为下面几点:

    • 确保实现最小的功能,摈弃完美主义
    • 主体功能实现

    首先,你需要大概阅读下项目的源代码。不管是你Clone 下来,还是使用Github 源代码阅读插件:比如 Octotree、GayHub

    其次,你需要了解项目结构:知道这块是干什么的,那块是干什么的。

    然后,你需要抓住核心的东西。从最核心的东西入手,先自己实现这个项目的最小功能。

    最后,如果你依然有兴趣。你可以这么操作。第一,换种编程语言重新实现,比如别人方案使用 Python ,你可以使用 Go; 第二,换个场景重新实现,比如别人是完成的是对知乎数据的分析抓取,那么你可以对简书,或者你感兴趣的网站数据分析抓取;第三,优化,有可能你能想出更好的实现方案,或者你仅仅只是优化这个项目的某一个层面而已。

    举个例子吧:

    我最近发现一个项目:汉子转拼音(https://github.com/mozillazg/go-pinyin)

    因为我对 Go 感兴趣,所以,选择了 Go 实现的汉子转拼音的版本。这个工具还有其他的实现方式。

    这个项目引起了我的注意,于是我阅读源代码,从帮助文档开始。

    帮助文档这么写:

    package main
    
    import (
        "fmt"
        "github.com/mozillazg/go-pinyin"
    )
    
    func main() {
        hans := "中国人"
    
        // 默认
        a := pinyin.NewArgs()
        fmt.Println(pinyin.Pinyin(hans, a))
        // [[zhong] [guo] [ren]]
    
        // 包含声调
        a.Style = pinyin.Tone
        fmt.Println(pinyin.Pinyin(hans, a))
        // [[zhōng] [guó] [rén]]
    
        // 声调用数字表示
        a.Style = pinyin.Tone2
        fmt.Println(pinyin.Pinyin(hans, a))
        // [[zho1ng] [guo2] [re2n]]
    
        // 开启多音字模式
        a = pinyin.NewArgs()
        a.Heteronym = true
        fmt.Println(pinyin.Pinyin(hans, a))
        // [[zhong zhong] [guo] [ren]]
        a.Style = pinyin.Tone2
        fmt.Println(pinyin.Pinyin(hans, a))
        // [[zho1ng zho4ng] [guo2] [re2n]]
    
        fmt.Println(pinyin.LazyPinyin(hans, pinyin.NewArgs()))
        // [zhong guo ren]
    
        fmt.Println(pinyin.Convert(hans, nil))
        // [[zhong] [guo] [ren]]
    
        fmt.Println(pinyin.LazyConvert(hans, nil))
        // [zhong guo ren]
    }
    

    看上去,是输入一个汉字,区分各种模式,比如默认的形式:不包含声调;比如包含声调;比如声调的一二三四声使用数字来表示;比如多音字模式;等等。

    看上去核心来自于:pinyin 这个包。

    
    func SinglePinyin(r rune, a Args) []string {
        if a.Fallback == nil {
            a.Fallback = Fallback
        }
        value, ok := PinyinDict[int(r)]
        pys := []string{}
        if ok {
            pys = strings.Split(value, ",")
        } else {
            pys = a.Fallback(r, a)
        }
        if len(pys) > 0 {
            if !a.Heteronym {
                pys = pys[:1]
            }
            return applyStyle(pys, a)
        }
        return pys
    }
    
    // Pinyin 汉字转拼音,支持多音字模式.
    func Pinyin(s string, a Args) [][]string {
        pys := [][]string{}
        for _, r := range s {
            py := SinglePinyin(r, a)
            if len(py) > 0 {
                pys = append(pys, py)
            }
        }
        return pys
    }
    
    // LazyPinyin 汉字转拼音,与 `Pinyin` 的区别是:
    // 返回值类型不同,并且不支持多音字模式,每个汉字只取第一个音.
    func LazyPinyin(s string, a Args) []string {
        a.Heteronym = false
        pys := []string{}
        for _, v := range Pinyin(s, a) {
            pys = append(pys, v[0])
        }
        return pys
    }
    
    

    看上去核心来自于这三个函数。遍历字符串。现在你的问题应该是如何将汉字字符串转换为拼音。

    继续阅读,发现这个 pinyin_dict.go 文件

    var PinyinDict = map[int]string{
        0x3007:  "líng,yuán,xīng",
        0x3400:  "qiū",
        0x3401:  "tiàn",
        0x3404:  "kuà",
        0x3405:  "wǔ",
        0x3406:  "yǐn",
    }
    

    看上去一个完整的对照表。即将字符串中字符转换为整型,通过整型能知道拼音是哪个。

    好,至此,大概知道了思路。

    将字符串中字符转换为十六进制数,通过十六进制数能得到拼音。

    那么为了实现最小功能。我可能会这么想:

    即:将自己的名字转换为拼音。

    所以我这么做。

    • 找到"谢伟"两个拼音,转化为 map
    • 遍历字符串,将字符转换为 十六进制数
    package main
    
    import (
        "flag"
        "fmt"
        "strings"
    )
    
    
    func main() {
        var dictMap = map[int32]string{}
    
        dictMap[0x8c22] = "xiè"
        dictMap[0x4f1f] = "wěi"
    
        var a = "谢伟"
        pys := [][]string{}
        for _, v := range a {
            py, ok := dictMap[v]
            pyr := []string{}
            if ok {
                pyr = strings.Split(py, ",")
            }
            pys = append(pys, pyr)
        }
    
        fmt.Println(pys)
    
    }
    
    

    结果:

    [[xiè] [wěi]]
    

    好,上面一步的实现,只是完成了特定的汉子转拼音。

    那么如何将任意汉子转换为拼音。

    • 将 pinyin_dict.go 的map 全部复制下来
    • 使用命令行解析参数
    package main
    
    import (
        "flag"
        "fmt"
        "go-example-for-live/thirteen/infra"
        "strings"
    )
    
    func Pinyin() {
    
        var name string
    
        flag.StringVar(&name, "n", "谢伟", "")
        flag.Parse()
        pys := []string{}
    
        for _, arg := range flag.Args() {
            for _, v := range arg {
                py, ok := infra.PinyinDict[v]
                if ok {
                    pyList := strings.Split(py, ",")
                    if len(pyList) > 0 {
                        pys = append(pys, pyList[0])
                    }
                }
            }
    
        }
    
        fmt.Println(pys)
    
    }
    
    func main() {
    
        Pinyin()
    
    }
    
    

    结果:(windows 平台 go build main.go, 这么运行 main.exe 参数; Linux 平台 go build main.go , ./main 参数), 下文以 windows 平台为例。

    main.exe 谢伟
    

    结果:

    [xiè wěi]
    
    main.exe 谢小路
    

    结果:

    [xiè xiǎo lù]
    
    main.exe 编程语言
    

    结果:

    [biān chéng yǔ yán]
    
    main.exe 鹅鹅鹅,曲项向天歌
    

    结果:

    [é é é qū xiàng xiàng tiān gē]
    
    main.exe 莫愁前路无知己,天下谁人不识君
    

    结果:

    [mò chóu qián lù wú zhī jǐ tiān xià shéi rén bù shí jūn]
    

    通过这个例子,大概我们已经知道这个项目的核心代码了。差不多就已经解构了这个项目。

    4. 扩展

    上面的实现最小的基本功能,通过示例演示了如何操作,但不够。你还需要继续思考。

    • 理解其他模块
    • 哪些不足

    还是以上文pinyin 项目为例,你或许会想拼音数据来自于哪?

    顺藤摸瓜,作者已经告诉你了:pinyin 数据

    Unihan Database 数据版本:

    Date: 2017-05-14 07:01:48 GMT [JHJ] Unicode version: 10.0.0

    • kHanyuPinyin.txt: Unihan DatabasekHanyuPinyin 部分的拼音数据(来源于《漢語大字典》的拼音数据)
    • kXHC1983.txt: Unihan DatabasekXHC1983 部分的拼音数据(来源于《现代汉语词典》的拼音数据)
    • kHanyuPinlu.txt: Unihan DatabasekHanyuPinlu 部分的拼音数据(来源于《現代漢語頻率詞典》的拼音数据)
    • kMandarin.txt: Unihan DatabasekMandarin 部分的拼音数据(普通话中最常用的一个读音。zh-CN 为主,如果 zh-CN 中没有则使用 zh-TW 中的拼音)
    • kMandarin_overwrite.txt: 手工纠正 kMandarin.txt 中有误的拼音数据(可以修改
    • GBK_PUA.txt: Private Use Area 中有拼音的汉字,参考 GB 18030 - 维基百科,自由的百科全书可以修改
    • nonCJKUI.txt: 不属于 CJK Unified Ideograph 但是却有拼音的字符(可以修改
    • kMandarin_8105.txt: 《通用规范汉字表》(2013 年版)里 8105 个汉字最常用的一个读音 (可以修改)
    • overwrite.txt: 手工纠正的拼音数据(可以修改
    • pinyin.txt: 合并上述文件后的拼音数据
    • zdic.txt: 汉典网 的拼音数据

    如果让你自己来做,你可能需要用到爬虫。把数据抓取下来。

    你还可能思考,这个项目哪些值得你借鉴?

    比如:作者是如何区分各种模式的?比如各种声调模式。为什么要区分各种声调模式啊?

    等等,诸如此类。

    5. 循坏

    等你花了些时间,度过这个阶段,你可能需要练习更高层次的项目,不断的选择,或是工作中的启发、或是工作中的需求等,不断的实现最小的功能,完成阅读、编写代码。不断的思考。


    全文完。等我有新的体会,我再分享出来。希望能对你有所启发,再会。

    相关文章

      网友评论

        本文标题:『Go 语言学习专栏』-- 第十三期

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