Go!

 如果是入门级的中文文档资料,已经非常丰富,在google code上面有专门的翻译小组,CSDN也有中文手册下载,相关中文书籍也有出版。这里有免费的https://github.com/astaxie/build-web-application-with-golang/blob/master/ebook/preface.md 
库和框架有,确实不多,毕竟出来没几年,但前景不可限量。

 

http://www.mikespook.com/learning-go/

------------------------------------------------------------------

http://www.oschina.net/translate/migrating-code-from-python-to-golang-what-you-need-to-know

这是一篇记录了我们把一大段 Python/Cython 代码迁移到 Go语言经历的(长)博客。如果你希望了解整个故事、背景等所有的内容,请接着读。如果只对 Python 开发者进入之前需要知道的东西感兴趣,跳到“我们早该知道的事情”一章。

背景

我们在 Repustate 技术上最大的成果就是实现了阿拉伯语情感分析。阿拉伯语真是一个难啃的骨头,它的单词语法形态太复杂了。阿拉伯语的分词(tokenization,把一个句子分成独立的词语)相比诸如英语更难,因为阿拉伯语单词内部可能包含空格(例如,aleph内的位置)。这个不需要保密,那就是 Repustate 使用了支持向量机(SVM)来得到句子最可能的意思,然后在此基础上分析情感。我们总共用了 22 个模型(22 个支持向量机),文档中的每个单词都会被分析。也就是说,如果一篇文档包含 500 词的话,将会有一万次以上的支持向量机的比较操作。

daviddengcn
daviddengcn
翻译于 1个月前

0人顶

 

 翻译的不错哦!

Python

Repustate几乎是彻底用 Python 实现的,因为我们使用了 Django 作为应用程序接口和网站架构。所以只能保持代码的统一,同时用 Python 实现整个阿拉伯语情感引擎。原型与实现的过程中,Python 还是很不错的。非常强的表达能力,强大的第三方库资源。如果你只是服务于网页的话,还是很完美的。但是,当你需要进行底层计算,需要在散列表(Python 中的字典)上进行大量的比较运算的时候,速度就慢下来了。我们每秒钟只能处理2到3篇阿拉伯语文档,这太慢了。对比我们的英语情感引擎,每秒钟能处理 500 篇文档。

瓶颈

于是,我们启动了 Python 分析器,研究哪部分执行得慢。还记得我说过我们会用 22 个支持向量机处理每一个单词吗?这些处理都是串行的,没有并行操作。好,我们第一个想法是把这个改成类似 map/reduce 的操作。长话短说:Python 中不适合使用 map/reduce。当你需要并发性的时候,Python 一点都不好用。2013 年的 PyCon 大会上,Guido 提到了 Tulip,他试图解决这个问题的新项目,但是还需要一段时间才能推出。如果已经有更好的选择,我们为什么还要等它呢。

daviddengcn
daviddengcn
翻译于 1个月前

0人顶

 

 翻译的不错哦!

换 Go 语言还是回家种田

我在 Mozilla 的朋友告诉我,Mazilla 服务中日志架构的大部分代码已经切换成 Go 了,部分原因是 goroutine(Go 协程)的强大。Go 是 Google 的一群人设计的,它把并行性作为一级概念,而不像 Python 的不同解决方案做的事后补充。于是,我们开始着手把 Python 换成 Go。

尽管 Go 代码还没达到产品级别,其结果已经非常令人鼓舞了。我们达到了每秒 1000 文档的速度,使用了更少的内存,还不用去处理用 Python 时碰到的多进程/gevent/“为什么 Ctrl+C 杀掉了我的进程”代码等讨厌的问题。

daviddengcn
daviddengcn
翻译于 1个月前

0人顶

 

 翻译的不错哦!

我们为什么爱上了Go

只要知道一点儿编程语言工作原理的人,(明白解释和编译以及动态与静态的区别,就会说:“老兄,Go 显然会更快。没错,我们也可以把整个东西用 Java 来重写,并且得到类似的性能,但这不是 Go 胜出的原因。你用 Go 写出代码来就很容易是正确的。我也说不清楚怎么回事,但是一旦代码编译通过(编译速度还很快),你就感觉到它可以工作了(不只是运行不提示错误,而是逻辑上就是对的)。我知道这听起来很玄乎,但确实是事实。这就像 Python 解决冗余问题(或者说无冗余),它把函数作为一级对象,从而函数编程可以轻松的进行。go线程和通道(channel)让你的生活如此轻松。你还可以得到静态类型带来的性能提升,更精确的控制内存分配,却不会因此损失表达性。

daviddengcn
daviddengcn
翻译于 1个月前

0人顶

 

 翻译的不错哦!

我们早该知道的事情

除去那些赞美之词,用 Go 的时候需要一种不同于用 Python 时的心态。下面是一些迁移时候的笔记,把 Python 转成 Go 时随机跃入我脑子的东西:

  • 没有内建的集合类型(需要使用 map 然后检查存在性)
  • 由于没有集合类型,需要自己实现交集、并集等方法
  • 没有元组(tuple),需要设计自己的结构(struct)或者使用slice(类似数组)
  • 没有类似 __getattr_() 的方法,需要你检查存在性而不能设置缺省值,例如 Python 中,你可以这么写:value = dict.get("a_key", "default_value")
  • 需要检查错误(或者至少显式的忽略它们)
  • 不能够有未使用的变量和包,需要时不时的注释掉一些代码
  • 在 []byte 和 string 之间切换,正则处理(regexp)使用 []byte(可改写的)。这是对的,但转换来转换去还是很麻烦
  • Python 语法更宽松。你可以用超出范围的索引取字符串的片段而不出错,也可以使用负数取片段。Go 就不行。
  • 无法使用混合类型的数据结构。这可能不一定合适,但是 Python 中有时候我会有一个取值可以是字符串和列表混合的字典。Go 里不行,你必须清理里的数据结构或者自定义结构*
  • 没法把元组或者列表分配成分开的变量(例如,x, y, x = [1, 2, 3])
  • 驼峰式大小写习惯(首字母不大写的函数/结构不会暴露给其他包)。我更喜欢 Python 的小写加下划线的习惯。
  • 必须显式的检查错误是否为空,不像 Python 中很多类型都可以像布尔类型一样的用(0,空串,None 都可以作为布尔“假”)
  • 一些模块(如 crypo/md5)的文档不足,但是 IRC 上的 go-nuts 很厉害,有强大的支持
  • 数字转字符串(int64->string)与 []byte 转字符串(只要 string([]byte))不同,前者需要调用 strconv
  • 读 Go 的代码绝对像是编程语言,而 Python 可以写成像是伪代码一样。Go 使用更多的非英文数字字符,使用 || 和 && 而不是 or 和 and。
  • 写文件会有 File.Write([]byte) 和 File.WriteString(string),与 Python 开发者的一种办法解决问题的信条不一致。
  • 字符串插入不好用,必须经常使用 fmt.Sprintf
  • 没有构造函数,通常的习惯是写一个 NewType() 函数返回你要的结构
  • Else(或者 else if)得正确的格式化,else 得和与 if 配对的大括号在一行。奇怪。
  • 函数内外使用不同的赋值操作符,= 和 := (译者注此为作者的误解,:= 是自动类型推导定义变量+赋值,= 则只是赋值,原文作者碰到的问题是函数外不能用 := ,只能 var + = 定义全局变量;函数内部其实两种操作符都是可以使用的)
  • 如果我只想要键值(dict.keys())或取值(dict.values())的列表,或者元组的列表(dict.items()),Go 中没有对应的函数,只能自己迭代 map 然后填列表
  • 我常用的一种写法是通过建立一个取值为函数的字典,通过键值调用相应的函数。在 Go 里面可以实现,但是函数的输入输出必须相同,也就是函数签名必须一致。*
  • 如果你使用 JSON,并且 JSON 是混合类型的,那么你中招了。你必须建立一个匹配 JSON 数据格式的自定义结构,然后把 JSON 解析成这个自定义的结构。相比 Python 时候的一句 obj = json.loads(json_blog) 多了很多工作。*

*译者注:以上很多条都是因为作者没有使用 Go 中的 interface{} 造成的)

