Skip to content

Commit

Permalink
第九章修改 (#841)
Browse files Browse the repository at this point in the history
Co-authored-by: Joe Chen <[email protected]>
loftea and unknwon authored May 10, 2022

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 30ca13a commit 471d59a
Showing 11 changed files with 80 additions and 70 deletions.
3 changes: 2 additions & 1 deletion eBook/09.0.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 9.0 包package
# 9.0 包 (package)

本章主要针对 Go 语言的包展开讲解。

@@ -7,3 +7,4 @@
- [目录](directory.md)
- 上一节:[将 map 的键值对调](08.6.md)
- 下一节:[标准库概述](09.1.md)

14 changes: 7 additions & 7 deletions eBook/09.1.md
Original file line number Diff line number Diff line change
@@ -69,9 +69,9 @@ for e := l.Front(); e != nil; e = e.Next() {
- `log`: 记录程序运行时产生的日志,我们将在后面的章节使用它。
- `encoding/json`-`encoding/xml`-`text/template`:
- `encoding/json`: 读取并解码和写入并编码 JSON 数据。
- `encoding/xml`: 简单的 XML1.0 解析器,有关 JSON 和 XML 的实例请查阅第 12.9/10 章节。
- `text/template`:生成像 HTML 一样的数据与文本混合的数据驱动模板(参见第 15.7 节)。
- `net`-`net/http`-`html`:(参见第 15 章)
- `encoding/xml`: 简单的 XML1.0 解析器,有关 JSON 和 XML 的实例请查阅第 [12.9](12.9.md)/[10](10.0.md) 章节。
- `text/template`:生成像 HTML 一样的数据与文本混合的数据驱动模板(参见[第 15.7 节](15.7.md))。
- `net`-`net/http`-`html`:(参见[ 15 章](15.0.md)
- `net`: 网络数据的基本操作。
- `http`: 提供了一个可扩展的 HTTP 服务器和基础客户端,解析 HTTP 请求和回复。
- `html`: HTML5 解析器。
@@ -80,16 +80,16 @@ for e := l.Front(); e != nil; e = e.Next() {

`exp` 包中有许多将被编译为新包的实验性的包。在下次稳定版本发布的时候,它们将成为独立的包。如果前一个版本已经存在了,它们将被作为过时的包被回收。然而 Go1.0 发布的时候并没有包含过时或者实验性的包。

**练习 9.1**
**练习 9.1** [Q20_linked_list.go](exercises\chapter_9\dlinked_list.go)

使用 `container/list` 包实现一个双向链表,将 101102 和 103 放入其中并打印出来。
使用 `container/list` 包实现一个双向链表,将 `101``102``103` 放入其中并打印出来。

**练习 9.2**
**练习 9.2** [size_int.go](exercises\chapter_9\size_int.go)

通过使用 `unsafe` 包中的方法来测试你电脑上一个整型变量占用多少个字节。

## 链接

- [目录](directory.md)
- 上一节:[package](09.0.md)
- 上一节:[ (package)](09.0.md)
- 下一节:[regexp 包](09.2.md)
11 changes: 6 additions & 5 deletions eBook/09.11.md
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@

当开始一个新项目或增加新的功能到现有的项目,你可以通过在应用程序中使用已经存在的库来节省开发时间。为了做到这一点,你必须理解库的 API(应用编程接口),那就是:库中有哪些方法可以调用,如何调用。你可能没有这个库的源代码,但作者肯定有记载的 API 以及详细介绍了如何使用它。

作为一个例子,我们将使用谷歌的 API 的 urlshortener 编写一个小程序:你可以尝试一下在 http://goo.gl/ 输入一个像 "http://www.destandaard.be" 这样的 URL,你会看到一个像 "http://goo.gl/O9SUO" 这样更短的 URL 返回,也就是说,在 Twitter 之类的服务中这是非常容易嵌入的。谷歌 urlshortener 服务的文档可以在 "http://code.google.com/apis/urlshortener/" 找到。(第 19 章,我们将开发自己版本的 urlshortener)。
作为一个例子,我们将使用谷歌的 API 的 urlshortener 编写一个小程序:你可以尝试一下在 http://goo.gl/ 输入一个像 "http://www.destandaard.be" 这样的 URL,你会看到一个像 "http://goo.gl/O9SUO" 这样更短的 URL 返回,也就是说,在 Twitter 之类的服务中这是非常容易嵌入的。谷歌 urlshortener 服务的文档可以在 "http://code.google.com/apis/urlshortener/" 找到。([第 19 章](19.0.md),我们将开发自己版本的 urlshortener)。

谷歌将这项技术提供给其他开发者,我们可以在我们自己的应用程序中调用 API (释放到指定的限制)。他们也生成了一个 Go 语言客户端库使调用变得更容易。

@@ -24,9 +24,9 @@ go install 将下载源码,编译并安装包

import "google.golang.org/api/urlshortener/v1"

现在我们写一个 Web 应用(参见第 15 章 4-8 节)通过表单实现短地址和长地址的相互转换。我们将使用 `template` 包并写三个处理函数:root 函数通过执行表单模板来展示表单,short 函数将长地址转换为短地址,long 函数逆向转换。
现在我们写一个 Web 应用(参见[ 15 章 4-8 节](15.4.md))通过表单实现短地址和长地址的相互转换。我们将使用 `template` 包并写三个处理函数:`root()` 函数通过执行表单模板来展示表单,`short()` 函数将长地址转换为短地址,`long()` 函数逆向转换。

要调用 urlshortener 接口必须先通过 http 包中的默认客户端创建一个服务实例 urlshortenerSvc:
要调用 `urlshortener` 接口必须先通过 `http` 包中的默认客户端创建一个服务实例 `urlshortenerSvc`
```go
urlshortenerSvc, _ := urlshortener.New(http.DefaultClient)
```
@@ -47,7 +47,7 @@ url, error := urlshortenerSvc.Url.Get(shwortUrl).Do()

返回的长地址便是转换前的原始地址。

示例 9.9 [urlshortener.go](examples/chapter_9/use_urlshortener.go)
示例 9.9 [urlshortener.go](examples/chapter_9/use_urlshortener.go)

```go
package main
@@ -146,4 +146,5 @@ script: _go_app

- [目录](directory.md)
- 上一节:[Go 的外部包和项目](09.10.md)
- 下一章:[结构(struct)与方法(method)](10.0.md)
- 下一章:[结构 (struct) 与方法 (method)](10.0.md)

10 changes: 5 additions & 5 deletions eBook/09.2.md
Original file line number Diff line number Diff line change
@@ -2,21 +2,21 @@

正则表达式语法和使用的详细信息请参考 [维基百科](http://en.wikipedia.org/wiki/Regular_expression)

在下面的程序里,我们将在字符串中对正则表达式模式pattern进行匹配。
在下面的程序里,我们将在字符串中对正则表达式模式 (pattern) 进行匹配。

如果是简单模式,使用 `Match` 方法便可:
如果是简单模式,使用 `Match()` 方法便可:

```go
ok, _ := regexp.Match(pat, []byte(searchIn))
```

变量 ok 将返回 true 或者 false,我们也可以使用 `MatchString`
变量 `ok` 将返回 `true` 或者 `false`,我们也可以使用 `MatchString()`

```go
ok, _ := regexp.MatchString(pat, searchIn)
```

更多方法中,必须先将正则模式通过 `Compile` 方法返回一个 Regexp 对象。然后我们将掌握一些匹配,查找,替换相关的功能。
更多方法中,必须先将正则模式通过 `Compile()` 方法返回一个 `Regexp` 对象。然后我们将掌握一些匹配,查找,替换相关的功能。

示例 9.2 [pattern.go](examples/chapter_9/pattern.go)

@@ -59,7 +59,7 @@ func main() {
John: ##.# William: ##.# Steve: ##.#
John: 5156.68 William: 9134.46 Steve: 11264.36

`Compile` 函数也可能返回一个错误,我们在使用时忽略对错误的判断是因为我们确信自己正则表达式是有效的。当用户输入或从数据中获取正则表达式的时候,我们有必要去检验它的正确性。另外我们也可以使用 `MustCompile` 方法,它可以像 `Compile` 方法一样检验正则的有效性,但是当正则不合法时程序将 panic(详情查看第 13.2 节)
`Compile()` 函数也可能返回一个错误,我们在使用时忽略对错误的判断是因为我们确信自己正则表达式是有效的。当用户输入或从数据中获取正则表达式的时候,我们有必要去检验它的正确性。另外我们也可以使用 `MustCompile()` 方法,它可以像 `Compile()` 方法一样检验正则的有效性,但是当正则不合法时程序将 `panic()`(详情查看[ 13.2 节](13.2.md)

## 链接

12 changes: 6 additions & 6 deletions eBook/09.3.md
Original file line number Diff line number Diff line change
@@ -4,13 +4,13 @@

经典的做法是一次只能让一个线程对共享变量进行操作。当变量被一个线程改变时(临界区),我们为它上锁,直到这个线程执行完成并解锁后,其他线程才能访问它。

特别是我们之前章节学习的 map 类型是不存在锁的机制来实现这种效果(出于对性能的考虑),所以 map 类型是非线程安全的。当并行访问一个共享的 map 类型的数据,map 数据将会出错。
特别是我们之前章节学习的 `map` 类型是不存在锁的机制来实现这种效果(出于对性能的考虑),所以 map 类型是非线程安全的。当并行访问一个共享的 `map` 类型的数据,`map` 数据将会出错。

在 Go 语言中这种锁的机制是通过 sync 包中 Mutex 来实现的。sync 来源于 "synchronized" 一词,这意味着线程将有序的对同一变量进行访问。
在 Go 语言中这种锁的机制是通过 `sync` 包中 `Mutex` 来实现的。sync 来源于 "synchronized" 一词,这意味着线程将有序的对同一变量进行访问。

`sync.Mutex` 是一个互斥锁,它的作用是守护在临界区入口来确保同一时间只能有一个线程进入临界区。

假设 info 是一个需要上锁的放在共享内存中的变量。通过包含 `Mutex` 来实现的一个典型例子如下:
假设 `info` 是一个需要上锁的放在共享内存中的变量。通过包含 `Mutex` 来实现的一个典型例子如下:

```go
import "sync"
@@ -33,7 +33,7 @@ func Update(info *Info) {
}
```

还有一个很有用的例子是通过 Mutex 来实现一个可以上锁的共享缓冲器:
还有一个很有用的例子是通过 `Mutex` 来实现一个可以上锁的共享缓冲器:

```go
type SyncedBuffer struct {
@@ -42,9 +42,9 @@ type SyncedBuffer struct {
}
```

在 sync 包中还有一个 `RWMutex` 锁:它能通过 `RLock()` 来允许同一时间多个线程对变量进行读操作,但是只能一个线程进行写操作。如果使用 `Lock()` 将和普通的 `Mutex` 作用相同。包中还有一个方便的 `Once` 类型变量的方法 `once.Do(call)`,这个方法确保被调用函数只能被调用一次。
`sync` 包中还有一个 `RWMutex` 锁:它能通过 `RLock()` 来允许同一时间多个线程对变量进行读操作,但是只能一个线程进行写操作。如果使用 `Lock()` 将和普通的 `Mutex` 作用相同。包中还有一个方便的 `Once` 类型变量的方法 `once.Do(call)`,这个方法确保被调用函数只能被调用一次。

相对简单的情况下,通过使用 sync 包可以解决同一时间只能一个线程访问变量或 map 类型数据的问题。如果这种方式导致程序明显变慢或者引起其他问题,我们要重新思考来通过 goroutines 和 channels 来解决问题,这是在 Go 语言中所提倡用来实现并发的技术。我们将在第 14 章对其深入了解,并在第 14.7 节中对这两种方式进行比较
相对简单的情况下,通过使用 `sync` 包可以解决同一时间只能一个线程访问变量或 `map` 类型数据的问题。如果这种方式导致程序明显变慢或者引起其他问题,我们要重新思考来通过 goroutines 和 channels 来解决问题,这是在 Go 语言中所提倡用来实现并发的技术。我们将在[ 14 ](14.0.md)对其深入了解,并在[ 14.7 ](14.7.md)中对这两种方式进行比较

## 链接

6 changes: 3 additions & 3 deletions eBook/09.4.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# 9.4 精密计算和 big 包

我们知道有些时候通过编程的方式去进行计算是不精确的。如果你使用 Go 语言中的 float64 类型进行浮点运算,返回结果将精确到 15 位,足以满足大多数的任务。当对超出 int64 或者 uint64 类型这样的大数进行计算时,如果对精度没有要求,float32 或者 float64 可以胜任,但如果对精度有严格要求的时候,我们不能使用浮点数,在内存中它们只能被近似的表示。
我们知道有些时候通过编程的方式去进行计算是不精确的。如果你使用 Go 语言中的 `float64` 类型进行浮点运算,返回结果将精确到 15 位,足以满足大多数的任务。当对超出 `int64` 或者 `uint64` 类型这样的大数进行计算时,如果对精度没有要求,`float32` 或者 `float64` 可以胜任,但如果对精度有严格要求的时候,我们不能使用浮点数,在内存中它们只能被近似的表示。

对于整数的高精度计算 Go 语言中提供了 big 包,被包含在 math 包下:有用来表示大整数的 `big.Int` 和表示大有理数的 `big.Rat` 类型(可以表示为 2/5 或 3.1416 这样的分数,而不是无理数或 π)。这些类型可以实现任意位类型的数字,只要内存足够大。缺点是更大的内存和处理开销使它们使用起来要比内置的数字类型慢很多。
对于整数的高精度计算 Go 语言中提供了 `big` 包,被包含在 `math` 包下:有用来表示大整数的 `big.Int` 和表示大有理数的 `big.Rat` 类型(可以表示为 2/5 或 3.1416 这样的分数,而不是无理数或 π)。这些类型可以实现任意位类型的数字,只要内存足够大。缺点是更大的内存和处理开销使它们使用起来要比内置的数字类型慢很多。

大的整型数字是通过 `big.NewInt(n)` 来构造的,其中 n 为 int64 类型整数。而大有理数是通过 `big.NewRat(n, d)` 方法构造。n(分子)和 d(分母)都是 int64 型整数。因为 Go 语言不支持运算符重载,所以所有大数字类型都有像是 `Add()``Mul()` 这样的方法。它们作用于作为 receiver 的整数和有理数,大多数情况下它们修改 receiver 并以 receiver 作为返回结果。因为没有必要创建 `big.Int` 类型的临时变量来存放中间结果,所以运算可以被链式地调用,并节省内存。
大的整型数字是通过 `big.NewInt(n)` 来构造的,其中 `n``int64` 类型整数。而大有理数是通过 `big.NewRat(n, d)` 方法构造。`n`(分子)和 `d`(分母)都是 `int64` 型整数。因为 Go 语言不支持运算符重载,所以所有大数字类型都有像是 `Add()``Mul()` 这样的方法。它们作用于作为 receiver 的整数和有理数,大多数情况下它们修改 receiver 并以 receiver 作为返回结果。因为没有必要创建 `big.Int` 类型的临时变量来存放中间结果,所以运算可以被链式地调用,并节省内存。

示例 9.2 [big.go](examples/chapter_9/big.go)

46 changes: 26 additions & 20 deletions eBook/09.5.md
Original file line number Diff line number Diff line change
@@ -88,11 +88,12 @@ Import with `_` :

import _ "./pack1/pack1"

pack1 包只导入其副作用,也就是说,只执行它的 init 函数并初始化其中的全局变量。
`pack1` 包只导入其副作用,也就是说,只执行它的 `init()` 函数并初始化其中的全局变量。

**导入外部安装包:**

如果你要在你的应用中使用一个或多个外部包,首先你必须使用 `go install`(参见第 9.7 节)在你的本地机器上安装它们。
如果你要在你的应用中使用一个或多个外部包,首先你必须使用 `go install`(参见[第 9.7 节](09.7.md))在你的本地机器上安装它们。


假设你想使用 `http://codesite.ext/author/goExample/goex` 这种托管在 Google Code、GitHub 和 Launchpad 等代码网站上的包。

@@ -112,17 +113,19 @@ pack1 包只导入其副作用,也就是说,只执行它的 init 函数并

**包的初始化:**

程序的执行开始于导入包,初始化 main 包然后调用 main 函数。
程序的执行开始于导入包,初始化 `main` 包然后调用 `main()` 函数。

一个没有导入的包将通过分配初始值给所有的包级变量和调用源码中定义的包级 init 函数来初始化。一个包可能有多个 init 函数甚至在一个源码文件中。它们的执行是无序的。这是最好的例子来测定包的值是否只依赖于相同包下的其他值或者函数。
一个没有导入的包将通过分配初始值给所有的包级变量和调用源码中定义的包级 `init()` 函数来初始化。一个包可能有多个 `init()` 函数甚至在一个源码文件中。它们的执行是无序的。这是最好的例子来测定包的值是否只依赖于相同包下的其他值或者函数。

init 函数是不能被调用的。
`init()` 函数是不能被调用的。

导入的包在包自身初始化前被初始化,而一个包在程序执行中只能初始化一次。

**编译并安装一个包(参见第 9.7 节):**
**编译并安装一个包(参见[第 9.7 节](09.7.md)):**


在 Linux/OS X 下可以用类似[第 3.9 节](03.9.md)的 Makefile 脚本做到这一点:

在 Linux/OS X 下可以用类似第 3.9 节的 Makefile 脚本做到这一点:

include $(GOROOT)/src/Make.inc
TARG=pack1
@@ -131,39 +134,42 @@ init 函数是不能被调用的。
pack1b.go\
include $(GOROOT)/src/Make.pkg

通过 `chmod 777 ./Makefile`确保它的可执行性。
通过 `chmod 777 ./Makefile` 确保它的可执行性。

上面脚本内的 include 语句引入了相应的功能,将自动检测机器的架构并调用正确的编译器和链接器。
上面脚本内的 `include` 语句引入了相应的功能,将自动检测机器的架构并调用正确的编译器和链接器。

然后终端执行 make 或 `gomake` 工具:他们都会生成一个包含静态库 pack1.a 的 _obj 目录。
然后终端执行 `make``gomake` 工具:他们都会生成一个包含静态库 `pack1.a``_obj` 目录。

go install(参见第 9.7 节,从 Go1 的首选方式)同样复制 pack1.a 到本地的 $GOROOT/pkg 的目录中一个以操作系统为名的子目录下。像 `import "pack1"` 代替 `import "path to pack1"`,这样只通过名字就可以将包在程序中导入。
go install(参见[第 9.7 节](09.7.md),从 Go1 的首选方式)同样复制 `pack1.a` 到本地的 `$GOROOT/pkg` 的目录中一个以操作系统为名的子目录下。像 `import "pack1"` 代替 `import "path to pack1"`,这样只通过名字就可以将包在程序中导入。


[第 13 章](13.0.md) 我们遇到使用测试工具进行测试的时候我们将重新回到自己的包的制作和编译这个话题。

当第 13 章我们遇到使用测试工具进行测试的时候我们将重新回到自己的包的制作和编译这个话题。

**问题 9.1**

a)一个包能分成多个源文件么?

b)一个源文件是否能包含多个包?

**练习 9.3**
**练习 9.3** [main_greetings.go](exercises\chapter_9\main_greetings.go)

创建一个程序 main_greetings.go 能够和用户说 "Good Day" 或者 "Good Night"。不同的问候应该放到单独的 greetings 包中。
创建一个程序 main_greetings.go 能够和用户说 `"Good Day"` 或者 `"Good Night"`。不同的问候应该放到单独的 `greetings` 包中。

在同一个包中创建一个 `IsAM` 函数返回一个布尔值用来判断当前时间是 AM 还是 PM,同样创建 `IsAfternoon``IsEvening` 函数。

使用 main_greetings 作出合适的问候(提示:使用 time 包)。
使用 main_greetings 作出合适的问候(提示:使用 `time` 包)。

**练习 9.4** 创建一个程序 [main_oddven.go](exercises\chapter_9\main_oddeven.go) 判断前 100 个整数是不是偶数,将判断所用的函数编写在 `even` 包里。

**练习 9.4** 创建一个程序 main_oddven.go 判断前 100 个整数是不是偶数,将判断所用的函数编写在 even 包里。
**练习 9.5** 使用[第 6.6 节](06.6.md)的斐波那契程序:

**练习 9.5** 使用第 6.6 节的斐波那契程序:

1)将斐波那契功能放入自己的 fibo 包中并通过主程序调用它,存储最后输入的值在函数的全局变量。
1)将斐波那契功能放入自己的 `fibo` 包中并通过主程序调用它,存储最后输入的值在函数的全局变量。

2)扩展 fibo 包将通过调用斐波那契的时候,操作也作为一个参数。实验 "+" 和 *
2)扩展 `fibo` 包将通过调用斐波那契的时候,操作也作为一个参数。实验 `"+"``"*"`

