Merge pull request #99 from chenwenli/master

update 2.2md
This commit is contained in:
astaxie
2012-10-29 02:50:41 -07:00
4 changed files with 52 additions and 50 deletions

72
2.2.md
View File

@@ -1,12 +1,12 @@
# 2.2 Go基础
这小节我们将要介绍如何定义变量、常量、Go内置类型以及一些Go程序设计中的技巧。
这小节我们将要介绍如何定义变量、常量、Go内置类型以及Go程序设计中的一些技巧。
## 定义变量
Go语言里面定义变量有多种方式。
使用`var`关键字定义变量是最基本的与C语言定义变量不同的是Go语言变量名在变量类型前面:
使用`var`关键字是Go最基本的定义变量方式与C语言不同的是Go把变量类型放在变量名后面:
//定义一个名称为“variableName”类型为"type"的变量
var variableName type
@@ -29,7 +29,7 @@ Go语言里面定义变量有多种方式。
*/
var vname1, vname2, vname3 type= v1, v2, v3
你是不是觉得上面这样的定义有点复杂没关系因为Go语言的设计者也发现了我们来让它变得简单一点。我们可以直接忽略类型声明,那么上面的东西变成这样了:
你是不是觉得上面这样的定义有点繁琐没关系因为Go语言的设计者也发现了有一种写法可以让它变得简单一点。我们可以直接忽略类型声明,那么上面的代码变成这样了:
/*
定义三个变量,它们分别初始化相应的值
@@ -38,7 +38,7 @@ Go语言里面定义变量有多种方式。
*/
var vname1, vname2, vname3 = v1, v2, v3
还是觉得上面的有些复杂?好吧,我觉得也是。让我们继续简化:
你觉得上面的还是有些繁琐?好吧,我觉得。让我们继续简化:
/*
定义三个变量,它们分别初始化相应的值
@@ -47,7 +47,7 @@ Go语言里面定义变量有多种方式。
*/
vname1, vname2, vname3 := v1, v2, v3
现在是不是看上去非常的简单了?`:=`这个符号直接取代了`var``type`,这样的代码是不是很简洁?这种形式叫做简短声明。不过它有一个限制,那就是它只能用在函数内部;在函数外部使用则会无法编译通过,比如说用它来定义全局变量。
现在是不是看上去非常简洁了?`:=`这个符号直接取代了`var``type`,这种形式叫做简短声明。不过它有一个限制,那就是它只能用在函数内部;在函数外部使用则会无法编译通过,所以一般用`var`方式来定义全局变量。
`_`(下划线)是个特殊的变量名,任何赋予它的值都会被丢弃。在这个例子中,我们将值`35`赋予`b`,并同时丢弃`34`
@@ -63,29 +63,27 @@ Go对于已声明但未使用的变量会在编译阶段报错比如下面的
## 常量
所谓常量也就是在编译阶段就确定下来的值而程序在运行时则无法改变该值。在Go程序中常量可定义为数值、布尔值或字符串等类型。
所谓常量,也就是在程序编译阶段就确定下来的值而程序在运行时则无法改变该值。在Go程序中常量可定义为数值、布尔值或字符串等类型。
它的语法如下:
const constantName = value
//如果需要,也可以明确指定常量的类型:
const Pi float32 = 3.1415926
下面是一些常量声明的例子:
const Pi = 3.1415926
const i = 10000
const MaxThread = 10
const prefix = 'astaxie_'
当然如果需要,也可以明确指定常量的类型:
const Pi float32 = 3.1415926
const prefix = "astaxie_"
## 内置基础类型
### Boolean
在Go中布尔值的类型为`bool`可用的值是`true``false`,默认为`false`
在Go中布尔值的类型为`bool`,值是`true``false`,默认为`false`
//示例代码
var isActive bool // 全局变量声明
@@ -99,30 +97,30 @@ Go对于已声明但未使用的变量会在编译阶段报错比如下面的
### 数值类型
整数类型有无符号和带符号两种。Go同时支持`int``uint`这两种类型的长度相同但具体长度取决于编译器的实现。当前的gc和gccgo编译器在32位和64位平台上都使用32位来表示`int``uint`但未来在64位平台上可能增加到64位。Go里面也有直接定义好位数的类型`rune`, `int8`, `int16`, `int32`, `int64``byte`, `uint8`, `uint16`, `uint32`, `uint64``rune``int32`的别称,`byte``uint8`的别称。
整数类型有无符号和带符号两种。Go同时支持`int``uint`,这两种类型的长度相同,但具体长度取决于不同编译器的实现。当前的gcc和gccgo编译器在32位和64位平台上都使用32位来表示`int``uint`但未来在64位平台上可能增加到64位。Go里面也有直接定义好位数的类型`rune`, `int8`, `int16`, `int32`, `int64``byte`, `uint8`, `uint16`, `uint32`, `uint64`其中`rune``int32`的别称,`byte``uint8`的别称。
>需要注意的一点是,这些类型的变量之间不允许互相赋值或操作,不然会在编译时引起编译器报错。
>
>如下的代码会产生错误
>
> var a int8
> var b int32
> c:=a + b
>> var a int8
>> var b int32
>> c:=a + b
>
>另外尽管int的长度是32 bit, 但int 与 int32并不可以互用。
浮点数的类型有`float32``float64`两种(没有`float`类型),默认是`float64`
这就是全部吗NoGo还支持复数。它的默认类型是`complex128`64位实数+64位虚数。如果需要小一些的也有`complex64`(32位实数+32位虚数)。复数的形式为`re + imi`,其中`re`是实数部分,`im`是虚数部分,而最后的`i`是虚数单位。下面是一个使用复数的例子:
这就是全部吗NoGo还支持复数。它的默认类型是`complex128`64位实数+64位虚数。如果需要小一些的也有`complex64`(32位实数+32位虚数)。复数的形式为`RE + IMi`,其中`RE`是实数部分,`IM`是虚数部分,而最后的`i`是虚数单位。下面是一个使用复数的例子:
var c complex64 = 5+5i
//output: (5+5i)
fmt.Printf("Value is: %v", c)
它会打印:(5+5i)
### 字符串
我们在上一节中讲过Go中的字符串都是用`UTF-8`的形式编码。字符串通过用一对双引号(`""`)或反引号(`` ` ```` ` ``)括起来定义,它的类型是`string`。
我们在上一节中讲过Go中的字符串都是`UTF-8`字符集编码。字符串用一对双引号(`""`)或反引号(`` ` `` `` ` ``)括起来定义,它的类型是`string`。
//示例代码
var frenchHello string // 声明变量为字符串的一般方法
@@ -148,7 +146,7 @@ Go对于已声明但未使用的变量会在编译阶段报错比如下面的
fmt.Printf("%s\n", s2)
Go中可以使用`+`来链接两个字符串:
Go中可以使用`+`操作符来连接两个字符串:
s := "hello,"
m := " world"
@@ -157,7 +155,7 @@ Go中可以使用`+`来链接两个字符串:
修改字符串也可写为:
s := "hello"
s := "hello"
s = "c" + s[1:] // 字符串虽不能更改,但可进行切片操作
fmt.Printf("%s\n", s)
@@ -237,8 +235,8 @@ Go里面有一个关键字`iota`,这个关键字用来声明`enum`的时候采
### Go程序设计的一些规则
Go之所以会那么简洁是因为它有一些默认的行为
- 大写字母开头的变量是导出的,也就是其它包可以读取的,类似`class`中`public`的概念;小写字母开头的就是导出的
- 大写字母开头的函数也是一样,相当于`public`函数;小写字母开头的就是类似`private`
- 大写字母开头的变量是导出的,也就是其它包可以读取的,是公用变量;小写字母开头的就是不可导出的,是私有变量。
- 大写字母开头的函数也是一样,相当于`class`中的带`public`关键词的公有函数;小写字母开头的就是`private`关键词的私有函数。
## array、slice、map
@@ -252,7 +250,8 @@ Go之所以会那么简洁是因为它有一些默认的行为
var arr [10]int // 声明了一个int类型的数组
arr[0] = 42 // 数组下标是从0开始的
arr[1] = 13 // 赋值操作
fmt.Printf("The first element is %d\n", arr[0]) // 获取数据
fmt.Printf("The first element is %d\n", arr[0]) // 获取数据返回42
fmt.Printf("The last element is %d\n", arr[9]) //返回未赋值的最后一个元素默认返回0
由于长度也是数组类型的一部分,因此`[3]int`与`[4]int`是不同的类型,数组也就不能改变长度。数组之间的赋值是值的赋值,即当把一个数组作为参数传入函数的时候,传入的其实是该数组的副本,而不是它的指针。如果要使用指针,那么就需要用到后面介绍的`slice`类型了。
@@ -264,7 +263,7 @@ Go之所以会那么简洁是因为它有一些默认的行为
c := [...]int{4, 5, 6} // 可以省略长度而采用`...`的方式Go会自动根据元素个数来计算长度
也许你会说我想数组里面还是数组能实现吗当然咯Go支持嵌套数组即多维数组。比如下面的代码就声明了一个二维数组
也许你会说,我想数组里面的值还是数组能实现吗当然咯Go支持嵌套数组即多维数组。比如下面的代码就声明了一个二维数组
// 声明了一个二维数组该数组以两个数组作为元素其中每个数组中又有4个int类型的元素
doubleArray := [2][4]int{[4]int{1, 2, 3, 4}, [4]int{5, 6, 7, 8}}
@@ -279,9 +278,9 @@ Go之所以会那么简洁是因为它有一些默认的行为
### slice
在很多应用场景中,数组并不能满足我们的需求。在刚开始我们并不知道需要多大的数组因此我们就需要“动态数组”。在Go里面这种数据结构叫`slice`
在很多应用场景中,数组并不能满足我们的需求。在初始定义数组我们并不知道需要多大的数组因此我们就需要“动态数组”。在Go里面这种数据结构叫`slice`
`slice`并不是真正意义上的动态数组,而是一个引用类型。`slice`总是指向底层的一个`array``slice`的声明也可以像`array`一样,只是不需要长度。
`slice`并不是真正意义上的动态数组,而是一个引用类型。`slice`总是指向一个底层`array``slice`的声明也可以像`array`一样,只是不需要长度。
// 和声明array一样只是少了长度
var fslice []int
@@ -306,7 +305,7 @@ Go之所以会那么简洁是因为它有一些默认的行为
b = ar[3:5]
// b的元素是ar[3]和ar[4]
>注意`slice`和数组在声明时的区别:声明数组时,方括号内写明了数组的长度或使用`...`自动计算长度,而声明``slice``时,方括号内没有任何字符。
>注意`slice`和数组在声明时的区别:声明数组时,方括号内写明了数组的长度或使用`...`自动计算长度,而声明`slice`时,方括号内没有任何字符。
它们的数据结构如下所示
@@ -316,7 +315,7 @@ slice有一些简便的操作
- `slice`的默认开始位置是0`ar[:n]`等价于`ar[0:n]`
- `slice`的第二个序列默认是数组的长度,`ar[n:]`等价于`ar[n:len(ar)]`
- `slice`如果从一个数组里面直接获取,可以这样`ar[:]`因为默认第一个序列是0第二个是数组的长度即等价于`ar[0:len(ar)]`
- 如果从一个数组里面直接获取`slice`,可以这样`ar[:]`因为默认第一个序列是0第二个是数组的长度即等价于`ar[0:len(ar)]`
下面这个例子展示了更多关于`slice`的操作:
@@ -376,8 +375,7 @@ slice有一些简便的操作
numbers["three"] = 3
fmt.Println("第三个数字是: ", numbers["three"]) // 读取数据
// 打印出来如:
// 第三个数字是: 3
// 打印出来如:第三个数字是: 3
这个`map`就像我们平常看到的表格一样,左边列是`key`,右边列是值
@@ -416,15 +414,19 @@ slice有一些简便的操作
`make`用于内建类型(`map`、`slice` 和`channel`)的内存分配。`new`用于各种类型的内存分配。
内建函数`new`本质上说跟其它语言中的同名函数功能一样:`new(T)`分配了零值填充的`T`类型的内存空间,并且返回其地址,即一个`*T`类型的值。用Go的术语说它返回了一个指针指向新分配的类型`T`的零值。有一点非常重要:`new`返回指针。
内建函数`new`本质上说跟其它语言中的同名函数功能一样:`new(T)`分配了零值填充的`T`类型的内存空间,并且返回其地址,即一个`*T`类型的值。用Go的术语说它返回了一个指针指向新分配的类型`T`的零值。有一点非常重要:
内建函数`make(T, args)`与`new(T)`有着不同的功能,它只能创建`slice`、`map`和`channel`,并且返回一个有初始值(非零)的`T`类型,而不是`*T`。本质来讲,导致这三个类型有所不同的原因是指向数据结构的引用在使用前必须被初始化。例如,一个`slice`,是一个包含指向数据(内部`array`)的指针、长度和容量的三项描述符;在这些项目被初始化之前,`slice`为`nil`。对于`slice`、`map`和`channel`来说,`make`初始化了内部的数据结构,填充适当的值。`make`返回初始化后的(非零)值
>`new`返回指针
内建函数`make(T, args)`与`new(T)`有着不同的功能make只能创建`slice`、`map`和`channel`,并且返回一个有初始值(非零)的`T`类型,而不是`*T`。本质来讲,导致这三个类型有所不同的原因是指向数据结构的引用在使用前必须被初始化。例如,一个`slice`,是一个包含指向数据(内部`array`)的指针、长度和容量的三项描述符;在这些项目被初始化之前,`slice`为`nil`。对于`slice`、`map`和`channel`来说,`make`初始化了内部的数据结构,填充适当的值。
>`make`返回初始化后的(非零)值。
下面这个图详细的解释了`new`和`make`之间的区别。
![](images/2.2.makenew.png?raw=true)
关于“零值”,所指并非是空值,而是一种“变量未填充前”的默认值通常为 0。
关于“零值”,所指并非是空值,而是一种“变量未填充前”的默认值通常为0。
此处罗列 部分类型 的 “零值”
int 0