这次折腾值吗?

绝对值,一百万个值!速度的提升简直太惊人了!并且我还想强调一下,Go 是一个现在时髦的语言,所以再招新人的时候,让 Go 成为 Repustate 的重要技术支持会很重要。

 ===========================================

http://www.mikespook.com/2011/02/%E7%BF%BB%E8%AF%91go-%E5%92%8C-python-%E7%9A%84-web-%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%80%A7%E8%83%BD%E5%AF%B9%E6%AF%94/

原文在此:http://ziutek.github.com/web_bench/

由于是早上看到 鱼哥,在推上的推荐,我实在忍不住……这是中午的草率之举,所以鱼哥 对本文的翻译负全责。

PS:别说我工作状态不饱满,我在等丫的程序执行完……

————翻译分割线————

Go 和 Python Web 服务器性能对比

我通常使用 Python 来构建 Web 应用。一年前,在兴趣的驱使下,我开始学习 Go。 在此期间,我重写了一些原本由 C 开发的 CGI 应用,包括运行于 chroot 环境下的同thttpd 服务器一起的应用。我开始寻找可以开发易于 chroot、且内置 Web 服务器的独立 Web 应用的工具。那时,我开始玩 web.go 框架、mustache.go 模板、Go 原生 http 包和 GoMySQL 数据库 API。我发现,有 http、mustache.go  GoMySQL 包的 Go 可以是我用来工作的不错的工具组合。因此,我决定使用 Go 编写我的应用。

在工作过程中发现,我需要比 mustache.go 更加灵活,比 GoMySQL 更加成熟、没有那么多 Bug 的东西。最终,我使用 Kasia.go 模板和 MyMySQL (为我的应用定制开发的包,不过我将其贡献给了 Go 社区)。重写的应用即便是在比以前的负载更高的运营环境下,也工作得很好。我开始思考这个问题:用 Go 实现独立 Web 应用比 Python 到底快了(或者是慢了)多少。我决定做一些各种框架和服务器不同的用途的测试。为了比较,我选择了下面的 Go 包:

  • 原始的 Go http包;
  • web.go 框架(它使用运行于独立模式[standalone mode] 的 http 包);
  • twister 框架 (它同样使用 http 包)。

和下面的 Python Web服务器/框架:

  • 使用 CherryPy WSGI 服务器的 web.py 框架;
  • 使用 flup FastCGI 做 nginx 服务器的后台处理的 web.py 框架;
  • tornado 异步服务器/框架;
  • nginx 做负载均衡的 tornado。

每一个用例,我都编写了一个小应用,略微复杂一些的、传统的 Hello World 例子。任何应用都包括:

  • 使用正则表达式通过 URL 路径传递参数;
  • 使用语句创建多行输出;
  • 使用 printf 形式的格式化函数/表达式格式化输出。

我想,这些都是在 Web 应用中常见的操作,所以应当包含在任何简易的性能对比测试中。所有测试应用的代码在下面的链接中:

测试环境

测试环境包括两台 使用千兆以太网链接的PC (请求发起者和应用服务器)。

  • 请求发起者:2 x Xeon 2.6 GHz with hyperthreading, Debian SID, kernel: 2.6.33.7.2-rt30-1-686 #1 SMP PREEMPT RT;
  • 服务器: MSI Netbook with two core Intel U4100 1.30GHz, AC power connected, 64-bit Ubuntu 10.10, kernel: 2.6.35-25-generic #44-Ubuntu SMP, Python 2.6.6-2ubuntu2, web.py 0.34-2, flup 1.0.2-1, tornado 0.2-1, nginx 0.7.67-3ubuntu1;

为了产生 HTTP 请求并且评估测试应用的性能,我使用 siege 性能测试工具。Siege 可以用多线程模拟多个用户。我使用了下面的命令产生请求:

siege -c 200 -t 20s http://SERVER_ADDR:8080/Hello/100

或者多个类似的命令,减少参数 -c 的量(在这个测试中,我同时运行了多个 Python 脚本)。它模拟了 200 用户的请求,并持续 20 秒。这个 URL 使得 Web 应用对每个请求都输出 100 行。Go 应用使用 Go 发布版 2011-02-01.1。

结果

GOMAXPROCS=1, 一个 Python 进程:

框架请求速率 [1/sec]
Go http 1350  
Twister 1324  
Web.go 1141  
Tornado 882  
Tornado+nginx 862  
Web.py+CheryPy 169  
Web.py+nginx 114  

GOMAXPROCS=2, 两个 Python 并发进程:

框架请求速率 [1/sec]
Go http 1768  
Twister 1746  
Tornado 1682  
Web.go 1516  
Tornado+nginx 1378  
Web.py+CheryPy 308  
Web.py+nginx 198  

GOMAXPROCS=4, 四个 Python 并发进程:

框架请求速率 [1/sec]
Go http 2063  
Twister 2020  
Web.go 1753  
Tornado 1662  
Tornado+nginx 1364  
Web.py+CheryPy 304  
Web.py+nginx 211  

Web.py+nginx 工作的 flup FastCGI 选项:multiplexed=False, multithreaded=False。如果 multiplexed=True 它会运行得慢一些。如果 multithreaded=True 而只有一个进程服务于 nginx 服务器,会报下面的错误:

[error] 18166#0: *66139 connect() to
unix:/home/michal/Programowanie/web_bench/socket failed (11: Resource
temporarily unavailable) while connecting to upstream

FastCGI 的多进程由 spawn-fcgi 产生。

结论

你可以看到 Go 赢得了几乎所有的测试用例。web.go 框架的那个不太理想的结果可能是由于它先尝试用指定的 URL 寻找静态页面,然后才会执行处理方法。让我惊讶的是 tornado Python 框架如此之高的性能,尤其是跟 web.py 框架相比而言。我同样对 CherryPy 服务器比 nginx+flup 快感到惊讶 (我使用 web.py+flup+nginx 跑几乎所有的 Python Web 应用)。

 

=============================================

 http://tonychee1989.diandian.com/Python-C-Go-test

数学上有个关于圆周率的近似值计算公式:

π≈4×(1-1/3+1/5-1/7……)

后面多项式的值越多,最终的结果就越精确,于是,我写了3个程序:

  1. Python:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #!/usr/bin/env python3
                                                                           
    import sys
                                                                           
    n=int(sys.argv[1])
    n+=0 if n%2==1 else 1
    sign=1
    t=0
    for i in range(1,n+2,2):
        t+=(1/i)*sign
        sign=-sign
                                                                           
    s=4*t
    print ('\N{GREEK SMALL LETTER PI}={}'.format(s))
  2. C:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #include <stdio.h>
                                                                          
    int n,i;
    int sign=1;
    double t,s;
                                                                          
    int main(int argc,char ** argv)
    {
            n=atoi(argv[1]);
            n+=(n%2==1?0:1);
            for(i=1;i<n+2;i+=2)
            {
                    t+=(1.0f/(double)i)*sign;
                    sign=-sign;
            }
            s=4*t;
            printf("pi=%f\n",s);
    }
  3. Go:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    package main
                                                                        
    import "fmt"
    import "os"
    import "strconv"
                                                                        
    var(
            n int
            i int
            sign int =1
            t float64
            s float64
    )
                                                                        
    func main(){
            args:=os.Args
            n,_=strconv.Atoi(args[1])
            if n%2==0 {
                    n+=1
            }
            for i=1;i<n+2;i+=2 {
                    t+=(1.0/float64(i))*float64(sign)
                    sign=-sign
            }
            s=4*t
            fmt.Printf("pi=%f\n",s)
    }

    吐槽一下为什么我在Go语言中没有发现过选择运算符(?:)??

另外,为便于测试,我同时还写了一个shell脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash
#test.sh
                                                            
NUM=$1
                                                            
if [ ! $NUM ]; then
        echo -e '! $NUM defaults 1000000 !\n'
        NUM=1000000