main_fibo.go / fibonacci.go
[main_fibo.go](exercises\chapter_9\main_fibo.go) / [fibonacci.go](exercises/chapter_6/fibonacci.go)

## 链接

16 changes: 9 additions & 7 deletions eBook/09.6.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
# 9.6 为自定义包使用 godoc

godoc 工具(第 3.6 节)在显示自定义包中的注释也有很好的效果:注释必须以 `//` 开始并无空行放在声明(包,类型,函数)前。godoc 会为每个文件生成一系列的网页。
godoc 工具([第 3.6 节](03.6.md))在显示自定义包中的注释也有很好的效果:注释必须以 `//` 开始并无空行放在声明(包,类型,函数)前。godoc 会为每个文件生成一系列的网页。

例如:

-[doc_examples](examples/chapter_9/doc_example) 目录下我们有第 11.7 节中的用来排序的 go 文件,文件中有一些注释(文件需要未编译)
-[doc_examples](examples/chapter_9/doc_example) 目录下我们有[ 11.7 ](11.7.md)中的用来排序的 go 文件,文件中有一些注释(文件需要未编译)
- 命令行下进入目录下并输入命令:

godoc -http=:6060 -goroot="."
`godoc -http=:6060 -goroot="."`

`.` 是指当前目录,-goroot 参数可以是 `/path/to/my/package1` 这样的形式指出 package1 在你源码中的位置或接受用冒号形式分隔的路径,无根目录的路径为相对于当前目录的相对路径)
`.` 是指当前目录,`-goroot` 参数可以是 `/path/to/my/package1` 这样的形式指出 `package1` 在你源码中的位置或接受用冒号形式分隔的路径,无根目录的路径为相对于当前目录的相对路径)