22
2.3.md
View File

@@ -1,11 +1,11 @@
# 2.3 流程和函数
这小节我们要介绍Go里面的流程控制以及函数操作
## 流程控制
流程控制在编程语言中是最伟大的发明了,因为有了它,你可以通过很简单的描述来表达很复杂的事情
流程控制在编程语言中是最伟大的发明了,因为有了它,你可以通过很简单的流程描述来表达很复杂的逻辑。流程控制包含分三大类:条件判断,循环控制和无条件跳转
### if
`if`也许是所有语言中最常见的了,它的语法概括起来就是:`如果满足条件就做某事,否则做另一件事`
`if`也许是各种编程语言中最常见的了,它的语法概括起来就是:`如果满足条件就做某事,否则做另一件事`
Go里面`if`条件语法中不需要括号,如下代码所示
Go里面`if`条件判断语句中不需要括号,如下代码所示
if x > 10 {
fmt.Println("x is greater than 10")
@@ -13,7 +13,7 @@ Go里面`if`条件语法中不需要括号,如下代码所示
fmt.Println("x is less than 10")
}
Go的`if`还有一个强大的地方就是条件里面允许声明一个变量,这个变量的作用域只能在该条件中,出了这个条件就不起作用了,如下所示
Go的`if`还有一个强大的地方就是条件判断语句里面允许声明一个变量,这个变量的作用域只能在该条件逻辑块内,其他地方就不起作用了,如下所示
// 计算获取值x,然后根据x返回的大小判断是否大于10。
if x := computedValue(); x > 10 {
@@ -37,7 +37,7 @@ Go的`if`还有一个强大的地方就是条件里面允许声明一个变量
### goto
Go有`goto`语句——请明智地使用它。用`goto`跳转到一定是当前函数内定义的标签。例如假设这样一个循环:
Go有`goto`语句——请明智地使用它。用`goto`跳转到必须在当前函数内定义的标签。例如假设这样一个循环:
func myFunc() {
i := 0
@@ -47,7 +47,7 @@ Go有`goto`语句——请明智地使用它。用`goto`跳转到一定是当前
goto Here //跳转到Here去
}
标签名是大小写敏感的。
>标签名是大小写敏感的。
### for
Go里面最强大的一个控制逻辑就是`for`,它即可以用来循环读取数据,又可以当作`while`来控制逻辑,还能迭代操作。它的语法如下:
@@ -56,7 +56,7 @@ Go里面最强大的一个控制逻辑就是`for`,它即可以用来循环读
//...
}
`expression1``expression2``expression3`都是表达式,其中`expression1``expression3`是变量声明或者函数调用返回值之类的,`expression2`是条件判断,`expression1`在循环开始之前调用,`expression3`在每轮循环结束之时调用。
`expression1``expression2``expression3`都是表达式,其中`expression1``expression3`是变量声明或者函数调用返回值之类的,`expression2`用来条件判断,`expression1`在循环开始之前调用,`expression3`在每轮循环结束之时调用。
一个例子比上面讲那么多更有用,那么我们看看下面的例子吧:
@@ -224,7 +224,7 @@ Go里面最强大的一个控制逻辑就是`for`,它即可以用来循环读
上面这个里面我们可以看到`max`函数有两个参数,它们的类型都是`int`,那么第一个变量的类型可以省略(即 a,b int,而非 a int, b int)默认为离它最近的类型同理多于2个同类型的变量或者返回值。同时我们注意到它的返回值就是一个类型这个就是省略写法。
### 多个返回值
Go语言和C相比更先进的地方,其中一点就是能够返回多个值也许这个思想来源于Python
Go语言比C更先进的特性,其中一点就是函数能够返回多个值。
我们直接上代码看例子
@@ -246,7 +246,7 @@ Go语言和C相比更先进的地方其中一点就是能够返回多个
fmt.Printf("%d * %d = %d\n", x, y, xTIMESy)
}
上面的例子我们可以看到直接返回了两个参数,当然我们也可以命名返回参数的变量,这个例子里面只是用了两个类型,我们也可以改成如下这样的定义,然后返回的时候不用带上变量名,因为直接在函数里面初始化了。但如果你的函数是导出的(首字母大写),官方建议最好命名返回值,因为不命名返回值,虽然使得代码更加简洁了,但是会造成生成的文档不易读
上面的例子我们可以看到直接返回了两个参数,当然我们也可以命名返回参数的变量,这个例子里面只是用了两个类型,我们也可以改成如下这样的定义,然后返回的时候不用带上变量名,因为直接在函数里面初始化了。但如果你的函数是导出的(首字母大写),官方建议最好命名返回值,因为不命名返回值,虽然使得代码更加简洁了,但是会造成生成的文档可读性差
func SumAndProduct(A, B int) (add int, Multiplied int) {
add = A+B
@@ -265,7 +265,7 @@ Go函数支持变参。接受变参的函数是有着不定数量的参数的。
}
### 传值与传指针
当我们传一个参数值到被调用函数里面时实际上是传了这个值的一份copy当在被调用函数中修改参数值的时候调用函数中相应实参不会发生任何变化因为我们作用在copy上
当我们传一个参数值到被调用函数里面时实际上是传了这个值的一份copy当在被调用函数中修改参数值的时候调用函数中相应实参不会发生任何变化因为数值变化只作用在copy上。
为了验证我们上面的说法,我们来看一个例子
@@ -320,7 +320,7 @@ Go函数支持变参。接受变参的函数是有着不定数量的参数的。
这样,我们就达到了修改`x`的目的。那么到底传指针有什么好处呢?
- 传指针使得多个函数能操作同一个对象。
- 传指针比较轻量级 (8 bytes)只是传内存地址,我们可以通过指针高效的传递大的结构体。如果传值的话,那么每次传递, 在copy上面就会花费大量的时间和内存。所以记住了,当你要传递大的结构体的时候,用指针是一个明智的选择。
- 传指针比较轻量级 (8bytes),只是传内存地址,我们可以用指针传递体积大的结构体。如果用参数值传递的话, 在每次copy上面就会花费相对较多的系统开销(内存和时间)。所以当你要传递大的结构体的时候,用指针是一个明智的选择。
- Go语言中`string``slice``map`这三种类型的实现机制类似指针,所以可以直接传递,而不用取地址后传递指针。(注:若函数需改变`slice`的长度,则仍需要取地址传递指针)
### defer