fi
                                                            
echo -e '\n==Python pi.py==\n'
python3 --version
time ./pi.py $NUM
                                                            
echo -e '\n==C Lang pi_c==\n'
gcc --version|grep gcc
time ./pi_c $NUM
                                                            
echo -e '\n==Go Lang pi_go==\n'
go version
time ./pi_go $NUM

那么,接下来,就应该进行测试了,我想看看这三种语言执行效率怎样:

在终端输入命令:

1
./test.sh 100000000 2>&1|cat >report

最后,打开生成的report文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
==Python pi.py==
                                                 
Python 3.2.3
π=3.1415926735902504
                                                 
real    0m23.592s
user    0m23.381s
sys     0m0.188s
                                                 
==C Lang pi_c==
                                                 
gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
pi=3.141593
                                                 
real    0m1.096s
user    0m1.084s
sys     0m0.008s
                                                 
==Go Lang pi_go==
                                                 
go version go1
pi=3.141593
                                                 
real    0m1.254s
user    0m1.248s
sys     0m0.004s

图表的形式会直观一些:

从图中能看到:

  • Python在C面前就已经慢的可以了,毕竟Python是一种解释型语言。

  • 而C也好,Go也好,作为编译型语言,效率上的确很高。

  • Go其实比C慢一点,而之前做的一个测试,有时候这玩意居然会比Python还慢(http://weibo.com/1323904711/yzyHZkWj4)?不知道是哪出问题了

最后再吐槽一下,Go语言为什么没有问号运算符啊?难道是因为追求可读性给去掉了??!!

=================================== 

http://blog.jobbole.com/14386/

最近在忙一个Server+Client的项目,Client是Android手机,大概也就是几十的规模。Server是纯Go语言实现,没有apache或者ngix这种web server,也没有数据库,自己写了个文件管理module,处理这种小规模的服务没问题。算下来接触Go语言也有四个多月了,断断续续写了一些东西,这里记录一下心得吧。

先大概说下为什么用Go语言。我是一个对语言有洁癖的人,曾经是一个c+Python的坚定呐喊者,最常说的一句话就是,只要熟练这两种,什么情况都能应付,根本不用Java和C++(纯指我所在的领域)。核心代码用c,速度快,需要记的语言细节少;外围用Python glue,灵活,简洁,任何模块都容易上手,绝配。Java的繁琐,C++的无数无用的特性,都让我只在不得不用的时候才去用。Objective-C是另一个我欣赏的语言,问题是不跨平台,过于封闭。

可惜的是,在这个节奏极快的时代,不是所有情况下都适合上c。之前有一个项目也是类似的架构和规模,为了节省时间,当初几乎没有服务器平台编程经验的我,在服务器端选择用Django、Apache、MySQL做,成熟,社区活跃,又是python作为主要开发语言,这些都是这个选择的原因。说实话,几个月过去后,回首看,这不是一个愉快的经历。Django是一个好架构,大而全,而大而全有时也就意味着臃肿,五花八门的配置,过紧的模块耦合对引入第三方工具限制颇多,自带的ORM又不好用。之前从来没有搞过服务器配置的我,对Apache的配置和效率所带来的琐碎的东西也头疼。总的来说这个部分花了我很多时间,有新手学习服务器编程的必经过程,也有折腾Django和Apache没必要的时间浪费,很大部分上抵消了Python带来的快速开发的灵活性。而一旦服务器上线,动态语言带来的一些bug又会让人头疼。对于普通高校实验室这种没有完善的服务器调试的条件,基本就是改了就上线用,有些隐蔽bug到某些条件分支才会触发,一旦在运行中途出问题,改起来也麻烦。

从那时起,我就特别想,要是有一种语言能把c和Python的优点结合起来,也就是说

▲速度快,高性能

▲简洁明了,需要记的语言细节少,开发迅速(c)

▲灵活,开发快速,类Python的list,map等常用数据结构支持(Python)

▲完善的模块支持,模块也容易上手(Python)

▲对程序员友好的并行架构(Erlang)

▲安全,绝大部分问题能消灭在compile time中(C minus pointer)

那基本就是系统级和网络级编程最对我胃口的语言了。然后我就找到了Go

Go语言是一个新语言,截至目前为止,第一版正式版还没有发布。Go语言的设计者是Robert Griesemer, Rob Pike和Ken Thompson,当年设计C和Unix,后来的Plan9团队中的人 。Go语言的设计理念很明确,就是将动态类型语言的编程容易度和静态类型语言的安全效率结合起来。如果你想更深入了解Go语言的发展历史以及完整的目标,请参考Go语言 FAQ

Go语言

当然,Go语言吸引我的地方,不是因为其是Google出品,也不是因为其设计者皆为大牛,而是因为,Go语言真的做到了它所宣称的目标。Go语言就如同C和Python中间的完美结合,如果你是Python爱好者,又追求代码的速度和并行化,那么简单说,Go语言就是为你设计的。Go语言有很浓厚的C的遗风,尽量屏蔽C++和Java的影响,比如没有独立的OO体系(并不是说不能OO),一切以struct为中心,没有exceptions(Oh yes!),仍然有指针,等等。但是,Go语言又吸取了很多新语言的精华,并带有自己独特的设计。比如

1. 保留但大幅度简化指针

Go语言保留着C中值和指针的区别,但是对于指针繁琐用法进行了大量的简化,引入引用的概念。所以在Go语言中,你几乎不用担心会因为直接操作内寸而引起各式各样的错误。

2. 多参数返回

还记得在C里面为了回馈多个参数,不得不开辟几段指针传到目标函数中让其操作么?在Go里面这是完全不必要的。而且多参数的支持让Go无需使用繁琐的exceptions体系,一个函数可以返回期待的返回值加上error,调用函数后立刻处理错误信息,清晰明了。

3. Array,slice,map等内置基本数据结构

如果你习惯了Python中简洁的list和dict操作,在Go语言中,你不会感到孤单。一切都是那么熟悉,而且更加高效。如果你是C++程序员,你会发现你又找到了STL的vector 和 map这对朋友。

4. Interface

Go语言最让人赞叹不易的特性,就是interface的设计。任何数据结构,只要实现了interface所定义的函数,自动就implement了这个interface,没有像Java那样冗长的class申明,提供了灵活太多的设计度和OO抽象度,让你的代码也非常干净。千万不要以为你习惯了Java那种一条一条加implements的方式,感觉还行,等接口的设计越来越复杂的时候,无数Bug正在后面等着你。

同时,正因为如此,Go语言的interface可以用来表示任何generic的东西,比如一个空的interface,可以是string可以是int,可以是任何数据类型,因为这些数据类型都不需要实现任何函数,自然就满足空interface的定义了。加上Go语言的type assertion,可以提供一般动态语言才有的duck typing特性, 而仍然能在compile中捕捉明显的错误。

5. OO

Go语言本质上不是面向对象语言,它还是过程化的。但是,在Go语言中, 你可以很轻易的做大部分你在别的OO语言中能做的事,用更简单清晰的逻辑。是的,在这里,不需要class,仍然可以继承,仍然可以多态,但是速度却快得多。因为本质上,OO在Go语言中,就是普通的struct操作

6. Goroutine

这个几乎算是Go语言的招牌特性之一了,我也不想多提。如果你完全不了解Goroutine,那么你只需要知道,这玩意是超级轻量级的类似线程的东西,但通过它,你不需要复杂的线程操作锁操作,不需要care调度,就能玩转基本的并行程序。在Go语言里,触发一个routine和erlang spawn一样简单。基本上要掌握Go语言,以Goroutine和channel为核心的内存模型是必须要懂的。不过请放心,真的非常简单。

7. 更多现代的特性

和C比较,Go语言完全就是一门现代化语言,原生支持的Unicode, garbage collection, Closures(是的,和functional programming language类似), function是first class object,等等等等。

看到这里,你可能会发现,我用了很多轻易,简单,快速之类的形容词来形容Go语言的特点。我想说的是,一点都不夸张,连Go语言的入门学习到提高,都比别的语言门槛低太多太多。在大部分人都有C的背景的时代,对于Go语言,从入门到能够上手做项目,最多不过半个月。Go语言给人的感觉就是太直接了,什么都直接,读源代码直接,写自己的代码也直接。

有朋友要抗议了,你把Go语言吹的这么好,难道它就没有缺点?有,当然有,不过和它的优点比,我觉得很多缺点都是因为整个语言太新,不成熟,随着时间的推移都能得到解决,相比之下都能忍了。如果你希望进一步了解Go语言的优缺点,可以参考以下yufeng写的这篇文章,系统编程语言明日之星—Go(http://blog.yufeng.info/Go.pdf。

还有朋友要说,Go语言这么好,为什么没人用?我想说,眼界放开点,这个世界精彩的东西比你想象的多。Go语言被Google用于Youtube的数据库,被越来越多的国外公司(大部分创业公司)用于后端开发,甚至在天朝,也有完全用Go语言做服务开发的云应用公司了。可以说,随着Go 1即将到来的正式推出,Go语言的使用范围,应该会越来越广。

总结

如果你是Python和动态语言狂热爱好者,Go不一定能给你带来很大的惊喜,这纯粹取决于你得项目性质,考虑到Python目前在很多地方都用C做核心运算,速度在大部分情况下都不是大问题。scalability是一个问题,但并不是人人都会遇到的。

如果你是C爱好者,强烈建议你学习和使用Go。Go可以调用C/C++程序,又提供了太多的便利,速度上稍有牺牲,但并不大。在绝大部分场景下Go能给你带来媲美C的性能,而对于某些确实性能过于关键的场合,也可以通过cgo让Go和C搭配。

如果你是Java爱好者,除非你是做Android这种不得不用Java的平台,否则也建议你尝试学习Go,这个开发上感觉的差异如同比较开载着1吨石头的拖拉机和开保时捷911那么明显,而Java能给你的,Go能给得更好。

如果你是C++爱好者,!@#$%^&*,恭喜你,至少你的智商应该是没问题的。人生苦短,赶紧脱离C++这个苦海吧。你用来学89个C++高级特性的时间,估计已经用Go写了64个开源项目了。

如果你是像我一样的C和Python的爱好者,对动态语言又没有特殊的热爱……我还需要说什么呢?

让我们荡起双桨,去遨游Go语言的海洋吧。

 ###############################################

http://book.douban.com/review/5928325/

 过年在老家的时候不能上网,通过文档抽空学了下go语言。后来又买了这本书。总体上这本书是当前的 go语言书中比较不错的一本。但缺点在于对go的并行机制以及内存回收机制这两个非常重要的话题介绍的不够深入。go的并行机制是其最大的亮点go的存在基础,而内存回收机制更是是否能革c和java命的根基。 既然说到了这个书,当然也需要说说对go语言的看法。 
   
  go语言除了go这个并行计算的亮点不说,其他的优点有: 
   
  1. 非侵入式接口 习惯了java的侵入式接口,开始的时候这个我还不太接受。感觉这样设计,类的开发者不知道自己实现了那些接口,修改的时候如何确保不会影响使用方?没有了侵入接口的静态检查,会不会导致代码变更失控?但后来一想,java中流行的许多非侵入性框架不也通过代理模式实现了非侵入式接口。这个类似于流行的约定大于配置的概念。一些基本的,通用的接口比如Collection等,工具包中会前置约定或者由社区形成约定,大家遵循约定即可。 
   
  2. go的面向工程的设计 由语言的进化可以看出,早期的语言一般只提供了语法规则以及编译器,甚至没有标准库。后来的语言都会提供一个内置的标准库。再后来的语言会提供包管理以及依赖管理机制。而go提供的不仅如此,提供包括工程管理,文档管理,内置的单元测试机制,源码格式化等一整套工具。 
   
  http://jolestar.com/recently-reading-notes/

 

=================================================

http://www.cnblogs.com/AllenDang/archive/2012/03/03/2378534.html

从2000年至今,也写了11年代码了,期间用过VB、Delphi、C#、C++、Ruby、Python,一直在寻找一门符合自己心意和理念的语言。我很在意写代码时的手感和执行的效率,所以在Go出现之前一直没有找到。在熟悉Go之后,我虽没有停下脚步,也去体验了D语言,但几乎立即就放弃了,它的设计还是太复杂。

就说说Go吧。它的好其实也就两个字——简洁!

 

看很多朋友的留言都觉得这些“少个括号、少个分号”之类的东西没什么意义,真的吗?问题是,既然可以没有,为什么非得有?既然能够少打一个字符,为什么多打了还挺开心?还觉得天经地义?这里简单一点,那里简单一点,总的来说是不是就简单了很多?这里的设计简洁一点,那里简洁一点,是否整体就是紧凑高效?

很多东西,要整体去体会,才能感觉到真正的强大。没有前面这些语法上的各种“看起来没什么用”的支持,怎么能做到后面提到的那些设计上的简洁?

 

我坚信,少就是多,简单就是强大,不能减一分的设计才是真正的好设计!

 

简洁的变量声明和赋值

拿最简单的声明变量和赋值来看,下面这一句完成了声明类型到赋值,最后还有那个常见的分号作为语句的结束。

var i int = 10;

这个一点都不简洁对吧?为什么非要有“var”?为什么不能自己推导变量类型?为什么结尾非要加上分号?这三个问题,我相信Go语言的设计者也问过,并且都针对性的给了改进。重新来过。

i := 10

怎么样?“:=”是声明并推导类型的语法糖,结尾的分号也省了,因为这里我换行了,编译器明白的。

还可以一次性声明并赋值多个变量。

i, j, k := 1, 2, 3

不同的类型也可以。

i, j, k := 1, 1.0, “hello”

如果要声明一堆变量,但暂时不赋值呢?可以这样。

var (

    i, j int
    s string
    u, v, s = 2.0, 3.0, "bar"

)

Go的设计者甚至觉得多打几个“var”都不应该!

 

简洁的if

有点意思了对吧?我学习一门新语言的时候,第一眼看变量类型和声明,第二眼就会去看逻辑控制的语法。现在来看看都有些什么?

if i > 10 {

    println(“Greater then 10”)

}

稀松平常啊,难道一个简单的if还能更简单?恩,的确是的。首先if后面的条件判断没有人逼你再加上括号了,仅仅是少了两次按键嘛,还有呢?还有!下面这个应该是很常见的if使用场景。

result := SomeMethod()

if result > 0 {

}

很多时候result这个变量其实仅仅用于条件判断,完全可以在if之后就扔掉,所以Go有了这么个写法。

if result := SomeMethod(); result > 0 {

}

这个表达式太常用了,真是谁写谁知道,每次我写着一行都会心里一爽。来看看纠结一点的if段。

if a {

} else if b {

} else if c {

} else {

}

这种写法是可以的,但不是Go推荐的,理由是可以更简洁。比如强悍的switch。

 

强悍的switch

这是很大家熟知的switch用法,注意,没有break哦!Go里面case之间不会“下穿”。

switch tag {
    default: 
        s3()
    case 0, 1, 2, 3:
        s1()
    case 4, 5, 6, 7: 
        s2()
}
神奇一点的switch,嘿嘿,与if异曲同工之妙。
switch x := f(); {  // missing switch expression means "true"
    case x < 0: return -x
    default: return x
}

还有这个,有了这个更加明确的写法,你真的还会if…else if…else if…else…吗?

switch {
    case x < y: f1()
    case x < z: f2()
    case x == 4: f3()
}

条件判断舒服了,循环呢?

 

孤单的for

其实我一直不太明白,为什么一门语言里面要提供多个循环语法呢?for、while、do…while…都是不可替代的?用哪一个呢?似乎都是看个人爱好吧?可能大家随便就可以举个例子出来证明这三个东西存在的必要和细微的差别,但对于我来说,做同一件事情如果有多种方法其实就是设计上的冗余,会对使用者造成或多或少的困扰。来看看Go的循环吧。

for i := 0; i < 10; i++ {

}

for a < b {

}

for {

}

看吧,一个for就搞定所有情况了。来看一个常用的遍历集合,一把来说会写成这样。

count := len(someArray)

for i := 0; i < count; i++ {

    println(someArray[i])

}

简化这个,Go给出了一个关键字“range”,先看用法。

for i, value := range someArray {

    // i 是整型,代表下标

    // value就是数组内值的类型

}

range不单单可以用于数组,实际上它可以用于任何集合,比如map。

m := map[string]int{"mon":0, "tue":1, "wed":2, "thu":3, "fri":4, "sat":5, "sun":6}
for i, s := range a {
    // type of i is int
    // type of s is string
}

这里只是提到了几点最基本的语法场景,Go里面还有很多!

 

函数可以返回多个值

其实能够在一行多重赋值的语言挺多的,但一个函数能返回多个值的就很少了,比如在C#里面如果要返回两个int,通常会这么干。

public class TwoInts

{

    public int A;

    public int B;

}

public class Foo

{

    public TwoInts ReturnTwoInt();

}

然后就可以 TwoInts ti = foo.CalcTwoInt() 觉得悲催吗?也许你都麻木了对吗?很多语言都是这么设计的。函数只能返回一个值最大的问题是会导致出现很多没必要的数据结构。上面就体现了这个冗余,当然,你说可以用out关键字让函数返回,但这个语法用起来就不是那么安全了。而这个问题在Go里面解决起来太容易了,因为Go的函数可以返回多个值!

func returnTwoInt() (int, int) {

}

a, b := returnTwoInt()

我对Go的好感就是从这里萌芽的,这让我的库里面从此少了很多数据结构!这无形中就能降低设计的复杂度。

 

函数内部声明的对象指针可以安全的返回

func ReturnPointer() *Object1 {

    obj := new Object1()

    obj.A = “hello”

    return obj

}

Go的垃圾回收器会处理好这种情况的,放心啦!

 

异常处理?defer是啥?能吃吗?

为什么异常处理那么复杂?多少人可以安全的实现下面这个逻辑?以下是伪代码。

File f = File.Read(“c:\\text.txt”)

f.Write(xxx)

f.Close()

我相信,有经验的码农们脑子里面瞬间出现了各种版本的try…catch…finally…,还有各种各样的书写规范,比如“catch”里面的逻辑不能在抛异常之类的东西。其实想想,我们的要求很简单,打开一个文件,然后保证它在最后被关闭。仅此而已,为什么做这么简单的一件事情非要那么复杂?看看人家Go是怎么做的!

func SaveSomething() {

    if f, err := os.Open(“c:\\text.txt”); err == nil {

        //各种读写

        defer f.Close()

    }

}

凡是加了defer的函数,都会在当前函数(这里就是SaveSomething)执行完毕之后执行。就算“//各种读写”时发生异常f.Close也会坚定的在SaveSomething退出时被执行。有了这个,释放点资源,关闭个把句柄这种小事再也无足挂齿!

 

接口再也不用“实现”了

从我接触OO思想一来,凡是有接口的语言,都以不同的方式要求类“实现”接口,这样的方式我一直都认为是天经地义的,直到我遇见了Go。

type Speaker interface {

    Say()

}

上面定义了一个接口,只有一个方法,Say,不需要参数,也没有返回值。Go里面,任何拥有某个接口所定义所有方法的东西,都默认实现了该接口。这是一句拥有太多内涵的话,足矣对设计思路产生重大的影响。比如下面这个方法,它接受一个类型为Speaker的参数。

func SaySomething(s Speaker) {

    s.Say()

}

那么所有拥有Say()方法的东西都可以往里扔。

在Go的世界里,所有的东西都默认实现了interface{}这个接口。有了这个概念,即使没有泛型也能有效的降低设计复杂度。

 

多线程还能更简单点吗?

要写多线程,你要懂Thread,懂各种锁,懂各种信号量。在各类系统里面,“异步”逻辑通常代表“困难”。这是Go最强劲的部分,你见过比下面这个还简单的异步代码吗(以下代码摘自Go的官方范例)?

func IsReady(what string, minutes int64) { 
    time.Sleep(minutes * 60*1e9); 
    fmt.Println(what, "is ready") 

go IsReady("tea", 6); 
go IsReady("coffee", 2); 
fmt.Println("I'm waiting....");

执行的结果是,打印: 
I'm waiting.... (right away) 
coffee is ready (2 min later) 
tea is ready (6 min later)

Go语言内置了“go”这个语法,任何go的方法,都将会被异步执行。那异步方法之前传递消息呢?用channel呗。意如其名,就是一个管道,一个往里写,另外一个等着读。

ch := make(chan int) //创建一个只能传递整型的管道

func pump(ch chan int) { 
    for i := 0; ; i++ { ch <- i } //往管道里写值 
}

func suck(ch chan int) { 
    for { fmt.Println(<-ch) } //这里会等着直到有从管道里面出来 
}

go pump(ch) //异步执行pump

go suck(ch) //异步执行suck

嘿嘿,然后你就看到控制台上输出了一堆数字。

 

这次就写到这儿吧,对不住Go里面其他的好东西了,哥饿了,就不一一出场亮相了,抱歉抱歉!鞠躬!下台!

 

----------------------------------------------------------------------

http://www.programmer.com.cn/13730/

我在新浪微博、《Go语言编程》一书中下了一个论断:Go语言将超过C、Java,成为未来十年最流行的语言。

 

我曾在2007年第2届ECUG大会上提到了我对未来软件产业趋势的判断:存储与计算将向服务端转移;PC单机转换为强悍的服务器+多元化的终端。这个趋势判断对我的职业生涯其实影响非常重大。

从业务角度讲,正因为这个判断,促使我选择成立金山实验室研究云存储技术;也促使我关注Erlang这门面向分布式编程的领域性语言;促使我举办了一届届的ECUG大会;促使我加入盛大创新院,发起祥云计划(盛大云前
身),做盛大网盘;促使我们成立了七牛,有了七牛云存储。

从技术角度来讲,它促使我放弃了近10年的桌面开发经验,转向服务端开发。正如我在《我为什么选择了Erlang》中建议的那样:

要么不写程序,要么就写服务器端程序;
当然,你也可以去撰写移动终端设备上的代码,在PC平台上做开发的空间很小。
在金山实验室时,我选择了Java语言,并积极关注Erlang语言,对其进行小范围试用,以及大规模使用的可行性评估。我的结论是:

Erlang风格的并行思想真的很棒;
Erlang语言优势与劣势同样明显,这门语言不适合规模化推广使用。
Erlang的困难之处在于它是FP语言。我们缺乏深入人心的FP编程理论。我们并不了解FP“数据结构”学。这是Erlang语言无法逾越的门槛,决定了它只能是小众化语言。

进入盛大创新院后,我选择了C++,原因是我个人更喜欢C++,而对Java的设计哲学非常不感兴趣。出于对Erlang风格的并行思想的喜爱,我创建了一个名为CERL的项目,取义Erlang Model for C++,表明我希望能够把Erlang那一套并行编程模型搬到 C++中来,降低分布式编程的负担。

接触Go语言后,我发现CERL 2.0的并发编程模型,其实就是一个雏形版的Go语言并发模型,两者从理念上来说完全一致。

七牛公司成立于2011年5月底,我们选择了Go语言作为七牛云存储服务端的主体语言。当时Go语言甚至语法都还没完全稳定下来。为什么我可以如此坚定地相信,选择Go语言不会有错,并且相信Go语言会成为未来十年最流行的语言?除了Go语言的并发编程模型深得我心外,Go语言的各种语法特性显得那么深思熟虑、卓绝不凡,其对软件系统架构的领悟,处处带给我惊喜。

Go语言给我的第一个意外惊喜,是接口。当然,我意外的不是Go的非侵入式接口。在接口(或契约)的表达上,我一直认为Java和C#这些主流的静态类型语言都走错了方向。C++的模板尽管机制复杂,但走在了正确的方向上。但Go语言的接口远不是非侵入式接口那么简单,它是Go语言类型系统的纲。除了支持反射等高级特性外,Go语言还支持接口查询。

Go语言给我的第二个意外惊喜,是极度简化但完备的“面向对象编程”方法。Go语言废弃大量的OOP特性,如继承、构造/析构函数、虚函数、函数重载、默认参数等;简化的符号访问权限控制、将隐藏的this指针改为显式定义的receiver对象。让我看到了OOP编程核心价值原来如此简单—只是多数人都无法看透。

Go语言带给我的第三个惊喜,是它的函数多反回值和错误处理规范。函数多返回值比较容易想到,只有这样函数的输入输出才能清晰呈现,语义表达上才会足够清晰。不过让我没想到的是,Go引入了内置的error类型以及defer关键字来编写异常安全代码,让人拍案叫绝。

Go语言带给我的第四个惊喜,是它功能的内聚。一个最典型的案例是Go语言的组合功能。对于多数语言来说,组合只是形成复合类型的基本手段。但Go语言引入了匿名组合的概念,让其他语言原本需要引入继承甚至虚拟继承这样晦涩概念来完成的事情,统一到了组合这样的一个基础上。

Go语言带给我的第五个惊喜,是消除了堆与栈的边界。在Go语言之前,程序员是清楚地知道哪些变量在栈上,哪些变量在堆上。堆与栈是基于现代计算机系统的基础工作模型上形成的概念,Go语言屏蔽了变量定义在堆还是栈上这样的物理结构,相当于封装了一个新的计算机工作模型。这一点看似与Go语言显式表达的设计哲学不太一致,但我认为这是一项了不起的工作,且与Go语言的显式表达并不矛盾。Go语言强调的是对开发者的程序逻辑(语义)的显式表达,而非对计算机硬件结构的显示表达。对计算机硬件结构的高度抽象,将更有助于Go语言适应计算机硬件发展的变化。

Go语言带给我的第六个惊喜,是Go语言对C语言的支持。可以这么说,Go语言是除了Objective-C、C++这两门以兼容C为基础目标的语言外的所有语言中,对C语言支持最友善的一个。什么语言可以直接嵌入C代码?没有,除了Go。什么语言可以无缝调用C函数?没有,除了Go。对C语言的完美支持,是Go快速崛起的关键支撑。还有比C语言更让人觊觎的社区财富吗?那是一个取之不尽的金矿。

在《Go语言编程》一书的前言中,我谈到Go语言的基础哲学来源于C语言,而不是像Java、C#那样,学的是C++。C语言的成功在于它足够简单,所以Go语言也要非常简单。Go语言的定位就是成为互联网时代的C语言。本质上来说,Go语言的特性都围绕着以下设计哲学:

大道至简,显式表达。任何封装都是有漏洞的。最佳的表达方式就是最直白的表达方式,不试图去做任何包装。
最少特性原则。如果一个功能不对解决任何问题有显著价值,那么就不提供。
尽管是40年来出现的语言非常之多,但我认为,谈得上突破了C语言思想,将编程理念提高到一个新高度的,仅有Go语言而已。

Go语言是一门变革性的语言,要革的是C的命(顺便革Java的命)。Go语言很简单,但具备极强的表现力。从目前的状态来说,Go语言主要关注服务器领域的开发,但这不是Go语言的完整使命。

我们说Go语言适合服务端开发,仅是因为它的标准库支持方面,目前是向服务端开发倾斜:

网络库(包括 Socket、HTTP、RPC 等);
编码库(包括 JSON、XML、GOB等);
加密库(各种加密算法、摘要算法);
Web(包括 Template、HTML支持)。
而作为桌面开发的常规组件GDI和UI系统与事件处理,基本没有涉及。

总之,我认为,Go语言将引领未来10年IT产业的发展。在最初5年内,Go语言会在服务器端编程上大放异彩,而桌面端的开发则仍然处于探索和完善期,预计在后5年才趋于成熟,成为各种手持设备上的主流开发语言之一。

作者许式伟,七牛云存储CEO,曾任盛大创新院资深研究员、金山软件技术总监、WPS Office 2005首席架构师。开源爱好者,发布过包括WINX、TPL等十余个C++开源项目,拥有超过15年的C/C++开发经验。在接触Go语言后即可被其大道至简、少即是多的设计哲学所倾倒。七牛云存储是国内第一个吃螃蟹的团队,核心服务完全采用Go语言实现。

本文选自《程序员》杂志2012年10期,未经允许不得转载。如需转载请联系 market@csdn.net

《程序员》2012年杂志订阅送好礼活动火热进行中

 

 

http://coolshell.cn/articles/8460.html

周末天气不好,只能宅在家里,于是就顺便看了一下Go语言,觉得比较有意思,所以写篇文章介绍一下。我想写一篇你可以在乘坐地铁或公交车上下班时就可以初步了解一门语言的文章。所以,下面的文章主要是以代码和注释为主。只需要你对C语言,Unix,Python有一点基础,我相信你会在30分钟左右读完并对Go语言有一些初步了解的。

Hello World

文件名 hello.go
1
2
3
4
5
6
7
package main //声明本文件的package名
 
import "fmt" //import语言的fmt库——用于输出
 
func main() {
    fmt.Println("hello world")
}

 

运行

你可以有两种运行方式,

解释执行(实际是编译成a.out再执行)
1
2
$go run hello.go
hello world
编译执行
1
2
3
4
5
6
7
$go build hello.go
 
$ls
hello hello.go
 
$./hello
hello world

自己的package

你可以使用GOPATH环境变量,或是使用相对路径来import你自己的package。

Go的规约是这样的:

1)在import中,你可以使用相对路径,如 ./或 ../ 来引用你的package

2)如果没有使用相对路径,那么,go会去找$GOPATH/src/目录。

使用相对路径
1
import "./haoel"  //import当前目录里haoel子目录里的所有的go文件
使用GOPATH路径
1
import "haoel"  //import 环境变量 $GOPATH/src/haoel子目录里的所有的go文件

fmt输出格式

fmt包和libc里的那堆使用printf, scanf,fprintf,fscanf 很相似。下面的东西对于C程序员不会陌生。

注意:Println不支持,Printf才支持%式的输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main
 
import "fmt"
import "math"
 
func main() {
    fmt.Println("hello world")
 
    fmt.Printf("%t\n", 1==2)
    fmt.Printf("二进制:%b\n", 255)
    fmt.Printf("八进制:%o\n", 255)
    fmt.Printf("十六进制:%X\n", 255)
    fmt.Printf("十进制:%d\n", 255)
    fmt.Printf("浮点数:%f\n", math.Pi)
    fmt.Printf("字符串:%s\n", "hello world")
}

当然,也可以使用如\n\t\r这样的和C语言一样的控制字符

变量和常量

变量的声明很像 javascript,使用 var关键字。注意:go是静态类型的语言,下面是代码:

1
2
3
4
5
6
7
8
//声明初始化一个变量
var  x int = 100
var str string = "hello world"</pre>
//声明初始化多个变量
var  i, j, k int = 1, 2, 3
 
//不用指明类型,通过初始化值来推导
var b = true //bool型

还有一种定义变量的方式(这让我想到了Pascal语言,但完全不一样)

1
x := 100 //等价于 var x int = 100;

常量很简单,使用const关键字:

1
2
const s string = "hello world"
const pi float32 = 3.1415926

数组

直接看代码(注意其中的for语句,和C很相似吧,就是没有括号了)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func main() {
    var a [5]int
    fmt.Println("array a:", a)
 
    a[1] = 10
    a[3] = 30
    fmt.Println("assign:", a)
 
    fmt.Println("len:", len(a))
 
    b := [5]int{1, 2, 3, 4, 5}
    fmt.Println("init:", b)
 
    var c [2][3]int
    for i := 0; i < 2; i++ {
        for j := 0; j < 3; j++ {
            c[i][j] = i + j
        }
    }
    fmt.Println("2d: ", c)
}

运行结果:

1
2
3
4
5
array a: [0 0 0 0 0]
assign: [0 10 0 30 0]
len: 5
init: [1 2 3 4 5]
2d:  [[0 1 2] [1 2 3]]

数组的切片操作

这个很Python了。

1
2
3
4
5
6
7
8
9
10
a := [5]int{1, 2, 3, 4, 5}
 
b := a[2:4] // a[2] 和 a[3],但不包括a[4]
fmt.Println(b)
 
b = a[:4] // 从 a[0]到a[4],但不包括a[4]
fmt.Println(b)
 
b = a[2:] // 从 a[2]到a[4],且包括a[2]
fmt.Println(b)

分支循环语句

if语句

注意:if 语句没有圆括号,而必需要有花括号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//if 语句
if x % 2 == 0 {
    //...
}
//if - else
if x % 2 == 0 {
    //偶数...
} else {
    //奇数...
}
 
//多分支
if num < 0 {
    //负数
} else if num == 0 {
    //零
} else {
    //正数
}

switch 语句

注意:switch语句没有break,还可以使用逗号case多个值

1
2
3
4
5
6
7
8
9
10
11
12
switch i {
    case 1:
        fmt.Println("one")
    case 2:
        fmt.Println("two")
    case 3:
        fmt.Println("three")
    case 4,5,6:
        fmt.Println("four, five, six")
    default:
        fmt.Println("invalid value!")
}

for 语句

前面你已见过了,下面再来看看for的三种形式:(注意:Go语言中没有while)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//经典的for语句 init; condition; post
for i := 0; i<10; i++{
     fmt.Println(i)
}
 
//精简的for语句 condition
i := 1
for i<10 {
    fmt.Println(i)
    i++
}
 
//死循环的for语句 相当于for(;;)
i :=1
for {
    if i>10 {
        break
    }
    i++
}

关于分号

从上面的代码我们可以看到代码里没有分号。其实,和C一样,Go的正式的语法使用分号来终止语句。和C不同的是,这些分号由词法分析器在扫描源代码过程中使用简单的规则自动插入分号,因此输入源代码多数时候就不需要分号了

规则是这样的:如果在一个新行前方的最后一个标记是一个标识符(包括像intfloat64这样的单词)、一个基本的如数值这样的文字、或以下标记中的一个时,会自动插入分号:

break continue fallthrough return ++ -- ) }

