文档章节

Google Go Primer(一)

曾赛
 曾赛
发布于 2010/04/27 15:08
字数 3025
阅读 641
收藏 0

Go语言是什么?

Google最近发布新型的编程语言,Go。它被设计为将现代编程语言的先进 性带入到目前仍由C语言占统治地位的系统层面。然而,这一语言仍在试验阶段并在不断演变。

Go语言的设计者计划设计一门简单、高效、安全和 并发的语言。这门语言简单到甚至不需要有一个符号表来进行词法分析。它可以快速地编译;整个工程的编译时间在秒以下的情况是常事。它具备垃圾回收功能,因 此从内存的角度是安全的。它进行静态类型检查,并且不允许强制类型转换,因而对于类型而言是安全的。同时语言还内建了强大的并发实现机制。

阅读Go

Go的语法传承了与C一样的风格。程序由函数组成,而函数体是一系列的语句序列。一段代码块用花括号括起来。这门语言保留有限的关键字。表达式使用 同样的中缀运算符。语法上并无 太多出奇之处。

Go语言的作者在设计这一语言时坚持一个单一的指导原则:简单明了至上。一些新的语法构件提供了简明地表达一些约定俗成的概 念的方式,相较之下用C表达显得冗长。而其他方面则是针对几十年的使用所呈现出来的一些不合理的语言选择作出了改进。

变量声明

变量是如下声明的:

var sum int // 简单声明
var total int = 42 // 声明并初始化

最值得注意的是,这些声明里的类型跟在变量名的后面。乍一看有点怪,但这更清晰明了。比如,以下面这个C片段来说:

int* a, b;

它并明了,但这里实际的意思是a是一个指针,但b不是。如果要将两者都声明为指针,必须要重复星号。然后在Go语言里,通过如下方式可以将两者都 声明为指针:

var a, b *int

如果一个变量初始化了,编译器通常能推断它的类型,所以程序员不必显式的敲出来:

var label = "name"

然而,在这种情况下var几乎显得是多余了。因此,Go的作者引入了一个新的运算符来 声明和初始化一个新的变量:

name := "Samuel"

条件语句

Go语言当中的条件句与C当中所熟知的if-else构造一样,但条件不需要被打包在括号内。这样可以减少阅读代码时的视觉上的混乱。

括号并不是唯一被移去的视觉干扰。在条件之间可以包括一个简单的语句,所以如下的代码:

result := someFunc();
if result > 0 {
/* Do something */
} else {
/* Handle error */
}

可以被精简成:

if result := someFunc(); result > 0 { 
/* Do something */
} else {
/* Handle error */
}

然而,在后面这个例子当中,result只在条件块内部有效??而前者 中,它在整个包含它的上下文中都是可存取的。

分支语句

分支语句同样是似曾相识,但也有增强。像条件语句一样,它允许一个简单的语句位于分支的表达式之前。然而,他们相对于在C语言中的分支而言走得更远。

首先,为了让分支跳转更简明,作了两个修改。情况可以是逗号分隔的列表,而fall-throuth也不再是默认的行为。

因此,如下的C代码:

int result;
switch (byte) {
case 'a':
case 'b':
{
result = 1
break
}

default:
result = 0
}

在Go里就变成了这样:

var result int
switch byte {
case 'a', 'b':
result = 1
default:
result = 0
}

第二点,Go的分支跳转可以匹配比整数和字符更多的内容,任何有效的表达式都可以作为跳转语句值。只要它与分支条件的类型是一样的。

因此如下的C代码:

int result = calculate();
if (result < 0) {
/* negative */
} else if (result > 0) {
/* positive */
} else {
/* zero */
}

在Go里可以这样表达:

switch result := calculate(); true {
case result < 0:
/* negative */
case result > 0:
/* positive */
default:
/* zero */
}

这些都是公共的约定俗成,比如如果分支值省略了,就是默认为真,所以上面的代码可以这样写:

switch result := calculate(); {
case result < 0:
/* negative */
case result > 0:
/* positive */
default:
/* zero */
}

循环

Go只有一个关键字用于引入循环。但它提供了除do-while外C语言当中所有可用的循环方式。

条件

for a > b { /* ... */ }

初始,条件和步进

for i := 0; i < 10; i++ { /* ... */ }

范围

range语句右边的表达式必须是arrayslicestring或者map, 或是指向array的指针,也可以是channel

for i := range "hello" { /* ... */ }

无限循环

for { /* ever */ }

函数

声明函数的语法与C不同。就像变量声明一样,类型是在它们所描述的术语之后声明的。在C语言中:

int add(int a, b) { return a + b }

在Go里面是这样描述的:

func add(a, b int) int { return a + b }

多返回值

在C语言当中常见的做法是保留一个返回值来表示错误(比如,read()返回0),或 者保留返回值来通知状态,并将传递存储结果的内存地址的指针。这容易产生了不安全的编程实践,因此在像Go语言这样有良好管理的语言中是不可行的。