- 在浏览器打开地址:http://localhost:6060

然后你会看到本地的 godoc 页面(详见第 3.6 节)从左到右一次显示出目录中的包:
然后你会看到本地的 godoc 页面(详见[ 3.6 节](03.6.md))从左到右一次显示出目录中的包:

```
doc_example:
doc_example | Packages | Commands | Specification
```

下面是链接到源码和所有对象时有序概述(所以是很好的浏览和查找源代码的方式),连同文件/注释:

sort 包
`sort`

```go
func Float64sAreSorted
@@ -83,7 +85,7 @@ Convenience wrappers for common cases: type IntArray[Top]
Convenience types for common cases: IntArray type IntArray []int
```

如果你在一个团队中工作,并且源代码树被存储在网络硬盘上,就可以使用 godoc 给所有团队成员连续文档的支持。通过设置 `sync_minutes=n`,你甚至可以让它每 n 分钟自动更新您的文档!
如果你在一个团队中工作,并且源代码树被存储在网络硬盘上,就可以使用 godoc 给所有团队成员连续文档的支持。通过设置 `sync_minutes=n`,你甚至可以让它每 `n` 分钟自动更新您的文档!

## 链接

10 changes: 5 additions & 5 deletions eBook/09.7.md
Original file line number Diff line number Diff line change
@@ -4,11 +4,11 @@ go install 是 Go 中自动包安装工具:如需要将包安装到本地它