通常Go程序仅在for循环语句中使用分号,以此来分开初始化器、条件和增量单元。如果你在一行中写多个语句,也需要用分号分开。

注意无论任何时候,你都不应该将一个控制结构((ifforswitchselect)的左大括号放在下一行。如果这样做,将会在大括号的前方插入一个分号,这可能导致出现不想要的结果

map

map在别的语言里可能叫哈希表或叫dict,下面是和map的相关操作的代码,代码很容易懂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
func main(){
    m := make(map[string]int) //使用make创建一个空的map
 
    m["one"] = 1
    m["two"] = 2
    m["three"] = 3
 
    fmt.Println(m) //输出 map[three:3 two:2 one:1] (顺序在运行时可能不一样)
    fmt.Println(len(m)) //输出 3
 
    v := m["two"] //从map里取值
    fmt.Println(v) // 输出 2
 
    delete(m, "two")
    fmt.Println(m) //输出 map[three:3 one:1]
 
    m1 := map[string]int{"one": 1, "two": 2, "three": 3}
    fmt.Println(m1) //输出 map[two:2 three:3 one:1] (顺序在运行时可能不一样)
 
    for key, val := range m1{
        fmt.Printf("%s => %d \n", key, val)
        /*输出:(顺序在运行时可能不一样)
            three => 3
            one => 1
            two => 2*/
    }
}

指针

Go语言一样有指针,看代码

1
2
3
4
5
6
7
8
9
10
11
12
var i int = 1
var pInt *int = &i
//输出:i=1     pInt=0xf8400371b0       *pInt=1
fmt.Printf("i=%d\tpInt=%p\t*pInt=%d\n", i, pInt, *pInt)
 
*pInt = 2
//输出:i=2     pInt=0xf8400371b0       *pInt=2
fmt.Printf("i=%d\tpInt=%p\t*pInt=%d\n", i, pInt, *pInt)
 
i = 3
//输出:i=3     pInt=0xf8400371b0       *pInt=3
fmt.Printf("i=%d\tpInt=%p\t*pInt=%d\n", i, pInt, *pInt)

Go具有两个分配内存的机制,分别是内建的函数new和make。他们所做的事不同,所应用到的类型也不同,这可能引起混淆,但规则却很简单。

内存分配

new 是一个分配内存的内建函数,但不同于其他语言中同名的new所作的工作,它只是将内存清零,而不是初始化内存。new(T)为一个类型为T的新项目分配了值为零的存储空间并返回其地址,也就是一个类型为*T的值。用Go的术语来说,就是它返回了一个指向新分配的类型为T的零值的指针

make(T, args)函数的目的与new(T)不同。它仅用于创建切片、map和chan(消息管道),并返回类型T(不是*T)的一个被初始化了的(不是)实例。这种差别的出现是由于这三种类型实质上是对在使用前必须进行初始化的数据结构的引用。例如,切片是一个具有三项内容的描述符,包括指向数据(在一个数组内部)的指针、长度以及容量,在这三项内容被初始化之前,切片值为nil。对于切片、映射和信道,make初始化了其内部的数据结构并准备了将要使用的值。如:

下面的代码分配了一个整型数组,长度为10,容量为100,并返回前10个数组的切片

1
make([]int, 10, 100)

以下示例说明了newmake的不同。

1
2
3
4
5
6
7
8
9
10
11
12
13
var p *[]int = new([]int)   // 为切片结构分配内存;*p == nil;很少使用
var v  []int = make([]int, 10) // 切片v现在是对一个新的有10个整数的数组的引用
 
// 不必要地使问题复杂化:
var p *[]int = new([]int)
fmt.Println(p) //输出:&[]
*p = make([]int, 10, 10)
fmt.Println(p) //输出:&[0 0 0 0 0 0 0 0 0 0]
fmt.Println((*p)[2]) //输出: 0
 
// 习惯用法:
v := make([]int, 10)
fmt.Println(v) //输出:[0 0 0 0 0 0 0 0 0 0]

函数

老实说,我对Go语言这种反过来声明变量类型和函数返回值的做法有点不满(保持和C一样的不可以吗? 呵呵)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main
import "fmt"
 
func max(a int, b int) int { //注意参数和返回值是怎么声明的
 
    if a > b {
        return a
    }
    return b
}
 
func main(){
    fmt.Println(max(4, 5))
}

函数返回多个值

Go中很多Package 都会返回两个值,一个是正常值,一个是错误,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main
import "fmt"
 
func main(){
    v, e := multi_ret("one")
    fmt.Println(v,e) //输出 1 true
 
    v, e = multi_ret("four")
    fmt.Println(v,e) //输出 0 false
 
    //通常的用法(注意分号后有e)
    if v, e = multi_ret("four"); e {
        // 正常返回
    }else{
        // 出错返回
    }
}
 
func multi_ret(key string) (int, bool){
    m := map[string]int{"one": 1, "two": 2, "three": 3}
 
    var err bool
    var val int
 
    val, err = m[key]
 
    return val, err
}

函数不定参数

例子很清楚了,我就不多说了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func sum(nums ...int) {
    fmt.Print(nums, " "//输出如 [1, 2, 3] 之类的数组
    total := 0
    for _, num := range nums { //要的是值而不是下标
        total += num
    }
    fmt.Println(total)
}
func main() {
    sum(1, 2)
    sum(1, 2, 3)
 
    //传数组
    nums := []int{1, 2, 3, 4}
    sum(nums...)
}

函数闭包

nextNum这个函数返回了一个匿名函数,这个匿名函数记住了nextNum中i+j的值,并改变了i,j的值,于是形成了一个闭包的用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func nextNum() func() int {
    i,j := 1,1
    return func() int {
        var tmp = i+j
        i, j = j, tmp
        return tmp
    }
}
//main函数中是对nextNum的调用,其主要是打出下一个斐波拉契数
func main(){
    nextNumFunc := nextNum()
    for i:=0; i<10; i++ {
        fmt.Println(nextNumFunc())
    }
}

函数的递归

和c基本是一样的

1
2
3
4
5
6
7
8
9
10
func fact(n int) int {
    if n == 0 {
        return 1
    }
    return n * fact(n-1)
}
 
func main() {
    fmt.Println(fact(7))
}

结构体

Go的结构体和C的基本上一样,不过在初始化时有些不一样,Go支持带名字的初始化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
type Person struct {
    name string
    age  int
    email string
}
 
func main() {
    //初始化
    person := Person{"Tom", 30, "tom@gmail.com"}
    person = Person{name:"Tom", age: 30, email:"tom@gmail.com"}
 
    fmt.Println(person) //输出 {Tom 30 tom@gmail.com}
 
    pPerson := &person
 
    fmt.Println(pPerson) //输出 &{Tom 30 tom@gmail.com}
 
    pPerson.age = 40
    person.name = "Jerry"
    fmt.Println(person) //输出 {Jerry 40 tom@gmail.com}
}

结构体方法

不多说了,看代码吧。

注意:Go语言中没有public, protected, private的关键字,所以,如果你想让一个方法可以被别的包访问的话,你需要把这个方法的第一个字母大写。这是一种约定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
type rect struct {
    width, height int
}
 
func (r *rect) area() int { //求面积
    return r.width * r.height
}
 
func (r *rect) perimeter() int{ //求周长
    return 2*(r.width + r.height)
}
 
func main() {
    r := rect{width: 10, height: 15}
 
    fmt.Println("面积: ", r.area())
    fmt.Println("周长: ", r.perimeter())
 
    rp := &r
    fmt.Println("面积: ", rp.area())
    fmt.Println("周长: ", rp.perimeter())
}

接口和多态

接口意味着多态,下面是一个经典的例子,不用多说了,自己看代码吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//---------- 接 口 --------//
type shape interface {
    area() float64 //计算面积
    perimeter() float64 //计算周长
}
 
//--------- 长方形 ----------//
type rect struct {
    width, height float64
}
 
func (r *rect) area() float64 { //面积
    return r.width * r.height
}
 
func (r *rect) perimeter() float64 { //周长
    return 2*(r.width + r.height)
}
 
//----------- 圆  形 ----------//
type circle struct {
    radius float64
}
 
func (c *circle) area() float64 { //面积
    return math.Pi * c.radius * c.radius
}
 
func (c *circle) perimeter() float64 { //周长
    return 2 * math.Pi * c.radius
}
 
// ----------- 接口的使用 -----------//
func interface_test() {
    r := rect {width:2.9, height:4.8}
    c := circle {radius:4.3}
 
    s := []shape{&r, &c} //通过指针实现
 
    for _, sh := range s {
        fmt.Println(sh)
        fmt.Println(sh.area())
        fmt.Println(sh.perimeter())
    }
}

错误处理 – Error接口

函数错误返回可能是C/C++时最让人纠结的东西的,Go的多值返回可以让我们更容易的返回错误,其可以在返回一个常规的返回值之外,还能轻易地返回一个详细的错误描述。通常情况下,错误的类型是error,它有一个内建的接口。

1
2
3
type error interface {
    Error() string
}

还是看个示例吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package main
 
import "fmt"
import "errors"
 
//自定义的出错结构
type myError struct {
    arg  int
    errMsg string
}
//实现Error接口
func (e *myError) Error() string {
    return fmt.Sprintf("%d - %s", e.arg, e.errMsg)
}
 
//两种出错
func error_test(arg int) (int, error) {
    if arg < 0  {
         return -1, errors.New("Bad Arguments - negtive!")
     }else if arg >256 {
        return -1, &myError{arg, "Bad Arguments - too large!"}
    }
    return arg*arg, nil
}
 
//相关的测试
func main() {
    for _, i := range []int{-1, 4, 1000} {
        if r, e := error_test(i); e != nil {
            fmt.Println("failed:", e)
        } else {
            fmt.Println("success:", r)
        }
    }
}

程序运行后输出:

1
2
3
failed: Bad Arguments - negtive!
success: 16
failed: 1000 - Bad Arguments - too large!

错误处理 – Defer

下面的程序对于每一个熟悉C语言的人来说都不陌生(有资源泄露的问题),C++使用RAII来解决这种问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func CopyFile(dstName, srcName string) (written int64, err error) {
    src, err := os.Open(srcName)
    if err != nil {
        return
    }
 
    dst, err := os.Create(dstName)
    if err != nil {
        return
    }
 
    written, err = io.Copy(dst, src)
    dst.Close()
    src.Close()
    return
}

Go语言引入了Defer来确保那些被打开的文件能被关闭。如下所示:(这种解决方式还是比较优雅的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func CopyFile(dstName, srcName string) (written int64, err error) {
    src, err := os.Open(srcName)
    if err != nil {
        return
    }
    defer src.Close()
 
    dst, err := os.Create(dstName)
    if err != nil {
        return
    }
    defer dst.Close()
 
    return io.Copy(dst, src)
}

Go的defer语句预设一个函数调用(延期的函数),该调用在函数执行defer返回时立刻运行。该方法显得不同常规,但却是处理上述情况很有效,无论函数怎样返回,都必须进行资源释放。

我们再来看一个defer函数的示例:

1
2
3
for i := 0; i < 5; i++ {
    defer fmt.Printf("%d ", i)
}

被延期的函数以后进先出(LIFO)的顺行执行,因此以上代码在返回时将打印4 3 2 1 0。

总之,我个人觉得defer的函数行为有点怪异,我现在还没有完全搞清楚。

错误处理 – Panic/Recover

对于不可恢复的错误,Go提供了一个内建的panic函数,它将创建一个运行时错误并使程序停止(相当暴力)。该函数接收一个任意类型(往往是字符串)作为程序死亡时要打印的东西。当编译器在函数的结尾处检查到一个panic时,就会停止进行常规的return语句检查。

下面的仅仅是一个示例。实际的库函数应避免panic。如果问题可以容忍,最好是让事情继续下去而不是终止整个程序。

1
2
3
4
5
6
7
var user = os.Getenv("USER")
 
func init() {
    if user == "" {
        panic("no value for $USER")
    }
}

当panic被调用时,它将立即停止当前函数的执行并开始逐级解开函数堆栈,同时运行所有被defer的函数。如果这种解开达到堆栈的顶端,程序就死亡了。但是,也可以使用内建的recover函数来重新获得Go程的控制权并恢复正常的执行。 对recover的调用会通知解开堆栈并返回传递到panic的参量。由于仅在解开期间运行的代码处在被defer的函数之内,recover仅在被延期的函数内部才是有用的。

你可以简单地理解为recover就是用来捕捉Painc的,防止程序一下子就挂掉了。

下面是一个例程,很简单了,不解释了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
func g(i int) {
    if i>1 {
        fmt.Println("Panic!")
        panic(fmt.Sprintf("%v", i))
    }
 
}
 
func f() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered in f", r)
        }
    }()
 
    for i := 0; i < 4; i++ {
        fmt.Println("Calling g with ", i)
        g(i)
        fmt.Println("Returned normally from g.")
     }
}
 
func main() {
    f()
    fmt.Println("Returned normally from f.")
}

运行结果如下:(我们可以看到Painc后的for循环就没有往下执行了,但是main的程序还在往下走)

1
2
3
4
5
6
7
8
Calling g with  0
Returned normally from g.
Calling g with  1
Returned normally from g.
Calling g with  2
Panic!
Recovered in f 2
Returned normally from f.

你习惯这种编程方式吗?我觉得有点诡异。呵呵。

好了,上面是是一Go语言相关的编程语法的介绍,我没有事无巨细,只是让你了解一下Go语言是长什么样的。当然,这还没完,请期待下篇——Go语言的特性

posted @ 2013-06-14 11:36  陳聽溪  阅读(584)  评论(0)    收藏  举报