认识到这一问题的影响已超出了函数结果与错误通讯的简单需求的范畴,Go的作者们在语言中内建了函数返回多个值的能力。

作为例子,这个函数将返回整数除法的两个部分:

func divide(a, b int) (int, int) {
quotient := a / b
remainder := a % b
return quotient, remainder
}

有了多个返回值,有良好的代码文档会更好??而Go允许你给返回值命名,就像参数一样。你可以对这些返回的变量赋值,就像其它的变量一样。所以我们可以重写divide

func divide(a, b int) (quotient, remainder int) {
quotient = a / b
remainder = a % b
return
}

多返回值的出现促进了"comma-ok"的模式。有可能失败的函数可以返回第二个布尔结果来表示成功。作为替代,也可以返回一个错误对象,因此像下面这样的代码也就不见怪了:

if result, ok := moreMagic(); ok {
/* Do something with result */
}

匿名函数

有了垃圾收集器意味着为许多不同的特性敞开了大门??其中就包括匿名函数。Go为声明匿名函数提供了简单的语法。像许多动态语言一样,这些函数在它们被定义的范围内创建了词法闭包。

考虑如下的程序:

func makeAdder(x int) (func(int) int) {
return func(y int) int { return x + y }
}

func main() {
add5 := makeAdder(5)
add36 := makeAdder(36)
fmt.Println("The answer:", add5(add36(1))) //=> The answer: 42
}

基本类型

像C语言一样,Go提供了一系列的基本类型,常见的布尔,整数和浮点数类型都具备。它有一个Unicode的字符串类型和数组类型。同时该语言还引入了两 种新的类型:slice map

数组和切片

Go语言当中的数组不是像C语言那样动态的。它们的大小是类型的一部分,在编译时就决定了。数组的索引还是使用的熟悉的C语法(如 a[i]),并且与C一样,索引是由0开始的。编译器提供了内建的功能在编译时求得一个数组的长度 (如len(a))。如果试图超过数组界限写入,会产生一个运行时错误。

Go还提供了切片(slices),作为数组的变形。一个切片(slice)表示一个数组内的连续分段,支持程序员指定底层存储的明确部分。构建一个切片 的语法与访问一个数组元素类似:

/* Construct a slice on ary that starts at s and is len elements long */
s1 := ary[s:len]

/* Omit the length to create a slice to the end of ary */
s2 := ary[s:]

/* Slices behave just like arrays */
s[0] == ary[s] //=> true

// Changing the value in a slice changes it in the array
ary[s] = 1
s[0] = 42
ary[s] == 42 //=> true

该切片所引用的数组分段可以通过将新的切片赋值给同一变量来更改:

/* Move the start of the slice forward by one, but do not move the end */
s2 = s2[1:]

/* Slices can only move forward */
s2 = s2[-1:] // this is a compile error

切片的长度可以更改,只要不超出切片的容量。切片s的容量是数组从s[0]到数组尾端的大小,并由内建的cap()函数返回。一个切片的长度永远不能超出它的容量。

这里有一个展示长度和容量交互的例子:

a := [...]int{1,2,3,4,5} // The ... means "whatever length the initializer has"
len(a) //=> 5

/* Slice from the middle */
s := a[2:4] //=> [3 4]
len(s), cap(s) //=> 2, 3

/* Grow the slice */
s = s[0:3] //=> [3 4 5]
len(s), cap(s) //=> 3, 3

/* Cannot grow it past its capacity */
s = s[0:4] // this is a compile error

通常,一个切片就是一个程序所需要的全部了,在这种情况下,程序员根本用不着一个数组,Go有两种方式直接创建切片而不用引用底层存储:

/* literal */
s1 := []int{1,2,3,4,5}

/* empty (all zero values) */
s2 := make([]int, 10) // cap(s2) == len(s2) == 10

Map类型

几乎每个现在流行的动态语言都有的数据类型,但在C中不具备的,就是dictionary。Go提供了一个基本的dictionary类型叫做map。下 面的例子展示了如何创建和使用Go map:

m := make(map[string] int) // A mapping of strings to ints

/* Store some values */
m["foo"] = 42
m["bar"] = 30

/* Read, and exit program with a runtime error if key is not present. */
x := m["foo"]

/* Read, with comma-ok check; ok will be false if key was not present. */
x, ok := m["bar"]

/* Check for presence of key, _ means "I don't care about this value." */
_, ok := m["baz"] // ok == false

/* Assign zero as a valid value */
m["foo"] = 0;
_, ok := m["foo"] // ok == true

/* Delete a key */
m["bar"] = 0, false
_, ok := m["bar"] // ok == false

面向对象

Go语言支持类似于C语言中使用的面向对象风格。数据被组织成structs,然后定义操作这些structs的函数。类似于Python,Go语言提供 了定义函数并调用它们的方式,因此语法并不会笨拙。

Struct类型

定义一个新的struct类型很简单:

type Point struct {
x, y float64
}

现在这一类型的值可以通过内建的函数new来分配,这将返回一个指针,指向一块内存单元,其所占内存槽初始化为零。