在包安装前的先决条件是要自动处理包自身依赖关系的安装。被依赖的包也会安装到子目录下,但是没有文档和示例:可以到网上浏览。

go install 使用了 GOPATH 变量(详见第 2.2 节)。
go install 使用了 GOPATH 变量(详见[ 2.2 节](02.2.md))。

远端包(详见第 9.5 节):
远端包(详见[ 9.5 节](09.5.md)):

假设我们要安装一个有趣的包 tideland(它包含了许多帮助示例,参见 [项目主页](http://code.google.com/p/tideland-cgl))。
假设我们要安装一个有趣的包 `tideland`(它包含了许多帮助示例,参见[项目主页](http://code.google.com/p/tideland-cgl))。

因为我们需要创建目录在 Go 安装目录下,所以我们需要使用 root 或者 su 的身份执行命令。

@@ -28,11 +28,11 @@ import cgl "tideland-cgl.googlecode.com/hg"

升级到新的版本:

更新到新版本的 Go 之后本地安装包的二进制文件将全被删除。如果你想更新,重编译、重安装所有的go安装包可以使用`go install -a`
更新到新版本的 Go 之后本地安装包的二进制文件将全被删除。如果你想更新,重编译、重安装所有的 go 安装包可以使用`go install -a`

go 的版本发布的很频繁,所以需要注意发布版本和包的兼容性。go1 之后都是自己编译自己了。

go install 同样可以使用 go install 编译链接并安装本地自己的包(详见第 9.8.2 节)。
go install 同样可以使用 go install 编译链接并安装本地自己的包(详见[ 9.8.2 节](09.8.md))。

更多信息可以在 [官方网站](http://golang.org/cmd/go/) 找到。

16 changes: 8 additions & 8 deletions eBook/09.8.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# 9.8 自定义包的目录结构、go install 和 go test

为了示范,我们创建了一个名为 uc 的简单包,它含有一个 `UpperCase` 函数将字符串的所有字母转换为大写。当然这并不值得创建一个自定义包,同样的功能已被包含在 `strings` 包里,但是同样的技巧也可以应用在更复杂的包中。
为了示范,我们创建了一个名为 `uc` 的简单包,它含有一个 `UpperCase` 函数将字符串的所有字母转换为大写。当然这并不值得创建一个自定义包,同样的功能已被包含在 `strings` 包里,但是同样的技巧也可以应用在更复杂的包中。

## 9.8.1 自定义包的目录结构

下面的结构给了你一个好的示范(uc 代表通用包名, 名字为粗体的代表目录,斜体代表可执行文件):
下面的结构给了你一个好的示范(`uc` 代表通用包名, 名字为粗体的代表目录,斜体代表可执行文件):

/home/user/goprograms
ucmain.go (uc 包主程序)
@@ -24,7 +24,7 @@
pkg/linux_amd64
uc.a (包的目标文件)

将你的项目放在 goprograms 目录下(你可以创建一个环境变量 GOPATH,详见第 2.2/3 章节:在 .profile 和 .bashrc 文件中添加 `export GOPATH=/home/user/goprograms`),而你的项目将作为 src 的子目录。uc 包中的功能在 uc.go 中实现。
将你的项目放在 goprograms 目录下(你可以创建一个环境变量 `GOPATH`,详见第 [2.2](02.2.md)/[3](02.3.md) 章节:在 `.profile``.bashrc` 文件中添加 `export GOPATH=/home/user/goprograms`),而你的项目将作为 `src` 的子目录。`uc` 包中的功能在 uc.go 中实现。

示例 9.6 [uc.go](examples/chapter_9/uc.go)

@@ -37,7 +37,7 @@ func UpperCase(str string) string {
}
```

包通常附带一个或多个测试文件,在这我们创建了一个 uc_test.go 文件,如第 9.8 节所述
包通常附带一个或多个测试文件,在这我们创建了一个 uc_test.go 文件,[ 9.8 ](09.8.md)所述

示例 9.7 [test.go](examples/chapter_9/test.go)

@@ -86,11 +86,11 @@ include $(GOROOT)/src/Make.pkg

这个包可以通过 go test 测试。

创建一个 uc.a 的测试文件在目录下,输出为 PASS 时测试通过。
创建一个 uc.a 的测试文件在目录下,输出为 `PASS` 时测试通过。

在第 13.8 节我们将给出另外一个测试例子并进行深入研究
[ 13.8 ](13.8.md)我们将给出另外一个测试例子并进行深入研究

备注:有可能你当前的用户不具有足够的资格使用 go install(没有权限)。这种情况下,选择 root 用户 su。确保 Go 环境变量和 Go 源码路径也设置给 su,同样也适用你的普通用户(详见第 2.3 节)。
备注:有可能你当前的用户不具有足够的资格使用 go install(没有权限)。这种情况下,选择 root 用户 su。确保 Go 环境变量和 Go 源码路径也设置给 su,同样也适用你的普通用户(详见[ 2.3 节](02.3.md))。

接下来我们创建主程序 ucmain.go:

@@ -141,7 +141,7 @@ include $(GOROOT)/src/Make.cmd
如果我们想安装的包在系统上的其他 Go 程序中被使用,它一定要安装到 `$GOPATH` 下。
这样做,在 .profile 和 .bashrc 中设置 `export GOPATH=/home/user/goprograms`

然后执行 go install uc 将会复制包存档到 `$GOPATH/pkg/LINUX_AMD64/uc`
然后执行 `go install uc` 将会复制包存档到 `$GOPATH/pkg/LINUX_AMD64/uc`

现在,uc 包可以通过 `import "uc"` 在任何 Go 程序中被引用。

6 changes: 3 additions & 3 deletions eBook/09.9.md
Original file line number Diff line number Diff line change
@@ -6,9 +6,9 @@

在 Linux 和 OS X 的机器上 Git 是默认安装的,在 Windows 上你必须先自行安装,参见 [GitHub 帮助页面](http://help.github.com/win-set-up-git/)

这里将通过为第 9.8 节中的 uc 包创建一个 git 仓库作为演示
这里将通过为[ 9.8 ](09.8.md)中的 `uc` 包创建一个 git 仓库作为演示

进入到 uc 包目录下并创建一个 Git 仓库在里面: `git init`
进入到 `uc` 包目录下并创建一个 Git 仓库在里面: `git init`

信息提示: `Initialized empty git repository in $PWD/uc`

@@ -32,7 +32,7 @@ git push -u origin master

## 9.9.2 从 GitHub 安装

如果有人想安装您的远端项目到本地机器,打开终端并执行(NNNN 是你在 GitHub 上的用户名):`go get github.com/NNNN/uc`
如果有人想安装您的远端项目到本地机器,打开终端并执行(`NNNN` 是你在 GitHub 上的用户名):`go get github.com/NNNN/uc`

这样现在这台机器上的其他 Go 应用程序也可以通过导入路径:`"github.com/NNNN/uc"` 代替 `"./uc/uc"` 来使用。

0 comments on commit 471d59a

Please sign in to comment.