大家好,我叫谢伟,是一名程序员。
这个选题我认真思考了很久,决定把现在的方案分享出来,即:如何从 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 实现的汉子转拼音的版本。这个工具还有其他的实现方式。
- hotoo/pinyin: 汉语拼音转换工具 Node.js/JavaScript 版。
- mozillazg/python-pinyin: 汉语拼音转换工具 Python 版。
- mozillazg/rust-pinyin: 汉语拼音转换工具 Rust 版。
这个项目引起了我的注意,于是我阅读源代码,从帮助文档开始。
帮助文档这么写:
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 Database 中 kHanyuPinyin 部分的拼音数据(来源于《漢語大字典》的拼音数据) -
kXHC1983.txt
: Unihan Database 中 kXHC1983 部分的拼音数据(来源于《现代汉语词典》的拼音数据) -
kHanyuPinlu.txt
: Unihan Database 中 kHanyuPinlu 部分的拼音数据(来源于《現代漢語頻率詞典》的拼音数据) -
kMandarin.txt
: Unihan Database 中 kMandarin 部分的拼音数据(普通话中最常用的一个读音。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. 循坏
等你花了些时间,度过这个阶段,你可能需要练习更高层次的项目,不断的选择,或是工作中的启发、或是工作中的需求等,不断的实现最小的功能,完成阅读、编写代码。不断的思考。
全文完。等我有新的体会,我再分享出来。希望能对你有所启发,再会。
网友评论