var p *Point = new(Point)
p.x = 3
p.y = 4

这显得很冗长,而Go语言的一个目标是尽可能的简明扼要。所以提供了一个同时分配和初始化struct的语法:

var p1 Point = Point{3,4}  // Value
var p2 *Point = &Point{3,4} // Pointer

方法

一旦声明了类型,就可以将该类型显式的作为第一个参数来声明函数:

func (self Point) Length() float {
return math.Sqrt(self.x*self.x + self.y*self.y);
}

这些函数之后可作为struct的方法而被调用:

p := Point{3,4}
d := p.Length() //=> 5

方法实际上既可以声明为值也可以声明为指针类型。Go将会适当的处理引用或解引用对象,所以既可以对类型T,也可以对类型*T声明方式,并合理地使用它们。

让我们为Point扩展一个变换器:

/* Note the receiver is *Point */
func (self *Point) Scale(factor float64) {
self.x = self.x * factor
self.y = self.y * factor
}

然后我们可以像这样调用:

p.Scale(2);
d = p.Length() //=> 10

很重要的一点是理解传递给MoveToXYself和其它的参数一样,并且是传递,而不是引用传递。如果它被声明为Point,那么在方法内修改的struct就不再跟调用方的一样??值在它们传递给方法的时候被 拷贝,并在调用结束后被丢弃。

 

查看英文原文Google Go: A Primer 阅读全文
类别: Golang  查看评论

本文转载自:http://hi.baidu.com/workspaces/blog/item/f3baf036571fa21d91ef39ce.html

曾赛

曾赛

粉丝 353
博文 259
码字总数 81359
作品 0
成都
高级程序员
私信 提问
求《c++ primer 5》中文版pdf

根据历来经验, 书柜里很多书,老衲很少完整看完某一本, 即使是当年买的潘爱民翻译的《c++ primer 3》到现在都还没看完, so, 现在想找本《c++ primer 5》中文pdf 放到手机里随时、偶尔、心...

明月惊鹊
2015/06/14
8.8K
8
Kotlin 资源大全 - 学 Kotlin 看这一篇教程就够了

目录 介绍 官网及文档 中文社区 教程 & 文章 开源库和框架 Demo 其他 介绍 为什么要做这个? 今天凌晨的 Google I/O 上,Google 正式宣布官方支持 Kotlin. 为了让大家更快了解和上手 Kotlin...

稀土君
2017/05/19
0
0
11月推荐给程序员们的四本书

难得在家休息一段时间,职业病的原因吧,推荐技术书的习惯一时间没能改变。借着自己的微信平台,每天向大家推荐一些靠谱的内容,希望大家能喜欢。 先跟着我来看我今天推荐的第一本书吧! C+...

生气的散人
2013/11/11
988
2
Android开发技术周报 Issue#154

十一月 10, 20170 条评论AndroidDevWeekly Android开发技术周报 Issue#154 新闻 Google 官方文件管理器 Files Go 上线:入门设备也能流畅运行 Google 发布 Android 十一月更新:修复 ...

脉脉不得语
2017/11/10
0
0
机智云官网用到的库-primer.css我解析

Primer :解析结果-不要用。弃用理由:primer.css相当于默认样式,该文件有一大堆标签和类,单位是px和em,我认为在实际开发中会造成类名冲突、px em rem混用影响响应式布局、以及后期反编译...

hhj187
2016/09/04
10
0

没有更多内容

加载失败,请刷新页面

加载更多

Phpstorm2018 永久激活

1、安装phpstorm,安装包请自行官网下载 http://www.jetbrains.com/phpstorm/download/ 2、下载JetbrainsCrack.jar文件,存放至你的phpstorm执行文件同级目录下 下载JetbrainsCrack.jar 提取...

happyfish319
34分钟前
7
0
谈一谈Android进程间通信的几种方式

###来看一下Android中除了AIDL还有哪些进程间通信的方式: 1、Bundle Bundle实现了Parcelable,所以在Android中我们可以通过Intent在不同进程间传递Bundle数据。 但是在Intent 传输数据的过程...

二营长的意大利炮手
34分钟前
7
0
互联网薪资“高开低走”,你的能力是否真的可以匹配高薪?

对于国内外主流互联网大厂,技术出身似乎已经成为各大掌门人的必备标签。谷歌 CEO 桑达尔·皮查伊、马克·扎克伯格、李彦宏、马化腾、雷军等等皆为技术人出身,都曾参与了公司内部重要产品的...

Java技术剑
36分钟前
10
0
java 多线程

线程声明周期 线程的五个状态:新建,就绪,运行,阻塞,死亡。 其中就绪和运行两个状态客户互相转换,但运行到阻塞,阻塞到就绪,只能单向转换。 刚new出的线程就是【新建】状态,调用start...

雷开你的门
38分钟前
14
0
构造器Constructor是否可被overrid

构造器不能被重写,不能用static修饰构造器,只能用public private protected这三个权限修饰符,且不能有返回语句。

无名氏的程序员
42分钟前
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部