2
8.1.md
View File

@@ -215,7 +215,7 @@ Go语言中通过net包中的`DialTCP`函数来建立一个TCP连接并返回
conn.Write([]byte(daytime)) // don't care about return value
// we're finished with this client
}
func checkError(err os.Error) {
func checkError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
os.Exit(1)

6
genepub.sh Executable file → Normal file
View File

@@ -9,13 +9,13 @@ fi
cd html
cp ../*.md .
for i in *.md;do
#重新格式化md文件
#<EFBFBD><EFBFBD><EFBFBD>¸<EFBFBD>ʽ<EFBFBD><EFBFBD>md<EFBFBD>ļ<EFBFBD>
sed -i '/^[#]\{1,\}/s!^\([#]\{1,\}\)\([^#]\{1,\}\)!\1 \2!' $i
sed -i '/^[#]\{1,\}/s! ! !' $i
#处理md文件中的image src属性
#<EFBFBD><EFBFBD><EFBFBD><EFBFBD>md<EFBFBD>ļ<EFBFBD><EFBFBD>е<EFBFBD>image src<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
sed -i '/!\[\](images/s#images\(.*\)?raw=true#../Images\1#' $i
done
../build-web-application-with-golang >/dev/null
rm *.md
echo "文件已经就绪请使用sigil导入html目录中的html文件和images目录中的图片文件制作epub"
echo "<EFBFBD>ļ<EFBFBD><EFBFBD>Ѿ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD><EFBFBD>sigil<EFBFBD><EFBFBD><EFBFBD><EFBFBD>htmlĿ¼<EFBFBD>е<EFBFBD>html<EFBFBD>ļ<EFBFBD><EFBFBD><EFBFBD>imagesĿ¼<EFBFBD>е<EFBFBD>ͼƬ<EFBFBD>ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>epub"