如何看待王垠 新浪博客这篇评价Golang的博客

产品环境中 Go 语言的最佳实践 - 技术翻译 - 开源中国社区
产品环境中 Go 语言的最佳实践
【已翻译100%】
英文原文:
推荐于 3年前 (共 15 段, 翻译完成于 09-01)
参与翻译&(9人)&: ,
在SoundCloud,我们为客户构建了产品的API。或者说,我们主要的网站、手机客户端和手机应用是该API的第一批客户。该API背后是一个领域性的服务:SoundCloud基本上以面向服务体系结构的形式运作。
我们也是通晓多种语言的组织,因为我们使用了很多语言。并且这些服务(和基础设施支持)的许多部分是使用Golang开发的。事实上,我们都是早期Golang的使用者:目前,我们已在产品中使用Golang有两年半的时间。相关项目包括:
Bazooka,我们内部服务平台;产品思想非常类似于Keroku或Flynn。
我们外围的传输层使用通用的nginx, HAProxy等等,但是它们要和Golang服务协作。
我们的音频存储在AWS S3上,但是上传、转码和生成链接等需要Golang服务协调处理。
搜索采用了Elasticsearch, 探测使用复杂的机器学习模型,但是它们都与由Golang开发的基础设施相集成。
Prometheus,一个早期阶段的遥测系统纯粹是有Golang开发。
当前,流处理采用Cassandra,但是我们正打算(几乎)完全使用Golang代替。
我们也正在试验用Golnag开发的HTTP流媒体直播服务。
许多其他面向产品的小服务。
&翻译得不错哦!
这些项目大概有六个团队开发,包括十多人的SoundCloud勤杂工,他们中的大部分会全职使用Golang。毕竟在这个时候,这些项目和这样混杂的工程师中,我们已经逐渐形成了在产品中使用Golang的最好实践方法。我们的这些教训将对其他开始大举投资Golang的组织提供帮助。
在我们的笔记本上,我们已经设定了单一、全局的GOPATH。就个人而言,我喜欢使用$HOME,但是许多其他人使用$HOME下的一个子目录。我们克隆仓库进入GOPATH的相对路径,然后就可直接工作。即,
$&mkdir&-p&$GOPATH//soundcloud
$&cd&$GOPATH//soundcloud
$&git&clone&:soundcloud/roshi
我们中的许多人在早期一直和约定俗成的事情做斗争,以保持我们自己特有的代码组织方法。事实上,它根本不值得如此麻烦。
&翻译得不错哦!
对于编辑器,许多用户使用Vim以及各种插件。(我使用的就不错。)还有许多人,包括我自己也是,结合GoSublime使用Sublime Text。也有少数人使用Emacs,但没有人用IDE。我不确定这是不是个最佳的实践,但标出来挺有趣的。
我们的最佳实践是确保任何事情简单。许多服务源码半打包在main包中。
/soundcloud/simple/
main_test.go
support.go
support_test.go
比如我们的搜索调度器,两年后仍然是这样。在确定需要前不要创建新结构。
也许在某些时候你需要创建一个新的支持包。在你的main库中使用子目录,并使用完整的限定名导入。如果该包只有一个文件或一个结构,那么它肯定不需要分拆出来。
&翻译得不错哦!
有时一个仓库中需要包含多个二进制文件;比如这个任务需要一个服务,一个工作进程,或一个监控。在这种情况下,将每个二进制文件放在特定main包的单独的子目录中,并使用其他的子目录(或包)来实现共享的功能。
/soundcloud/complex/
complex-server/
main_test.go
handlers.go
handlers_test.go
complex-worker/
main_test.go
process.go
process_test.go
foo_test.go
bar_test.go
请注意,不要引入asrc目录。由于vendor子目录异常(下面介绍更多内容)不要在仓库中包含src目录,或将其添加到GOPATH。
格式及样式
通常来说,首先配置你的编辑器保存代码交给go fmt(或),使用默认参数。这意味使用tab缩进,用空格对齐。格式不正确的代码将不能提交。
&翻译得不错哦!
过去的风格指南非常广泛,但谷歌最近发布了他们的
文档,这几乎就是我们应遵守的公约。因此,我们使用它。
实际上我们把它推进了一点:
避免命名返回参数,除非他们能明确和显着地提高透明度。
避免用 make 和 new,除非他们是必要的(new(int),或 make(Chan int)),或者我们能提前知道要分配的东西的尺寸( make(map[int]string,n),或 make([]int,0,256))。
使用 struct{} 作为标记值,而不是布尔或接口{}。例如,集合是 map[string]struct{};信道是 chan struct{}。它明确标明了信息的明确缺乏。
打断长行的参数也很好。那更象是Java的风格:
//&不要这样。
func&process(dst&io.Writer,&readTimeout,
writeTimeout&time.Duration,&allowInvalid&bool,
max&int,&src&&-chan&util.Job)&{
这样会更好:
func&process(
dst&io.Writer,
readTimeout,&writeTimeout&time.Duration,
allowInvalid&bool,
src&&-chan&util.Job,
当构造对象时也同样分为多行:
f&:=&foo.New(foo.Config{?
Site:&"",?
Out:&&os.Stdout,?
Dest:&conference.KeyPair{?
Key:&&&"gophercon",
Value:&2014,
另外,当分配新的对象时,在初始化部分传递成员值(如上面)比下面这样过后设置要好。
//&不要这样。
f&:=&&Foo{}&//&or,&even&worse:&new(Foo)
f.Site&=&""
f.Out&=&os.Stdout
f.Dest.Key&=&"gophercon"
f.Dest.Value&=&2014
&翻译得不错哦!
我们尝试了通过多种方式向Go程序传递配置:解析配置文件,用 os.Getenv 直接从环境中提取配置,各种增值flag解析包。最后,最合乎经济原则的就是普通的package flag,它的严格类型和简单语义对我们所需的一切都绝对够用而且够好。
我们主要部署 的应用,12-Factor 应用程序通过环境传递配置。但即使这样,我们也使用一个启动脚本来把环境变量转换为flags。Flags作为程序及其运行环境之间的一个明确和全文档化的表面区域。他们对于了解和操作程序来说是非常宝贵的。
&翻译得不错哦!
一个关于flags的不错的习惯是把他们定义到你的main函数中。这样就能防止你在代码中随意的将他们作为全局变量使用,这使你严格的遵守依赖注入从而方便测试。
func&main()&{
payload&=&flag.String("payload",&"abc",&"payload&data")
delay&&&=&flag.Duration("delay",&1*time.Second,&"write&delay")
flag.Parse()
日志和遥测
我们尝试过几个日志框架,他们提供像日志级别,调试,路由输出,自定义格式化等等功能。最终我们选定package log。因为我们只记录可操作信息。 这意味着需要人工处理的 serious, panic级别的错误,或者结构化数据会被其他机器消耗。 举个例子,搜索转发器发送每一个它使用上下文信息处理的请求,因此我们的分析工作流可以看到新西兰的人们经常搜索 , 或者随便什么。
&翻译得不错哦!
我们考虑到遥测,在一个运行过程中释放出的任何其他量:请求响应时间,QPS,运行错误,队列深度等等。并且遥测基本上包括两种模式:push和pull。
push意味着释放指标到一个已知的系统。例如Graphite, Statsd, and AirBrake
pull意味着在一些已知的位置暴露指标,并允许已知的系统去擦除它们。例如,expvar和Prometheus(或许还有其他的)
当然两种方式都有自己的存在性。当你开始使用时,push是直观和简单的。但是推送指标的增长却有悖常理:你得到的越大,成本越高。我们过去发现在特定规模大小的基础设施上,pull是该尺度下的唯一模型。那也有许多值能反映一个运行的系统。所以,最好的实践是:expvar或者类似风格的。
&翻译得不错哦!
测试和验证
在一年的过程中我们尝试了许多的测试库和框架,但是很快放弃了他们中的大部分,今天我们所有的测试通过数据驱动(表驱动)测试,用普通的包测试。我们没有强烈或者明确的抱怨测试/检查包,除此之外,他们根本没有提供巨大的价值。有一件事情是有帮助的:让你更简单的对任意值进行比较(例如expected对got)。
包测试是面向单元测试的,对于集成测试,就会有点麻烦。运行的外部服务依赖于你的集成环境,但是我们找到了一个好的方式集成他们。写一个integration_test.go,给它一个integration的构建标签。定义(全局)标志,比如服务地址和连接字符串,用他们在你的测试中。
//&+build&integration
var&fooAddr&=&flag.String(...)
func&TestToo(t&*testing.T)&{
f,&err&:=&foo.Connect(*fooAddr)
&翻译得不错哦!
go test 和 go build 一样建立标签,所以你可以调用 go test -tags=integration 。它也综合了 flag.Parse 包的 main,所以任何被声明和可见的 flags 将被处理和提供给你的测试。
通过验证,我的意思是静态代码验证。幸运的是,Go 有一些很好的工具。我发现当考虑使用哪种工具时考虑编写代码的阶段很有用。
当做这种事时
go fmt(或 goimports)
go vet,golint, 或者 go test
go test -tags=integration
&翻译得不错哦!
我们的翻译工作遵照 ,如果我们的工作有侵犯到您的权益,请及时联系我们
王垠是谁?他用Go写过什么项目?
The stream is currently powered by Cassandra, but we’re rolling out a (nearly) pure Go replacement.
估计是用cassandra作为流式计算的存储设施吧,输出到mobile client也是从cassandra取的吧怎么学习golang?
我的图书馆
怎么学习golang?
怎么学习golang?
我是毕业以后才学的php,不是计算机专业。因此,我想学门静态语言。目前,自己学习golang也有一段时间了。但是感觉golang总是如不了门! 和php.比。感觉go较灵活。它的标准库的很多函数 参数、返回值什么的 不再像php一样单纯的就是那几个基本数据类型,而是接口,甚至结构什么的,而我总是被这些东西整得晕头转向!看文档不知道怎么下手!求知乎的大神指点迷津!怎么学习golang,或静态语言?----------------------一年以后--------------------ps:感谢各位大大热心的回答!
.zm-item-answer&}" data-init="{&params&: {&url_token&: , &pagesize&: 10, &offset&: 0}, &nodename&: &QuestionAnswerListV2&}">
90 后、宅男、码痴以及强迫症患者。?…
说了那么多你其实就缺个教程,什么长篇大论的我相信你激动万分的看完还是不知道发生了什么,在你会 go 之前也一定不明白那些牛逼的应用为啥牛逼,所以,广告开始:- 《Go编程基础》 - 《Go Web基础》 - 《Go名库讲解》
我是从python转向go的. 起始版本是Go 1.1.1
主要看, , 2 熟悉package,
package : strconv, net/http, net/url, string, math, time3 练习, project eular. 每学一门新语言的时候, 可以来做一遍.4 语言特点:
select, channel, defer, goroutine, 静态类型(int, int64, 类型转换), 闭包, package的组织形式5 reflect6 阅读开源软件的源代码, nsq, martini等.7 使用go去做一个项目.
入门看官网的tutorial不错,整整74页 然后看这页里的各个链接,推荐顺序:包文档也要过一遍
,这样可以知道标准库能做什么,里面的example也不少。
热爱美食的程序员
静下心来,不要急躁,欲速则不达。建议你先看:逐个例子的看,慢慢的改例子,然后运行;确保自己理解教程中给出的每一行代码,以及说明文字。然后再去看:然后,你就可以用go做实际项目咯~
成功的习惯比成功更重要
我觉得学习一门语言最重要的就是做到三点,第一看基础知识,第二学习抄代码,第三学习写代码第一点,很多人都觉得上来就动手写,但是你基础的东西都没掌握,怎么写呢?欲速则不达,所以基础的东西还是必须要先掌握好.这里推荐你几个基础的入门材料:邢星翻译的我觉得你把这几个基础掌握之后就可以开始抄袭代码了,如果你之前有PHP的开发经验,那么也许我写的这本书对你了解golang有帮助, 第二点,我们很多时候开始写代码都是没什么思路,也无从下手,但是我们可以模仿别人写代码,上海俗话里面常说"吃大户,用大户,消灭大户",我们就是"看代码,抄代码,最后自己写代码".这里给你几个入门级别的代码看看学习一下: 很简单,就是学习他的路由怎么实现的,如何编写自己的路由 这是一个Go+ AngularJS的实现,看看如何做API应用最后我列一下你可以自己参考去实现的一些功能,我当初培训我们战虎班的同学就是用这些来一起学习的.日志分析IP库分析管理后台查看分析日志第三点,自己写代码,这个时候就是已经对golang有了一定的了解了,那么我们就可以开始做自己的项目了,做项目最想就是快速开发,那么我就推荐你 使用beego框架可以很快速的开发你的Web或者API应用 使用docker来做虚拟化 学习分布式 分布式应用这个阶段就是找各种东西用golang来写,多写就会理解越来越深入. 交流群:Go技术社区:
这是CMU分布式系统课程,对应有四个课程项目,全部要求用go实现.项目都有完整的说明和单元测试程序.题主可以做一做
我最近也在学习go 不过是学着玩的。除了上面asta谢的回答 我给你的建议是:先看 [无闻的go基础视频]() 另外他还有web系列的视频自己找 除了看视频 还要看看书啊 :) 基础学差不多了 就要动手写写东西比如命令行程序
web程序(做网站)
看你的个人爱好了 我当年学c时就是只知道在一个黑乎乎的window下弄命令行程序而放弃了继续学习--太枯燥了
由于当时还不知道有GUI图形界面编程这回事(呵呵 勿笑
如果没记错大家大学都学的是谭浩强的那本c语言吧 )之后的vc++(borland
c++) 课程直接就晕菜学某个语言要知道某个语言擅长那个领域 不能拿鸡蛋当石头用 要会扬长避短 最后找个目标项目(你所在领域的某些算法或者业务 可以用程序实现 ) 一点点积累 只有做项目才能串联所学的知识点 程序类型?命令行或者web程序
(api , 服务,
job类的程序估计你暂时碰不到)如果你对做网站感兴趣 可以做个个人blog 。 先从 func mian(){} 开始吧 不要试图一口吃个胖子一生二 二生三 三生万物
细胞是从一分化的 最后成为一个人体系统
后端工程师
我是从Python转到Go的,学了大概2个月, 期间写了大概2500行游戏服务器框架(),不包含测试代码,不包含业务逻辑代码。主要是学习写的,主要就是实现golang的actor模型,服务之间的消息发送,消息处理,定时器实现,广播实现,log模块,websocket实现,gate模块,debug
service实现,还有一些基础库,如lru算法,300行简单的orm(curd),敏感词过滤,还有一个从 的移植的go版本sorted set实现。所以我觉得你缺的是一个开始,开始写代码,研究代码,阅读代码,重构,反复。首先我想先回答一下你心里的疑惑,因为你没有C的基础,又手php影响很深,所以刚开始接触golang时有点不习惯。你可能会经常想做类比,在php中寻找golang的影子,但毕竟面向领域不一样,差别还是有点大,所以遇到新的概念要快速接受,而不是一直怀疑这玩意怎么用,然后做对比。golang中结构体不就类似php中的类嘛,php的成员变量不就是结构体成员嘛,只不过php把成员变量和方法用大括号括起来了嘛;golang中的接口不就类似php的接口嘛,只不过php要显示implemtns,golang是隐式实现而已。。。所以刚开始不要被纠结概念,快速接受,然后写demo。一开始阅读golang标准库对你不太适合,看过基础教程之后开始写代码,从自己最擅长的开始,你是搞php的,建议从web开始,从 的开始搞起,用beego做个小应用,先不要想如何牛逼,如何优美,先以功能实现为主。从操就业,这时一股熟悉的感觉弥漫心头,自信悄然而来,嘎嘎golang小kiss而已。注意,自信很重要,自信很重要,真的很重要,重要的话要说3遍。自信来了,代码也写了,期间欠的债也该还了。啥,欠谁债了,你别吓我,我没钱。哈哈,当然是欠golang的债了,前期你玩了人家,很爽自信也来了,人家当然要赖着你了,yes,就是到了深入交流的时候了。这个时候你需要认真看项目中用到的标准库实现原理,框架实现原理,学习代码方式,golang的原理,可以读读。这和谈恋爱一样,深入交流你才知道,她还会这个,还能这样,哈哈,get新技能。当然这个阶段是痛苦的,切记不要一股脑一直研究,不然会把你带入深渊拔不出来。这和谈恋爱一样,你天天纠结人家七大姑八大姨的,前男友,前女友的,人家能给你好脸色才怪。要慢慢来,遇到纠结的地方先放一放,过段时间回头再来。参与牛逼项目,写牛逼代码,重构,重构再重构,这个阶段会与上面阶段交错,此起彼伏。学东西就和谈恋爱一样,追到手之前艰辛,追到手开始一段时间很幸福,中间会有一段纠结与美好并存的旅途,能否走到最后,看你能否坚持,是否常思考。。。
苦非常,乐做家
来我牛……一干golang大牛伺候……逃~~
always beta
书籍、社区之类的上面的都答差不多了,初学者可以看看这个,新手遇到的了50个go语言的坑: ,里面很多的坑,我也在学的过程中遇到过,避免这50个坑,可以节省编译期间的大部分时间,不说了,填坑去了
15年IT人,什么都不缺,除了心眼。
少废话,多动手。我从去年11月接触到golang,已经上线三个实际项目了,第四个正在开发中。先买了许世伟的书,啃了一周左右,就开始动手干,边干边学效率是最高的。
一个初出茅庐的IT人
我会建议题主学Java或C#。现有的Go的大部分资料都是给有相关经验的开发人员看的,比如已经知道了什么是结构体,什么是接口,对数据类型有概念的人。相反,java和C#由于已经出来很多年了,有很多适合0基础的人看的书
《C++Primer 5th》强势审校
你需要看这个《对 Go 语言的综合评价》——王垠
我之前做过php, java. 语言, 工具而已, 用得多自然就会了, 我常和朋友开玩笑说学一门新语言知道for循环你就会了, 当然肯定没那么简单, 你知道for循环的话至少要知道 变量定义与初始化, if else, 数组.map肯定是要会的. 对于golang的interface, struct, goroutine 都要有了解而golang的标准库在用的时候去查.这种基本的语法刚开始不必太纠结, 也不用去背, 了解上面的最多半天就搞定了吧.然后, 就要自己做东西来加深对golang的使用程度了, 可以自己用golang做个网站, 或做个网站爬虫, 如果是学生何不用golang代替C做作业呢.--------------关键还是思路, 语言只是工具(用久了自然就熟, 很久不用也就生了). 当然要深入golang源码就除外了.
Golang 爱好者
用心去理解Golang的语言特性,它的优势和劣势; 清晰了解项目的需求,加上一些架构的思想,就能做出满足项目要求软件。
一个有理想的程序员
想成为Designer的Developer
从1.0之前就开始关注Go语言,当时的资料还不多,主要是看官方的文档。现在各种学习资源都有,选择起来方便很多。首先如果有C的基础,推荐《》,通篇基于代码讲解,通俗易懂,没废话。花上一个周末的时间,很快就能自己动手开始写Go代码。之后可以看看相关创始人的博客,比如《》,加深对Go语言内部机制的理解。最好还能补一下并发的相关知识(比如自旋锁、互斥锁等等的概念),虽然不一定用得上,但知道的话日后调试代码能省不少时间。有空的话还可以读读,我觉得这可以算是最易懂的几份Language Specification。这些都做过的话,对Go语言方方面可以说都有了解了,剩下就是多写,多看。相比其他语言,Go语言还是挺好学习的。
- 如果你不知道拿Go来做什么 - 语言基础
互联网/音乐/游戏/旅行
我同意以上答主观点,看教程肯定是不能省略的,你不想连语法功能都不知道就开搞吧?但是除了看书以外,强烈推荐写工程,这可不是脚本语言写几个文件练练手就行了去年就接触过golang,但只是翻了翻书,唯一的印象就是这个语言语法反人类:干嘛类型都放后面?连继承都没有?指针没有c++那么灵活,也没比java的引用好太多啊。。。总之除了吐槽也没然后了,今年正好又来了兴致,索性认真学一下,于是工作业余时间搞了一个小项目:EasyProxy:把array,slice,channel,map,struct,interfae...全都用了一遍,把工程中需要用到的配置文件,日志,socket,http,json等也都搞了一遍在这个小应用第一版结束时对golang的感觉比较清晰了,而不像之前仅仅看书那样有点模糊总之:对程序员来说,对写代码就对了
一个菜鸟,啥都不会
这个问题挺早的,那个时候还没有又全面又透彻的 Go 入门书。现在的话书最推荐The Go Programming Language,就是The C Programming Language的那个作者柯宁汉写的,中文版地址在看完这本书之后 Go 的各式语法就了解得差不多了,关于最佳实践请看 Effective Go 和 Go in practice, 前者是 Go 官方提供的语言注意事项,后者的涉猎面更广泛,作者似乎也是一个知名库的作者。再然后就是深入
Go 的内部实现,由于 Go 迭代很快,很多资料都过时了,目前值得一看的就是雨痕的 Go 学习笔记。但是这本书是针对 Go 1.5 和 1.6
的,1.7 和 1.8 又有很大的变动,所以最好的了解办法是去看 Go 的 proposal, 设计文档和 issue。
.zm-item-answer&}" data-init="{&params&: {&url_token&: , &pagesize&: 10, &offset&: 0}, &nodename&: &QuestionAnswerListV2&}">
我来回答这个问题写回答…我要回答
与世界分享你的知识、经验和见解
已有帐号?
人关注该问题
相关 Live 推荐 ??
? 2017 知乎
发表评论:
馆藏&21404
TA的最新馆藏王垠:如何掌握程序语言 - 文章 - 伯乐在线
& 王垠:如何掌握程序语言
学习程序语言是每个程序员的必经之路。可是这个世界上有太多的程序语言,每一种都号称具有最新的“特性”。所以程序员的苦恼就在于总是需要学习各种稀奇古怪的语言,而且必须紧跟“潮流”,否则就怕被时代所淘汰。
作为一个程序语言的研究者,我深深的知道这种心理产生的根源。程序语言里面其实有着非常简单,永恒不变的原理。看到了它们,就可以在很短的时间之内就能学会并且开始使用任何新的语言,而不是花费很多功夫去学习一个又一个的语言。
对程序语言的各种误解
学习程序语言的人,经常会出现以下几种心理,以至于他们会觉得有学不完的东西,或者走上错误的道路。以下我把这些心理简要分析一下。
1. 程序语言无用论。这是国内大学计算机系的教育常见的错误。教授们常常对学生灌输:“用什么程序语言不重要,重要的是算法。”而其实,程序语言却是比算法更加精髓的东西。任何算法以及它的复杂度分析,都是相对于某种计算模型,而程序语言就是描述这种计算模型的符号系统。算法必须用某种语言表述出来,通常算法设计者使用伪码,这其实是不严谨的,容易出现推理漏洞。算法设计再好,如果不懂得程序语言的原理,也不可能高效的实现。即使实现了,也可能会在模块化和可扩展性上面有很大问题。某些算法专家或者数学家写出来的程序极其幼稚,就是因为他们忽视了程序语言的重要性。
2. 追求“新语言”。基本的哲学告诉我们,新出现的事物并不一定是“新事物”,它们有可能是历史的倒退。事实证明,新出现的语言,可能还不如早就存在的。其 实,现代语言的多少“新概念”不存在于最老的一些语言里呢?程序语言就像商品,每一家都为了拉拢程序员作广告,而它们绝大多数的设计都可能是肤浅而短命 的。如果你看不透这些东西的设计,就会被它们蒙蔽住。很多语言设计者其实并不真的懂得程序语言设计的原理,所以常常在设计中重复前人的错误。但是为了推销 自己的语言和系统,他们必须夸夸其谈,进行宗教式的宣传。
3. “存在即是合理”。记得某人说过:“不能带来新的思维方式的语言,是没有必要存在的。”他说的是相当正确的。世界上有这么多的语言,有哪些带来了新的思维 方式呢?其实非常少。绝大部分的语言给世界带来的其实是混乱。有人可能反驳说:“你怎么能说 A 语言没必要存在?我要用的那个库 L,别的语言不支持,只能用 A。”但是注意,他说的是存在的“必要性”。如果你把存在的“事实”作为存在的“必要性”,那就逻辑错乱了。就像如果二战时我们没能打败希特勒,现在都做 了他的奴隶,然后你就说:“希特勒应该存在,因为他养活了我们。”你的逻辑显然有问题,因为如果历史走了另外一条路(即希特勒不存在),我们会过上自由幸 福的生活,所以希特勒不应该存在。对比一个东西存在与不存在的两种可能的后果,然后做出判断,这才是正确的逻辑。按照这样的推理,如果设计糟糕的 A 语言不存在,那么设计更好的 B 语言很有可能就会得到更多的支持,从而实现甚至超越 L 库的功能。
4. 追求“新特性”。程序语言的设计者总是喜欢“发明”新的名词,喜欢炒作。普通程序员往往看不到,大部分这些“新概念”其实徒有高深而时髦的外表,却没有实 质的内涵。常常是刚学会一个语言 A,又来了另一个语言 B,说它有一个叫 XYZ 的新特性。于是你又开始学习 B,如此继续。在内行人看来,这些所谓的“新特性”绝大部分都是新瓶装老酒。很多人写论文喜欢起这样的标题:《XYZ:A Novel Method for …》。这造成了概念的爆炸,却没有实质的进步。
5. 追求“小窍门”。很多编程书喜欢卖弄一些小窍门,教你如何让程序显得“短小”。比如它们会跟你讲 “(i++) – (++i)” 应该得到什么结果;或者追究运算符的优先级,说这样可以少打括号;要不就是告诉你“if 后面如果只有一行代码就可以不加花括号”,等等。殊不知这些小窍门,其实大部分都是程序语言设计的败笔。它们带来的不是清晰的思路,而是是逻辑的混乱和认 知的负担。比如 C 语言的 ++ 运算符,它的出现是因为 C 语言设计者们当初用的计算机内存小的可怜,而 “i++” 显然比 “i=i+1” 少 2 个字符,所以他们觉得可以节省一些空间。现在我们再也不缺那点内存,可是 ++ 运算符带来的混乱和迷惑,却流传了下来。现在最新的一些语言,也喜欢耍这种语法上的小把戏。如果你追求这些小窍门,往往就抓不住精髓。
6. 针对“专门领域”。很多语言没有新的东西,为了占据一方土地,就号称自己适合某种特定的任务,比如文本处理,数据库查询,WEB编程,游戏设计,并行计算。但是我们真的需要不同的语言来干这些事情吗?其实绝大部分这些事情都能用同一种通用语言来解决,或者在已有语言的基础上做很小的改动。只不过由于各种政治和商业原因,不同的语言被设计用来占领市场。就学习而言,它们其实是无关紧要的,而它们带来的“学习负担”,其实差不多掩盖了它们带来的好处。其实从一些设计良好的通用语言,你可以学会所有这些“专用语言”的精髓,而不用专门去学它们。
7. 宗教信仰。很多人对程序语言有宗教信仰。这跟人们对操作系统有宗教信仰很类似。其实如果你了解程序语言的本质,就会发现其实完全没必要跟人争论一些事情。某个语言有缺点,应该可以直接说出来,却被很多人忌讳,因为指出缺点总是招来争论和憎恨。这原因也许在于程序语言的设计不是科学,它类似于圣经,它没法被“证伪”。没有任何实验可以一下子断定那种语言是对的,那种是错的。所以虽然你觉得自己有理,却很难让人信服。没有人会去争论哪家的汉堡更好,却有很多人争论那种语言更好。因为很多人把程序语言当成自己的神,如果你批评我的语言,你就是亵渎我的神。解决的办法也许是,不要把自己正在用的语言看得太重要。你现在认为是对的东西,也许不久就会被你认为是错的,反之亦然。
如何掌握程序语言
看到了一些常见的错误心理,那么我们来谈一下什么样的思维方式会更加容易的掌握程序语言。
1. 专注于“精华”和“原理”。就像所有的科学一样,程序语言最精华的原理其实只有很少数几个,它们却可以被用来构造出许许多多纷繁复杂的概念。但是人们往往 忽视了简单原理的重要性,匆匆看过之后就去追求最新的,复杂的概念。他们却没有注意到,绝大部分最新的概念其实都可以用最简单的那些概念组合而成。而对基 本概念的一知半解,导致了他们看不清那些复杂概念的实质。比如这些概念里面很重要的一个就是递归。国内很多学生对递归的理解只停留于汉诺塔这样的程序,而 对递归的效率也有很大的误解,认为递归没有循环来得高效。而其实递归比循环表达能力强很多,而且效率几乎一样。有些程序比如解释器,不用递归的话基本没法完成。
2. 实现一个程序语言。学习使用一个工具的最好的方式就是制造它,所以学习程序语言的最好方式就是实现一个程序语言。这并不需要一个完整的编译器,而只需要写 一些简单的解释器,实现最基本的功能。之后你就会发现,所有语言的新特性你都大概知道可以如何实现,而不只停留在使用者的水平。实现程序语言最迅速的方式就是使用一种像 Scheme 这样代码可以被作为数据的语言。它能让你很快的写出新的语言的解释器。我的 GitHub 里面有一些我写的解释器的例子(比如这个短小的实现了 Haskell 的 lazy 语义)。
几种常见风格的语言
下面我简要的说一下几种常见风格的语言以及它们的问题。
1. 面向对象语言
事实说明,“面向对象”这整个概念基本是错误的。它的风靡是因为当初的“软件危机”(天知道是不是真的存在这危机)。 设计的初衷是让“界面”和“实现”分离,从而使得下层实现的改动不影响上层的功能。可是大部分面向对象语言的设计都遵循一个根本错误的原则:“所有的东西 都是对象(Everything is an object)。”以至于所有的函数都必须放在所谓的“对象”里面,而不能直接被作为参数或者变量传递。这导致很多时候需要使用繁琐的设计模式 (design patterns) 来达到甚至对于 C 语言都直接了当的事情。而其实“界面”和“实现”的分离,并不需要把所有函数都放进对象里。另外的一些概念,比如继承,重载,其实带来的问题比它们解决的 还要多。
“面向对象方法”的过度使用,已经开始引起对整个业界的负面作用。很多公司里的程序员喜欢生搬硬套一些不必要的设计模式,其实什么好事情也没干,只是使得程序冗长难懂。
那 么如何看待具备高阶函数的面向对象语言,比如 Python, JavaScript, Ruby, Scala? 当然有了高阶函数,你可以直截了当的表示很多东西,而不需要使用设计模式。但是由于设计模式思想的流毒,一些程序员居然在这些不需要设计模式的语言里也采用繁琐的设计模式,让人哭笑不得。所以在学习的时候,最好不要用这些语言,以免受到不必要的干扰。到时候必要的时候再回来使用它们,就可以取其精华,去其糟粕。
2. 低级过程式语言
那么是否 C 这样的“低级语言”就会好一些呢?其实也不是。很多人推崇 C,因为它可以让人接近“底层”,也就是接近机器的表示,这样就意味着它速度快。这里其实有三个问题:
1) 接近“底层”是否是好事?
2)“速度快的语言”是什么意思?
3) 接近底层的语言是否一定速度快?
对于第一个问题,答案是否定的。其实编程最重要的思想是高层的语义(semantics)。语义构成了人关心的问题以及解决它们的算法。而具体的实现 (implementation),比如一个整数用几个字节表示,虽然还是重要,但却不是至关重要的。如果把实现作为学习的主要目标,就本末倒置了。因为 实现是可以改变的,而它们所表达的本质却不会变。所以很多人发现自己学会的东西,过不了多久就“过时”了。那就是因为他们学习的不是本质,而只是具体的实 现。
其次,谈语言的“速度”,其实是一句空话。语言只负责描述一个程序,而程序运行的速度,其实绝大部分不取决于语言。它主要取决于 1)算法 和 2)编译器的质量。编译器和语言基本是两码事。同一个语言可以有很多不同的编译器实现,每个编译器生成的代码质量都可能不同,所以你没法说“A 语言比 B 语言快”。你只能说“A 语言的 X 编译器生成的代码,比 B 语言的 Y 编译器生成的代码高效”。这几乎等于什么也没说,因为 B 语言可能会有别的编译器,使得它生成更快的代码。
我举个例子吧。在历史上,Lisp 语言享有“龟速”的美名。有人说“Lisp 程序员知道每个东西的值,却不知道任何事情的代价”,讲的就是这个事情。但这已经是很久远的事情了,现代的 Lisp 系统能编译出非常高效的代码。比如商业的 Chez Scheme 编译器,能在5秒钟之内编译它自己,编译生成的目标代码非常高效。它可以直接把 Scheme 程序编译到多种处理器的机器指令,而不通过任何第三方软件。它内部的一些算法,其实比开源的 LLVM 之类的先进很多。
另外一些 函数式语言也能生成高效的代码,比如 OCaml。在一次程序语言暑期班上,Cornell 的 Robert Constable 教授讲了一个故事,说是他们用 OCaml 重新实现了一个系统,结果发现 OCaml 的实现比原来的 C 语言实现快了 50 倍。经过 C 语言的那个小组对算法多次的优化,OCaml 的版本还是快好几倍。这里的原因其实在于两方面。第一是因为函数式语言把程序员从底层细节中解脱出来,让他们能够迅速的实现和修改自己的想法,所以他们能 够迅速的找到更好的算法。第二是因为 OCaml 有高效的编译器实现,使得它能生成很好的代码。
从上面的例子,你也许已经可以看出,其实接近底层的语言不一定速度就快。因为编译器这种东西其实可以有很高级的“智能”,甚至可以超越任何人能做到的底层优化。但是编译器还没有发展到可以代替人来制造算法的地步。所以现在人需要做的,其实只是设计和优化自己的高层算法。
3. 高级过程式语言
很早的时候,国内计算机系学生的第一门编程课都是 Pascal。Pascal 是很不错的语言,可是很多人当时都没有意识到。上大学的时候,我的 Pascal 老师对我们说:“我们学校的教学太落后了。别的学校都开始教 C 或者 C++ 了,我们还在教 Pascal。”现在真正理解了程序语言的设计原理以后我才真正的感觉到,原来 Pascal 是比 C 和 C++ 设计更好的语言。它不但把人从底层细节里解脱出来,没有面向对象的思维枷锁,而且有一些很好的设计,比如强类型检查,嵌套函数定义等等。可是计算机的世界 真是谬论横行,有些人批评 Pascal,把优点都说成是缺点。比如 Brain Kernighan 的这篇《》,现在看来真是谬误百出。Pascal 现在已经几乎没有人用了。这并不很可惜,因为它被错怪的“缺点”其实已经被正名,并且出现在当今最流行的一些语言里:Java, Python, C#, ……
4. 函数式语言
函数式语言相对来说是当今最好的设计,因为它们不但让人专注于算法和对问题的解决,而且没有面向对象语言那些思维的限制。但是需要注意的是并不是每个函数式语言的特性都是好东西。它们的支持者们经常把缺点也说成是优点,结果你其实还是被挂上一些不必要的枷锁。比如
OCaml 和 SML,因为它们的类型系统里面有很多不成熟的设计,导致你需要记住太多不必要的规则。
5. 逻辑式语言
逻辑式语言(比如 Prolog)是一种超越函数式语言的新的思想,所以需要一些特殊的训练。逻辑式语言写的程序,是能“反向运行”的。普通程序语言写的程序,如果你给它一个输入,它会给你一个输出。但是逻辑式语言很特别,如果你给它一个输出,它可以反过来给你所有可能的输入。其实通过很简单的方法,可以不费力气的把程序从函数式转换成逻辑式的。但是逻辑式语言一般要在“pure”的情况下(也就是没有复杂的赋值操作)才能反向运行。所以学习逻辑式语言最好是从函数式语言开始,在理解了递归,模式匹配等基本的函数式编程技巧之后再来看 Prolog,就会发现逻辑式编程简单了很多。
可是学习编程总要从某种语言开始。那么哪种语言呢?就我的观点,首先可以从 Scheme 入门,然后学习一些 Haskell (但不是全部),之后其它的也就触类旁通了。你并不需要学习它们的所有细枝末节,而只需要学习最精华的部分。所有剩余的细节,会在实际使用中很容易的被填补上。现在我推荐几本比较好的书。
《》(TLS):我觉得 Dan Friedman 的 The Little Schemer 是目前最好,最精华的编程入门教材。这本书很薄,很精辟。它的前身叫《The Little Lisper》。很多资深的程序语言专家都是从这本书学会了 Lisp。虽然它叫“The Little Schemer”,但它并不使用 Scheme 所有的功能,而是忽略了 Scheme 的一些毛病,直接进入最关键的主题:递归和它的基本原则。
《Structure and Interpretation of Computer Programs | 计算机程序的构造和解释》(SICP):The Little Schemer 其实是比较难的读物,所以我建议把它作为下一步精通的读物。SICP 比较适合作为第一本教材。但是我需要提醒的是,你最多只需要看完前三章。因为从第四章开始,作者开始实现一个 Scheme 解释器,但是作者的实现并不是最好的方式。你可以从别的地方更好的学到这些东西。不过也许你可以看完 SICP 第一章之后就可以开始看 TLS。
《》:对于 Haskell,我最开头看的是 A Gentle Introduction to Haskell,因为它特别短小。当时我已经会了 Scheme,所以不需要再学习基本的函数式语言的东西。我从这个文档学到的只不过是 Haskell 对于类型和模式匹配的概念。
过度到面向对象语言
那么如果从函数式语言入门,如何过渡到面向对象语言呢?毕竟大部分的公司用的是面向对象语言。如果你真的学会了函数式语言,就会发现面向对象语言已经易如反掌。函数式语言的设计比面向对象语言简单和强大很多,而且几乎所有的函数式语言教材(比如 SICP)都会教你如何实现一个面向对象系统。你会深刻的看到面向对象的本质以及它存在的问题,所以你会很容易的搞清楚怎么写面向对象的程序,并且会发现 一些窍门来避开它们的局限。你会发现,即使在实际的工作中必须使用面向对象语言,也可以避免面向对象的思维方式,因为面向对象的思想带来的大部分是混乱和冗余。
深入本质和底层
那么是不是完全不需要学习底层呢?当然不是。但是一开头就学习底层硬件,就会被纷繁复杂的硬件设计蒙蔽头脑,看不清楚本质上简单的原理。在学会高层的语言之后,可以进行“语义学”和“编译原理”的学习。
简言之,(semantics) 就是研究程序的符号表示如何对机器产生“意义”,通常语义学的学习包含 lambda calculus 和各种解释器的实现。编译原理 (compilation) 就是研究如何把高级语言翻译成低级的机器指令。编译原理其实包含了计算机的组成原理,比如二进制的构造和算术,处理器的结构,内存寻址等等。但是结合了语义学和编译原理来学习这些东西,会事半功倍。因为你会直观的看到为什么现在的计算机系统会设计成这个样子:为什么处理器里面有寄存器 (register),为什么需要堆栈(stack),为什么需要堆(heap),它们的本质是什么。这些甚至是很多硬件设计者都不明白的问题,所以它们的硬件里经常含有一些没必要的东西。因为他们不理解语义,所以经常不明白他们的硬件到底需要哪些部件和指令。但是从高层语义来解释它们,就会揭示出它们的本质,从而可以让你明白如何设计出更加优雅和高效的硬件。
这就是为什么一些程序语言专家后来也开始设计硬件。比如 Haskell 的创始人之一 Lennart Augustsson 后来设计了 BlueSpec,一种高级的硬件描述语言,可以 100% 的合成 (synthesis) 为硬件电路。Scheme 也被广泛的使用在硬件设计中,比如 Motorola, Cisco 和曾经的 Transmeta,它们的芯片设计里面含有很多 Scheme 程序。
这基本上就是我对学习程序语言的初步建议。以后可能会就其中一些内容进行更加详细的阐述。
可能感兴趣的话题
对于这篇文章,我需要做出一个很简单的抉择:信他,和不信他……我选择了不信,我信仰C语言,谢谢~~~
关于伯乐在线博客
在这个信息爆炸的时代,人们已然被大量、快速并且简短的信息所包围。然而,我们相信:过多“快餐”式的阅读只会令人“虚胖”,缺乏实质的内涵。伯乐在线内容团队正试图以我们微薄的力量,把优秀的原创文章和译文分享给读者,为“快餐”添加一些“营养”元素。
新浪微博:
推荐微信号
(加好友请注明来意)
– 好的话题、有启发的回复、值得信赖的圈子
– 分享和发现有价值的内容与观点
– 为IT单身男女服务的征婚传播平台
– 优秀的工具资源导航
– 翻译传播优秀的外文文章
– 国内外的精选文章
– UI,网页,交互和用户体验
– 专注iOS技术分享
– 专注Android技术分享
– JavaScript, HTML5, CSS
– 专注Java技术分享
– 专注Python技术分享
& 2017 伯乐在线

我要回帖

更多关于 王垠 新浪博客 的文章

 

随机推荐