Hello, 世界

欢迎来到Go 编程语言指南.

本指南分为三节。在每节的末尾有若干个练习需要读者完成。

本指南可以进行交互。点击“Run”按钮(或按 Shift + Enter)可以在 远程服务器上 你的电脑上编译并执行程序。 结果展示在代码的下面。

这些例子程序展示了 Go 的各个方面。在指南中的程序可以成为您积累经验的开始。

编辑程序并且再次执行它。

每当你准备好继续,点击“Next”按钮或按 PageDown 键。

(译者:本文保留英文原文在下方。)


Hello, 世界

Welcome to a tour of the Go programming language.

The tour is divided into three sections. At the end of each section is a series of exercises for you to complete.

The tour is interactive. Click the Run button now (or type Shift-Enter) to compile and run the program on a remote server. your computer. The result is displayed below the code.

These example programs demonstrate different aspects of Go. The programs in the tour are meant to be starting points for your own experimentation.

Edit the program and run it again.

Whenever you're ready to move on, click the Next button or type the PageDown key.

package main

import "fmt"

func main() {
	fmt.Println("Hello, 世界")
}

Go 本地语言

这个教程有其他语言:

(如果你想将这个指南翻译成其他语言,请从https://code.google.com/p/go-tour 检出源码,翻译static/index.html, 然后发布到App Engine, 使用 appengine/README进行介绍.)

点击“Next”按钮或按 PageDown 键继续。


Go local

The tour is available in other languages:

(If you would like to translate the tour to another language, check out the source from https://code.google.com/p/go-tour, translate static/index.html, and then deploy it to App Engine using the instructions in appengine/README.)

Click the "next" button or type PageDown to continue.

离线 Go 指南

在没有互联网接入的情况下该指南可以作为独立的程序使用。

指南独立运行会在本地的设备上构建和编译代码,这运行得更快。并且会包括一些在沙盒版本中没有的体验上的增强。

为了在本地运行指南首先要安装 Go, 然后使用 go get 安装 gotour:(译者:安装前请检查系统是否安装Mercurial)

    go get code.google.com/p/go-tour/gotour

也可以安装中文版:(译者:安装前请检查系统是否安装Mercurial)

    go get bitbucket.org/mikespook/go-tour-zh/gotour

也可以安装本版 “附带中文翻译版”:(译者:安装前请检查系统是否安装Git,本版翻译有些许不同,属于个人理解。本版本翻译目的为了学习go语言,感谢mikespook的翻译。)

    go get github.com/zhanming/go-tour-cn/gotour.cn

最后执行安装产生的 gotour 执行文件。(译者:本安装修改了可执行文件名为"gotour.cn",以便与英文版区分)

如果不安装本地版本,点击“Next”按钮或者按 PageDown 继续。

(可以在任何时候点击“目录”按钮返回这个介绍。)


Go offline

This tour is also available as a stand-alone program that you can use without access to the internet.

The stand-alone tour is faster, as it builds and runs the code samples on your own machine. It also includes additional exercises not available in this sandboxed version.

To run the tour locally first install Go, then use go get to install gotour:

go get code.google.com/p/go-tour/gotour

and run the resultant gotour executable.

Otherwise, click the "next" button or type PageDown to continue.

(You may return to these instructions at any time by clicking the "index" button.)

介绍

每个 Go 程序都是由包组成的。

程序运行的入口从包的 main方法。

这个程序使用并导入了包 "fmt""math"

按惯例,包名与导入路径的最后一个目录一致。


Packages

Every Go program is made up of packages.

Programs start running in package main.

This program is using the packages with import paths "fmt" and "math".

By convention, the package name is the same as the last element of the import path.

package main

import (
	"fmt"
	"math"
)

func main() {
	fmt.Println("Happy", math.Pi, "Day")
}

导入

这个代码用圆括号组合了导入,这是“factored”式导入声明。同样可以编写多个导入语句,例如:

        import "fmt"
        import "math"
        
不过通常都会用 factored 格式来使代码工整。

Imports

This code groups the imports into a parenthesized, "factored" import statement. You can also write multiple import statements, like:

import "fmt"
import "math"
but it's common to use the factored form to eliminate clutter.
package main

import (
	"fmt"
	"math"
)

func main() {
	fmt.Printf("Now you have %g problems.",
		math.Nextafter(2, 3))
}

导出名

在导入了一个包之后,就可以用其导出的名称来调用它。

在 Go 中,首字母大写的名称是被导出的。

FooFOO 都是被导出的名称。 名称 foo 是不会被导出的。

执行代码。然后将 math.pi 改为 math.Pi 再试着执行一下。


Exported names

After importing a package, you can refer to the names it exports.

In Go, a name is exported if it begins with a capital letter.

Foo is an exported name, as is FOO. The name foo is not exported.

Run the code. Then rename math.pi to math.Pi and try it again.

package main

import (
	"fmt"
	"math"
)

func main() {
	fmt.Println(math.pi)
}

函数

函数可以有零个或多个参数。

在这个例子中,add 有两个 int 类型的参数。

注意类型声明在变量名之后

(参考这篇关于 Go 语法定义的文章了解类型以这种形式出现的原因。)


Functions

A function can take zero or more arguments.

In this example, add takes two parameters of type int.

Notice that the type comes after the variable name.

(For more about why types look the way they do, see the article on Go's declaration syntax.)

package main

import "fmt"

func add(x int, y int) int {
	return x + y
}

func main() {
	fmt.Println(add(42, 13))
}

函数

当两个或多个连续的函数命名参数是同一类型,则除了最后一个参数需类型声明之外,其他都可以省略。

在这个例子中 ,我们将

x int, y int

缩写为

x, y int

Functions

When two or more consecutive named function parameters share a type, you can omit the type from all but the last.

In this example, we shortened

x int, y int

to

x, y int
package main

import "fmt"

func add(x, y int) int {
	return x + y
}

func main() {
	fmt.Println(add(42, 13))
}

函数

函数可以返回任意数量的返回值。

这个函数返回了两个字符串。


Functions

A function can return any number of results.

This function returns two strings.

package main

import "fmt"

func swap(x, y string) (string, string) {
	return y, x
}

func main() {
	a, b := swap("hello", "world")
	fmt.Println(a, b)
}

函数

函数有参数。在 Go 中,函数可以返回多个“结果参数”,而不仅仅是一个值。它们可以像变量那样被命名和使用。

如果命名了返回值的参数,一个没有参数的 return 语句,会将当前的值作为返回值返回。


Functions

Functions take parameters. In Go, functions can return multiple "result parameters", not just a single value. They can be named and act just like variables.

If the result parameters are named, a return statement without arguments returns the current values of the results.

package main

import "fmt"

func split(sum int) (x, y int) {
	x = sum * 4/9
	y = sum - x
	return
}

func main() {
	fmt.Println(split(17))
}

变量

var 语句声明了一个变量的列表;跟函数的参数列表一样,类型声明在最后面。


Variables

The var statement declares a list of variables; as in function argument lists, the type is last.

package main

import "fmt"

var x, y, z int
var c, python, java bool

func main() {
	fmt.Println(x, y, z, c, python, java)
}

变量

变量声明时可以包含初始值,每个变量对应一个。

如果初始值是存在的,则可以省略类型声明;变量将从初始值中获得类型。


Variables

A var declaration can include initializers, one per variable.

If an initializer is present, the type can be omitted; the variable will take the type of the initializer.

package main

import "fmt"

var x, y, z int = 1, 2, 3
var c, python, java = true, false, "no!"

func main() {
	fmt.Println(x, y, z, c, python, java)
}

变量

在一个函数里面,短赋值语句:= 可以用于替代 var 的隐式类型声明。

:= 结构不能使用在函数外,函数外的每个语法块都必须以关键字开始。)


Variables

Inside a function, the := short assignment statement can be used in place of a var declaration with implicit type.

(Outside a function, every construct begins with a keyword and the := construct is not available.)

package main

import "fmt"

func main() {
	var x, y, z int = 1, 2, 3
	c, python, java := true, false, "no!"

	fmt.Println(x, y, z, c, python, java)
}

常量

常量的声明与变量类似,只不过使用 const 关键字。

常量可以是字符、字符串、布尔或数值类型的值。


Constants

Constants are declared like variables, but with the const keyword.

Constants can be character, string, boolean, or numeric values.

package main

import "fmt"

const Pi = 3.14

func main() {
	const World = "世界"
	fmt.Println("Hello", World)
	fmt.Println("Happy", Pi, "Day")

	const Truth = true
	fmt.Println("Go rules?", Truth)
}

数值常量

数值常量是高精度的

一个未指定类型的常量由上下文来决定其类型。

也尝试一下输出 needInt(Big)吧。


Numeric Constants

Numeric constants are high-precision values.

An untyped constant takes the type needed by its context.

Try printing needInt(Big) too.

package main

import "fmt"

const (
	Big = 1<<100
	Small = Big>>99
)

func needInt(x int) int { return x*10 + 1 }
func needFloat(x float64) float64 {
	return x*0.1
}

func main() {
	fmt.Println(needInt(Small))
	fmt.Println(needFloat(Small))
	fmt.Println(needFloat(Big))
}

For

Go 只有一种循环结构,for 循环。

基本的 for 循环看起来跟 C 或者 Java 中做的一样,除了没有了 ( ) 之外(甚至强制不能使用它们),而 { } 是必须的。


For

Go has only one looping construct, the for loop.

The basic for loop looks as it does in C or Java, except that the ( ) are gone (they are not even optional) and the { } are required.

package main

import "fmt"

func main() {
	sum := 0
	for i := 0; i < 10; i++ {
		sum += i
	}
	fmt.Println(sum)
}

For

跟 C 或者 Java 中一样,前置、后置条件可以为空。


For

As in C or Java, you can leave the pre and post statements empty.

package main

import "fmt"

func main() {
	sum := 1
	for ; sum < 1000; {
		sum += sum
	}
	fmt.Println(sum)
}

For

基于这一点,你也可以省略分号: C 的 while 循环在 Go 中也是用 for 实现。


For

At that point you can drop the semicolons: C's while is spelled for in Go.

package main

import "fmt"

func main() {
	sum := 1
	for sum < 1000 {
		sum += sum
	}
	fmt.Println(sum)
}

For

如果省略了循环条件,它会一直循环下去(译者:死循环或无限循环)。


For

If you omit the loop condition, it loops forever.

package main

func main() {
	for ; ; {
	}
}

For

为了避免累赘,分号也可以省略,这样一个无限循环可以被简洁地表达。


For

And with no clauses at all, the semicolons can be omitted, so an infinite loop is compactly expressed.

package main

func main() {
	for {
	}
}

If

if 语句看起来跟 C 或者 Java 中的一样,除了没有了 ( ) 之外(甚至强制不能使用它们),而 { } 是必须的。

(耳熟吗?)


If

The if statement looks as it does in C or Java, except that the ( ) are gone (they are not even optional) and the { } are required.

(Sound familiar?)

package main

import (
	"fmt"
	"math"
)

func sqrt(x float64) string {
	if x < 0 {
		return sqrt(-x) + "i"
	}
	return fmt.Sprint(math.Sqrt(x))
}

func main() {
	fmt.Println(sqrt(2), sqrt(-4))
}

If

for 一样,if 语句可以在条件之前执行一个简短的声明。

由这个语句定义的变量的作用域仅在 if 范围之内。

(在最后的 return 语句处使用 v 看看。)


If

Like for, the if statement can start with a short statement to execute before the condition.

Variables declared by the statement are only in scope until the end of the if.

(Try using v in the last return statement.)

package main

import (
	"fmt"
	"math"
)

func pow(x, n, lim float64) float64 {
	if v := math.Pow(x, n); v < lim {
		return v
	}
	return lim
}

func main() {
	fmt.Println(
		pow(3, 2, 10),
		pow(3, 3, 20),
	)
}

If

if 的简短声明处定义的变量同样可以在对应的 else 块中使用。


If

Variables declared inside an if's short statement are also available inside any of the else blocks.

package main

import (
	"fmt"
	"math"
)

func pow(x, n, lim float64) float64 {
	if v := math.Pow(x, n); v < lim {
		return v
	} else {
		fmt.Printf("%g >= %g\n", v, lim)
	}
	// can't use v here, though
	return lim
}

func main() {
	fmt.Println(
		pow(3, 2, 10),
		pow(3, 3, 20),
	)
}

基本类型

Go 的基本类型有

bool

string

int  int8  int16  int32  int64
uint uint8 uint16 uint32 uint64 uintptr

byte // uint8 的别名

rune // int32 的别名
     // 代表一个Unicode码位

float32 float64

complex64 complex128
        

Basic types

Go's basic types are

bool

string

int  int8  int16  int32  int64
uint uint8 uint16 uint32 uint64 uintptr

byte // alias for uint8

rune // alias for int32
     // represents a Unicode code point

float32 float64

complex64 complex128
package main

import (
	"math/cmplx"
	"fmt"
)

var (
	ToBe bool = false
	MaxInt uint64 = 1<<64 - 1
	z complex128 = cmplx.Sqrt(-5+12i)
)

func main() {
	const f = "%T(%v)\n"
	fmt.Printf(f, ToBe, ToBe)
	fmt.Printf(f, MaxInt, MaxInt)
	fmt.Printf(f, z, z)
}

结构体

一个结构体(struct)就是一个成员变量的集合。

(而 type 定义跟其字面意思相符。)


Structs

A struct is a collection of fields.

(And a type declaration does what you'd expect.)

package main

import "fmt"

type Vertex struct {
	X int
	Y int
}

func main() {
	fmt.Println(Vertex{1, 2})
}

结构体成员变量

Struct Fields(结构体成员变量)使用点号来访问。


Struct Fields

Struct fields are accessed using a dot.

package main

import "fmt"

type Vertex struct {
	X int
	Y int
}

func main() {
	v := Vertex{1, 2}
	v.X = 4
	fmt.Println(v.X)
}

指针

Go 有指针,但是没有指针运算。

结构体成员变量可以通过结构体指针来访问。通过指针的间接访问也是透明的。


Pointers

Go has pointers, but no pointer arithmetic.

Struct fields can be accessed through a struct pointer. The indirection through the pointer is transparent.

package main

import "fmt"

type Vertex struct {
	X int
	Y int
}

func main() {
	p := Vertex{1, 2}
	q := &p
	q.X = 1e9
	fmt.Println(p)
}

Struct Literals

Struct Literals(结构体文法)表示通过结构体成员变量的值作为列表来新分配一个结构体。

使用 Name: 语法可以仅列出部分字段。(字段名的顺序无关。)

特殊的前缀 & 构造了指向结构体文法的指针。


Struct Literals

A struct literal denotes a newly allocated struct value by listing the values of its fields.

You can list just a subset of fields by using the Name: syntax. (And the order of named fields is irrelevant.)

The special prefix & constructs a pointer to a struct literal.

package main

import "fmt"

type Vertex struct {
	X, Y int
}

var (
	p = Vertex{1, 2}  // has type Vertex
	q = &Vertex{1, 2} // has type *Vertex
	r = Vertex{X: 1}  // Y:0 is implicit
	s = Vertex{}      // X:0 and Y:0
)

func main() {
	fmt.Println(p, q, r, s)
}

new 函数

表达式 new(T) 分配了一个零初始化的 T 值,并返回指向它的指针。

var t *T = new(T)

t := new(T)

The new function

The expression new(T) allocates a zeroed T value and returns a pointer to it.

var t *T = new(T)

or

t := new(T)
package main

import "fmt"

type Vertex struct {
	X, Y int
}

func main() {
	v := new(Vertex)
	fmt.Println(v)
	v.X, v.Y = 11, 9
	fmt.Println(v)
}

Map

map 映射键到值。

map 必须用 make 来创建(不是 new);一个值为 nil 的 map 是空的,并且不能赋值。


Maps

A map maps keys to values.

Maps must be created with make (not new) before use; the nil map is empty and cannot be assigned to.

package main

import "fmt"

type Vertex struct {
	Lat, Long float64
}

var m map[string]Vertex

func main() {
	m = make(map[string]Vertex)
	m["Bell Labs"] = Vertex{
		40.68433, 74.39967,
	}
	fmt.Println(m["Bell Labs"])
}

Map

map literals(map 的文法)跟struct literals(结构体文法)相似,但是键是必须的。


Maps

Map literals are like struct literals, but the keys are required.

package main

import "fmt"

type Vertex struct {
	Lat, Long float64
}

var m = map[string]Vertex{
	"Bell Labs": Vertex{
		40.68433, -74.39967,
	},
	"Google": Vertex{
		37.42202, -122.08408,
	},
}

func main() {
	fmt.Println(m)
}

Map

如果顶层类型只有类型名的话,可以在文法的元素中省略键名。


Maps

If the top-level type is just a type name, you can omit it from the elements of the literal.

package main

import "fmt"

type Vertex struct {
	Lat, Long float64
}

var m = map[string]Vertex{
	"Bell Labs": {40.68433, -74.39967},
	"Google":    {37.42202, -122.08408},
}

func main() {
	fmt.Println(m)
}

Mutating Maps

Insert or update an element in map m:

m[key] = elem

Retrieve an element:

elem = m[key]

Delete an element:

delete(m, key)

Test that a key is present with a two-value assignment:

elem, ok = m[key]

If key is in m, ok is true. If not, ok is false and elem is the zero value for the map's element type.

Similarly, when reading from a map if the key is not present the result is the zero value for the map's element type.

package main

import "fmt"

func main() {
	m := make(map[string]int)

	m["Answer"] = 42
	fmt.Println("The value:", m["Answer"])

	m["Answer"] = 48
	fmt.Println("The value:", m["Answer"])

	delete(m, "Answer")
	fmt.Println("The value:", m["Answer"])

	v, ok := m["Answer"]
	fmt.Println("The value:", v, "Present?", ok)
}

Slice

slice 指向数组的值,并且同时包含了长度信息。

[]T 是一个元素类型为 T 的 slice。


Slices

A slice points to an array of values and also includes a length.

[]T is a slice with elements of type T.

package main

import "fmt"

func main() {
	p := []int{2, 3, 5, 7, 11, 13}
	fmt.Println("p ==", p)

	for i := 0; i < len(p); i++ {
		fmt.Printf("p[%d] == %d\n",
			i, p[i])
	}
}

Slice

slice 可以重新切片,创建一个新的 slice 值指向相同的数组。

表达式

s[lo:hi]

表示从 lohi-1 的 slice 元素,含有两端。 因此

s[lo:lo]

是空的,而

s[lo:lo+1]

有一个元素。


Slices

Slices can be re-sliced, creating a new slice value that points to the same array.

The expression

s[lo:hi]

evaluates to a slice of the elements from lo through hi-1, inclusive. Thus

s[lo:lo]

is empty and

s[lo:lo+1]

has one element.

package main

import "fmt"

func main() {
	p := []int{2, 3, 5, 7, 11, 13}
	fmt.Println("p ==", p)
	fmt.Println("p[1:4] ==", p[1:4])

	// missing low index implies 0
	fmt.Println("p[:3] ==", p[:3])

	// missing high index implies len(s)
	fmt.Println("p[4:] ==", p[4:])
}

Slice

slice 由函数 make 创建。这会分配一个零长度的数组并且返回一个 slice 指向这个数组:

a := make([]int, 5)  // len(a)=5
        
slice 有长度和容量。slice 的容量是底层数组可以增长的最大长度。

为了指定容量,可传递第三个参数到 make

b := make([]int, 0, 5)
// len(b)=0, cap(b)=5
        
slice 可以通过“重新切片”来扩容(增加容量):

b = b[:cap(b)] // len(b)=5, cap(b)=5
b = b[1:]      // len(b)=4, cap(b)=4
        

Slices

Slices are created with the make function. It works by allocating a zeroed array and returning a slice that refers to that array:

a := make([]int, 5)  // len(a)=5
Slices have length and capacity. A slice's capacity is the maximum length the slice can grow within the underlying array.

To specify a capacity, pass a third argument to make:

b := make([]int, 0, 5) // len(b)=0, cap(b)=5
Slices can be grown by "re-slicing" (up to their capacity):

b = b[:cap(b)] // len(b)=5, cap(b)=5
b = b[1:]      // len(b)=4, cap(b)=4
package main

import "fmt"

func main() {
	a := make([]int, 5)
	printSlice("a", a)
	b := make([]int, 0, 5)
	printSlice("b", b)
	c := b[:2]
	printSlice("c", c)
	d := c[2:5]
	printSlice("d", d)
}

func printSlice(s string, x []int) {
	fmt.Printf("%s len=%d cap=%d %v\n",
		s, len(x), cap(x), x)
}

Slice

含有零个值的 slice 是 nil

一个 nil 的 slice 的长度和容量是 0。

(了解更多关于 slice 的内容,参阅文章 “Slices:使用和内幕”。)


Slices

The zero value of a slice is nil.

A nil slice has a length and capacity of 0.

(To learn more about slices, read the "Slices: usage and internals" article.)

package main

import "fmt"

func main() {
	var z []int
	fmt.Println(z, len(z), cap(z))
	if z == nil {
		fmt.Println("nil!")
	}
}

函数

函数也是值。


Functions

Functions are values too.

package main

import (
	"fmt"
	"math"
)

func main() {
	hypot := func(x, y float64) float64 {
		return math.Sqrt(x*x + y*y)
	}

	fmt.Println(hypot(3, 4))
}

函数

并且函数是完全闭包的。

函数 adder 返回一个闭包。每个闭包被绑定到自己的 sum 变量上。


Functions

And functions are full closures.

The adder function returns a closure. Each closure is bound to its own sum variable.

package main

import "fmt"

func adder() func(int) int {
	sum := 0
	return func(x int) int {
		sum += x
		return sum
	}
}

func main() {
	pos, neg := adder(), adder()
	for i := 0; i < 10; i++ {
		fmt.Println(
			pos(i),
			neg(-2*i),
		)
	}
}

Range

for 循环的 range 格式可以对 slice 或者 map 进行迭代循环。


Range

The range form of the for loop iterates over a slice or map.

package main

import "fmt"

var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}

func main() {
	for i, v := range pow {
	    fmt.Printf("2**%d = %d\n", i, v)
	}
}

Range

可以将值赋值给 _ 来忽略键和值。

如果只需要索引值,去掉“, value”的部分即可。


Range

You can skip the index or value by assigning to _.

If you only want the index, drop the “, value” entirely.

package main

import "fmt"

func main() {
	pow := make([]int, 10)
	for i := range pow {
		pow[i] = 1<<uint(i)
	}
	for _, value := range pow {
		fmt.Printf("%d\n", value)
	}
}

Switch

你可能已经猜到 switch 的形式了。

case 语句匹配后会自动终止,除非用 fallthrough 语句作为结尾。


Switch

You probably knew what switch was going to look like.

A case body breaks automatically, unless it ends with a fallthrough statement.

package main

import (
	"fmt"
	"runtime"
)

func main() {
	fmt.Print("Go runs on ")
	switch os := runtime.GOOS; os {
	case "darwin":
		fmt.Println("OS X.")
	case "linux":
		fmt.Println("Linux.")
	default:
		// freebsd, openbsd,
		// plan9, windows...
		fmt.Printf("%s.", os)
	}
}

Switch

switch 的条件从上到下的执行,当匹配成功的时候停止。

(例如,

switch i {
case 0:
case f():
}

i==0 时不会调用 f。)


Switch

Switch cases evaluate cases from top to bottom, stopping when a case succeeds.

(For example,

switch i {
case 0:
case f():
}

does not call f if i==0.)

package main

import (
	"fmt"
	"time"
)

func main() {
	fmt.Println("When's Saturday?")
	today := time.Now().Weekday()
	switch time.Saturday {
	case today+0:
		fmt.Println("Today.")
	case today+1:
		fmt.Println("Tomorrow.")
	case today+2:
		fmt.Println("In two days.")
	default:
		fmt.Println("Too far away.")
	}
}

Switch

没有条件的 switch 与 switch true 一样。

这一构造使得可以用更清晰的形式来编写if-then-else。


Switch

Switch without a condition is the same as switch true.

This construct can be a clean way to write long if-then-else chains.

package main

import (
	"fmt"
	"time"
)

func main() {
	t := time.Now()
	switch {
	case t.Hour() < 12:
	    fmt.Println("Good morning!")
	case t.Hour() < 17:
	    fmt.Println("Good afternoon.")
	default:
	    fmt.Println("Good evening.")
	}
}

练习:循环和函数

作为练习函数和循环,用牛顿法实现开方函数。

在这个例子中,牛顿法是通过选择一个初始点 z 然后重复这一过程求 Sqrt(x) 的近似值:

为了做到这个,只需要重复计算 10 次,并且观察不同的值(1,2,3,……)是如何逐步逼近结果的。

然后,修改循环条件,使得当值停止改变(或改变非常小)的时候退出循环。观察迭代次数是否变化。结果与 math.Sqrt 接近吗?

提示:定义并初始化一个浮点值,向其提供一个浮点语法或使用转换:

        z := float64(1)
        z := 1.0
        

Exercise: Loops and Functions

As a simple way to play with functions and loops, implement the square root function using Newton's method.

In this case, Newton's method is to approximate Sqrt(x) by picking a starting point z and then repeating:

Newton's method

To begin with, just repeat that calculation 10 times and see how close you get to the answer for various values (1, 2, 3, ...).

Next, change the loop condition to stop once the value has stopped changing (or only changes by a very small delta). See if that's more or fewer iterations. How close are you to the math.Sqrt?

Hint: to declare and initialize a floating point value, give it floating point syntax or use a conversion:

z := float64(1)
z := 1.0
package main

import (
	"fmt"
)

func Sqrt(x float64) float64 {
}

func main() {
	fmt.Println(Sqrt(2))
}

练习:Map

实现 WordCount。它应当返回一个含有 s 的每个 “word” 数量的 map。函数 wc.Test 针对这个函数执行一个测试用例,并打印成功或者失败。

你会发现 strings.Fields 很有帮助。


Exercise: Maps

Implement WordCount. It should return a map of the counts of each “word” in the string s. The wc.Test function runs a test suite against the provided function and prints success or failure.

You might find strings.Fields helpful.

package main

import (
	"tourcode.google.com/p/go-tour/wc"
)

func WordCount(s string) map[string]int {
	return map[string]int{"x": 1}
}

func main() {
	wc.Test(WordCount)
}

练习:Slice

实现 Pic。它应当接受一个 slice 的长度 dy,和 slice 中每个元素的长度的 8 位无符号整数 dx。当执行这个程序,它会将整数转换为灰度(好吧,蓝度)图片进行展示。

图片的实现已经完成。可能用到的函数包括 x^y(x+y)/2x*y

(需要使用循环来分配 [][]uint8 中的每个 []uint8。)

(使用 uint8(intValue) 在类型之间转换。)


Exercise: Slices

Implement Pic. It should return a slice of length dy, each element of which is a slice of dx 8-bit unsigned integers. When you run the program, it will display your picture, interpreting the integers as grayscale (well, bluescale) values.

The choice of image is up to you. Interesting functions include x^y, (x+y)/2, and x*y.

(You need to use a loop to allocate each []uint8 inside the [][]uint8.)

(Use uint8(intValue) to convert between types.)

package main

import "tourcode.google.com/p/go-tour/pic"

func Pic(dx, dy int) [][]uint8 {
}

func main() {
	pic.Show(Pic)
}

练习:斐波纳契闭包

让我们通过函数找些乐趣。

实现一个 fibonacci 函数,返回一个函数(一个闭包)可以返回连续的斐波纳契数。


Exercise: Fibonacci closure

Let's have some fun with functions.

Implement a fibonacci function that returns a function (a closure) that returns successive fibonacci numbers.

package main

import "fmt"

// fibonacci is a function that returns
// a function that returns an int.
func fibonacci() func() int {
}

func main() {
	f := fibonacci()
	for i := 0; i < 10; i++ {
		fmt.Println(f())
	}
}

进阶练习:复数立方根

让我们通过 complex64complex128 来探索一下 Go 内建的复数。 对于立方根,牛顿法需要大量循环:

找到 2 的立方根,确保算法能够工作。 这里有 cmath.Pow 函数。


Advanced Exercise: Complex cube roots

Let's explore Go's built-in support for complex numbers via the complex64 and complex128 types. For cube roots, Newton's method amounts to repeating:

Newton's method

Find the cube root of 2, just to make sure the algorithm works. There is a Pow function in the math/cmplx package.

package main

import "fmt"

func Cbrt(x complex128) complex128 {
}

func main() {
	fmt.Println(Cbrt(2))
}
Methods and Interfaces(方法和接口)

方法和接口

方法

Go 没有类。然而,仍然可以在结构体类型上定义方法。

方法接收者出现在 func 关键字和方法名之间的参数中。


Methods

Go does not have classes. However, you can define methods on struct types.

The method receiver appears in its own argument list between the func keyword and the method name.

package main

import (
	"fmt"
	"math"
)

type Vertex struct {
	X, Y float64
}

func (v *Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
	v := &Vertex{3, 4}
	fmt.Println(v.Abs())
}

方法

事实上,可以对包中的任意类型定义任意方法,而不仅仅是结构体。

不能对来自其他包的类型或基础类型定义方法。


Methods

In fact, you can define a method on any type you define in your package, not just structs.

You cannot define a method on a type from another package, or on a basic type.

package main

import (
	"fmt"
	"math"
)

type MyFloat float64

func (f MyFloat) Abs() float64 {
	if f < 0 {
		return float64(-f)
	}
	return float64(f)
}

func main() {
	f := MyFloat(-math.Sqrt2)
	fmt.Println(f.Abs())
}

接收者为指针的方法

方法可以与命名类型或命名类型的指针关联。

刚刚看到的两个 Abs 方法。一个是在 *Vertex 指针类型上,而另一个在 MyFloat 值类型上。

有两个原因需要使用指针接收者。首先避免在每个方法调用中拷贝值(如果值类型是大的结构体的话会更有效率)。其次,方法可以修改接收者指向的值。

尝试修改 Abs 的定义,同时 Scale 方法使用 Vertex 代替 *Vertex 作为接收者。

vVertex 的时候 Scale 方法没有任何作用。Scale 修改 v。当 v 是一个值(非指针),方法看到的是 Vertex 的副本,并且无法修改原始值。

Abs 的工作方式是一样的。只不过,仅仅读取 v。所以读取的是原始值(通过指针)还是那个值的副本并没有关系。


Methods with pointer receivers

Methods can be associated with a named type or a pointer to a named type.

We just saw two Abs methods. One on the *Vertex pointer type and the other on the MyFloat value type.

There are two reasons to use a pointer receiver. First, to avoid copying the value on each method call (more efficient if the value type is a large struct). Second, so that the method can modify the value that its receiver points to.

Try changing the declarations of the Abs and Scale methods to use Vertex as the receiver, instead of *Vertex.

The Scale method has no effect when v is a Vertex. Scale mutates v. When v is a value (non-pointer) type, the method sees a copy of the Vertex and cannot mutate the original value.

Abs works either way. It only reads v. It doesn't matter whether it is reading the original value (through a pointer) or a copy of that value.

package main

import (
	"fmt"
	"math"
)

type Vertex struct {
	X, Y float64
}

func (v *Vertex) Scale(f float64) {
	v.X = v.X * f
	v.Y = v.Y * f
}

func (v *Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
	v := &Vertex{3, 4}
	v.Scale(5)
	fmt.Println(v, v.Abs())
}

接口

接口类型是由一组方法定义的。

接口类型的值可以容纳实现这些方法的任何值。


Interfaces

An interface type is defined by a set of methods.

A value of interface type can hold any value that implements those methods.

package main

import (
	"fmt"
	"math"
)

type Abser interface {
	Abs() float64
}

func main() {
	var a Abser
	f := MyFloat(-math.Sqrt2)
	v := Vertex{3, 4}

	a = f  // a MyFloat implements Abser
	a = &v // a *Vertex implements Abser
	a = v  // a Vertex, does NOT
	       // implement Abser

	fmt.Println(a.Abs())
}

type MyFloat float64

func (f MyFloat) Abs() float64 {
	if f < 0 {
		return float64(-f)
	}
	return float64(f)
}

type Vertex struct {
	X, Y float64
}

func (v *Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

接口

一种类型通过实现那些方法来实现接口。

没有显式声明的必要。

隐式接口解藕了实现接口的包和定义接口的包:互不依赖。

也鼓励明确的接口定义,因为这样就无需找到每一个实现,并对其加上新的接口名称。

Package io 定义了 ReaderWriter;但不是一定要这么做。


Interfaces

A type implements an interface by implementing the methods.

There is no explicit declaration of intent.

Implicit interfaces decouple implementation packages from the packages that define the interfaces: neither depends on the other.

It also encourages the definition of precise interfaces, because you don't have to find every implementation and tag it with the new interface name.

Package io defines Reader and Writer; you don't have to.

package main

import (
	"fmt"
	"os"
)

type Reader interface {
	Read(b []byte) (n int, err error)
}

type Writer interface {
	Write(b []byte) (n int, err error)
}

type ReadWriter interface {
	Reader
	Writer
}

func main() {
	var w Writer

	// os.Stdout implements Writer
	w = os.Stdout

	fmt.Fprintf(w, "hello, writer\n")
}

错误

错误是可以用字符串描述自己的任何东西。 主要思路是由预定义的内建接口类型,error, 和其返回字符串窜的方法 Error 构成。


type error interface {
        Error() string
}
        

当用 fmt 包的多种不同的打印函数输出一个 error 时,会自动的调用该方法。


Errors

An error is anything that can describe itself as an error string. The idea is captured by the predefined, built-in interface type, error, with its single method, Error, returning a string:

type error interface {
	Error() string
}

The fmt package's various print routines automatically know to call the method when asked to print an error.

package main

import (
	"fmt"
	"time"
)

type MyError struct {
	When time.Time
	What string
}

func (e *MyError) Error() string {
	return fmt.Sprintf("at %v, %s",
		e.When, e.What)
}

func run() error {
	return &MyError{
		time.Now(),
		"it didn't work",
	}
}

func main() {
	if err := run(); err != nil {
		fmt.Println(err)
	}
}

Web 服务器

包 http 通过任何实现了 http.Handler 的值来响应 HTTP 请求:

package http

type Handler interface {
        ServeHTTP(w ResponseWriter,
                  r *Request)
}
        

在这个例子中,类型 Hello 实现了 http.Handler

访问 http://localhost:4000/ 会看到来自程序的问候。 注意: 这个例子无法在基于 web 的指南用户界面运行。为了尝试编写 web 服务器,可能需要安装 Go


Web servers

Package http serves HTTP requests using any value that implements http.Handler:

package http

type Handler interface {
	ServeHTTP(w ResponseWriter, r *Request)
}

In this example, the type Hello implements http.Handler.

Visit http://localhost:4000/ to see the greeting. Note: This example won't run through the web-based tour user interface. To try writing web servers you may want to Install Go.

package main

import (
	"fmt"
	"net/http"
)

type Hello struct{}

func (h Hello) ServeHTTP(
		w http.ResponseWriter,
		r *http.Request) {
	fmt.Fprint(w, "Hello!")
}

func main() {
	var h Hello
	http.ListenAndServe("localhost:4000",h)
}

图片

Package image 定义了 Image 接口:

package image

type Image interface {
        ColorModel() color.Model
        Bounds() Rectangle
        At(x, y int) color.Color
}
	

(参阅文档了解全部信息。)

同样,ColorColorModel 也是接口,但是通常因为直接使用预定义的实现 image.RGBAColorimage.RGBAColorModel 而忽略它们。


Images

Package image defines the Image interface:

package image

type Image interface {
	ColorModel() color.Model
	Bounds() Rectangle
	At(x, y int) color.Color
}

(See the documentation for all the details.)

Also, color.Color and color.Model are interfaces, but we'll ignore that by using the predefined implementations color.RGBA and color.RGBAModel.

package main

import (
	"fmt"
	"image"
)

func main() {
	m := image.NewRGBA(image.Rect(0, 0, 100, 100))
	fmt.Println(m.Bounds())
	fmt.Println(m.At(0, 0).RGBA())
}

练习:错误

从之前的练习中复制 Sqrt 函数,修改一下使其返回 error 值。

Sqrt 接收到一个负数时,应当返回一个非 nil 的错误值。复数同样也不被支持。

新建一个类型

type ErrNegativeSqrt float64

使其成为一个 error,通过

func (e ErrNegativeSqrt) Error() string

方法就可以让 ErrNegativeSqrt(-2).Error() 返回 "cannot Sqrt negative number: -2"

注意:Error 方法内调用 fmt.Print(e) 将会让程序陷入死循环。 可以先转换 e 来避免这个: fmt.Print(float64(e))。为什么?

修改 Sqrt 函数,使其接受一个负数时,返回 ErrNegativeSqrt 值。


Exercise: Errors

Copy your Sqrt function from the earlier exercises and modify it to return an error value.

Sqrt should return a non-nil error value when given a negative number, as it doesn't support complex numbers.

Create a new type

type ErrNegativeSqrt float64

and make it an error by giving it a

func (e ErrNegativeSqrt) Error() string

method such that ErrNegativeSqrt(-2).Error() returns "cannot Sqrt negative number: -2".

Note: a call to fmt.Print(e) inside the Error method will send the program into an infinite loop. You can avoid this by converting e first: fmt.Print(float64(e)). Why?

Change your Sqrt function to return an ErrNegativeSqrt value when given a negative number.

package main

import (
	"fmt"
)

func Sqrt(f float64) (float64, error) {
	return 0, nil
}

func main() {
	fmt.Println(Sqrt(2))
	fmt.Println(Sqrt(-2))
}

练习:HTTP 处理

实现下面的类型,并在其上定义 ServeHTTP 方法。 在 web 服务器中注册它们来处理指定的路径。

type String string
        
type Struct struct {
        Greeting string
        Punct    string
        Who      string
}

例如,可以使用如下方式注册处理方法:

http.Handle("/string", String("I'm a frayed knot."))
http.Handle("/struct", &Struct{"Hello", ":", "Gophers!"})

Exercise: HTTP Handlers

Implement the following types and define ServeHTTP methods on them. Register them to handle specific paths in your web server.

type String string

type Struct struct {
	Greeting string
	Punct    string
	Who      string
}

For example, you should be able to register handlers using:

http.Handle("/string", String("I'm a frayed knot."))
http.Handle("/struct", &Struct{"Hello", ":", "Gophers!"})
package main

import (
	"net/http"
)

func main() {
	// your http.Handle calls here
	http.ListenAndServe("localhost:4000", nil)
}

练习:图片

还记得之前写的图片生成器吗? 现在来编写另一个,不过这次将会返回一个 image.Image 的实现来代替 slice 的数据。

自定义一个 Image 类型,实现必要的方法,并且调用 pic.ShowImage

Bounds 应当返回一个 image.Rectangle,例如 image.Rect(0, 0, w, h)

ColorModel 应当返回 image.RGBAColorModel

At 应当返回一个颜色;在这个例子里,在最后一个图片生成器的值 v 匹配 color.RGBAColor{v, v, 255, 255}


Exercise: Images

Remember the picture generator you wrote earlier? Let's write another one, but this time it will return an implementation of image.Image instead of a slice of data.

Define your own Image type, implement the necessary methods, and call pic.ShowImage.

Bounds should return a image.Rectangle, like image.Rect(0, 0, w, h).

ColorModel should return color.RGBAModel.

At should return a color; the value v in the last picture generator corresponds to color.RGBA{v, v, 255, 255} in this one.

package main

import (
	"image"
	"tourcode.google.com/p/go-tour/pic"
)

type Image struct{}

func main() {
	m := Image{}
	pic.ShowImage(m)
}

练习:Rot13 读取器

一般的模式是 io.Reader 包裹另一个 io.Reader,用某些途径修改特定的流。

例如, gzip.NewReader 函数输入一个 io.Reader(gzip 的数据流)并且返回一个同样实现了 io.Reader*gzip.Reader(解压缩后的数据流)。

实现一个实现了 io.Readerrot13Reader,用 ROT13 修改数据流中的所有的字母进行密文替换。

rot13Reader 已经提供。通过实现其 Read 方法使得它匹配 io.Reader


Exercise: Rot13 Reader

A common pattern is an io.Reader that wraps another io.Reader, modifying the stream in some way.

For example, the gzip.NewReader function takes an io.Reader (a stream of gzipped data) and returns a *gzip.Reader that also implements io.Reader (a stream of the decompressed data).

Implement a rot13Reader that implements io.Reader and reads from an io.Reader, modifying the stream by applying the ROT13 substitution cipher to all alphabetical characters.

The rot13Reader type is provided for you. Make it an io.Reader by implementing its Read method.

package main

import (
	"io"
	"os"
	"strings"
)

type rot13Reader struct {
	r io.Reader
}

func main() {
	s := strings.NewReader(
		"Lbh penpxrq gur pbqr!")
	r := rot13Reader{s}
	io.Copy(os.Stdout, &r)
}
Concurrency(并发)

并发

Goroutine

goroutine 是由 Go 运行时环境管理的轻量级线程。

go f(x, y, z)

开启一个新的 goroutine 执行

f(x, y, z)

fxyz 是当前 goroutine 中定义的,但是在新的 goroutine 中运行 f

goroutine 在相同的地址空间中运行,因此访问共享内存必须进行同步。 sync 提供了这种可能,不过在 Go 中并不经常用到,因为有其他的办法。 (在接下来的内容中会涉及到。)


Goroutines

A goroutine is a lightweight thread managed by the Go runtime.

go f(x, y, z)

starts a new goroutine running

f(x, y, z)

The evaluation of f, x, y, and z happens in the current goroutine and the execution of f happens in the new goroutine.

Goroutines run in the same address space, so access to shared memory must be synchronized. The sync package provides useful primitives, although you won't need them much in Go as there are other primitives. (See the next slide.)

package main

import (
	"fmt"
	"runtimetime"
)

func say(s string) {
	for i := 0; i < 5; i++ {
		runtime.Gosched()time.Sleep(100 * time.Millisecond)
		fmt.Println(s)
	}
}

func main() {
	go say("world")
	say("hello")
}

Channel(管道)

channel 是有类型的管道,可以用 channel 操作符 <- 对其发送或者接收值。

ch <- v    // 将 v 送入 channel ch。
v := <-ch  // 从 ch 接收,并且赋值给 v。

(“箭头”就是数据流的方向。)

和 map 与 slice 一样,channel 使用前必须创建:

ch := make(chan int)
	

默认情况下,在另一端准备好之前,发送和接收都会阻塞。这使得 goroutine 可以在没有明确的锁或竞态变量的情况下进行同步。


Channels

Channels are a typed conduit through which you can send and receive values with the channel operator, <-.

ch <- v    // Send v to channel ch.
v := <-ch  // Receive from ch, and
           // assign value to v.

(The data flows in the direction of the arrow.)

Like maps and slices, channels must be created before use:

ch := make(chan int)

By default, sends and receives block until the other side is ready. This allows goroutines to synchronize without explicit locks or condition variables.

package main

import "fmt"

func sum(a []int, c chan int) {
	sum := 0
	for _, v := range a {
		sum += v
	}
	c <- sum  // send sum to c
}

func main() {
	a := []int{7, 2, 8, -9, 4, 0}

	c := make(chan int)
	go sum(a[:len(a)/2], c)
	go sum(a[len(a)/2:], c)
	x, y := <-c, <-c  // receive from c

	fmt.Println(x, y, x + y)
}

缓冲 channel

channel 可以是带缓冲的。为 make 提供第二个参数作为缓冲长度来初始化一个缓冲 channel:

ch := make(chan int, 100)

向缓冲 channel 发送数据的时候,只有在缓冲区满的时候才会阻塞。当缓冲区清空的时候接受阻塞。

修改例子使得缓冲区被填满,然后看看会发生什么。


Buffered Channels

Channels can be buffered. Provide the buffer length as the second argument to make to initialize a buffered channel:

ch := make(chan int, 100)

Sends to a buffered channel block only when the buffer is full. Receives block when the buffer is empty.

Modify the example to overfill the buffer and see what happens.

package main

import "fmt"

func main() {
	c := make(chan int, 2)
	c <- 1
	c <- 2
	fmt.Println(<-c)
	fmt.Println(<-c)
}

Range 和 Close

发送者可以 close 一个 channel 来表示再没有值会被发送了。接收者可以通过赋值语句的第二参数来测试 channel 是否被关闭:当没有值可以接收并且 channel 已经被关闭,那么

v, ok := <-ch

ok 会被设置为 false

循环 for i := range c 会不断从 channel 接收值,直到它被关闭。

注意: 只有发送者才能关闭 channel,而不是接收者。向一个已经关闭的 channel 发送数据会引起 panic。

还要注意:channel 与文件不同;通常情况下无需关闭它们。只有在需要告诉接收者没有更多的数据的时候才有必要进行关闭,例如中断一个 range


Range and Close

A sender can close a channel to indicate that no more values will be sent. Receivers can test whether a channel has been closed by assigning a second parameter to the receive expression: after

v, ok := <-ch

ok is false if there are no more values to receive and the channel is closed.

The loop for i := range c receives values from the channel repeatedly until it is closed.

Note: Only the sender should close a channel, never the receiver. Sending on a closed channel will cause a panic.

Another note: Channels aren't like files; you don't usually need to close them. Closing is only necessary when the receiver must be told there are no more values coming, such as to terminate a range loop.

package main

import (
	"fmt"
)

func fibonacci(n int, c chan int) {
	x, y := 0, 1
	for i := 0; i < n; i++ {
		c <- x
		x, y = y, x + y
	}
	close(c)
}

func main() {
	c := make(chan int, 10)
	go fibonacci(cap(c), c)
	for i := range c {
		fmt.Println(i)
	}
}

Select

select 语句使得一个 goroutine 在多个通讯操作上等待。

select 会阻塞,直到条件分支中的某个可以继续执行,这时就会执行那个条件分支。如果有多个都准备好的时候,会随机选一个。


Select

The select statement lets a goroutine wait on multiple communication operations.

A select blocks until one of its cases can run, then it executes that case. It chooses one at random if multiple are ready.

package main

import "fmt"

func fibonacci(c, quit chan int) {
	x, y := 0, 1
	for {
		select {
		case c <- x:
			x, y = y, x + y
		case <-quit:
			fmt.Println("quit")
			return
		}
	}
}

func main() {
	c := make(chan int)
	quit := make(chan int)
	go func() {
		for i := 0; i < 10; i++ {
			fmt.Println(<-c)
		}
		quit <- 0
	}()
	fibonacci(c, quit)
}

默认选择

select 中的其他条件分支都没有准备好的时候,default 分支会被执行。

为了非阻塞的发送或者接收,可使用 default 分支:

select {
case i := <-c:
        // use i
default:
        // receiving from c would block
}

注意: 这个例子无法在指南的基于 web 的用户界面中运行,因为沙盒环境是没有定时器概念的。需要安装、 Go 来了解这个例子如何运行。


Default Selection

The default case in a select is run if no other case is ready.

Use a default case to try a send or receive without blocking:

select {
case i := <-c:
	// use i
default:
	// receiving from c would block
}

Note: This example won't run through the web-based tour user interface because the sandbox environment has no concept of time. You may want to install Go to see this example in action.

package main

import (
	"fmt"
	"time"
)

func main() {
	tick := time.Tick(1e8)
	boom := time.After(5e8)
	for {
		select {
		case <-tick:
			fmt.Println("tick.")
		case <-boom:
			fmt.Println("BOOM!")
			return
		default:
			fmt.Println("    .")
			time.Sleep(5e7)
		}
	}
	}

练习:等价二叉树

可以用多种不同的二叉树的叶子节点存储相同的数列值。例如,这里有两个二叉树保存了序列 1,1,2,3,5,8,13。

用于检查两个二叉树是否存储了相同的序列的函数在多数语言中都是相当复杂的。这里将使用 Go 的并发和 channel 来编写一个简单的解法。

这个例子使用了 tree 包,定义了类型:

type Tree struct {
        Left  *Tree
        Value int
        Right *Tree
}

Exercise: Equivalent Binary Trees

There can be many different binary trees with the same sequence of values stored at the leaves. For example, here are two binary trees storing the sequence 1, 1, 2, 3, 5, 8, 13. binary trees

A function to check whether two binary trees store the same sequence is quite complex in most languages. We'll use Go's concurrency and channels to write a simple solution.

This example uses the tree package, which defines the type:

type Tree struct {
	Left  *Tree
	Value int
	Right *Tree
}

练习:等价二叉树

1. 实现 Walk 函数。

2. 测试 Walk 函数。

函数 tree.New(k) 构造了一个随机结构的二叉树,保存了值 k2k3k,...,10k

创建一个新的 channel ch 并且对其进行步进:

go Walk(tree.New(1), ch)

然后从 channel 中读取并且打印 10 个值。应当是值 1,2,3,...,10。

3.Walk 实现 Same 函数来检测是否 t1t2 存储了相同的值。

4. 测试 Same 函数。

Same(tree.New(1), tree.New(1)) 应当返回 true,而 Same(tree.New(1), tree.New(2)) 应当返回 false。


Exercise: Equivalent Binary Trees

1. Implement the Walk function.

2. Test the Walk function.

The function tree.New(k) constructs a randomly-structured binary tree holding the values k, 2k, 3k, ..., 10k.

Create a new channel ch and kick off the walker:

go Walk(tree.New(1), ch)

Then read and print 10 values from the channel. It should be the numbers 1, 2, 3, ..., 10.

3. Implement the Same function using Walk to determine whether t1 and t2 store the same values.

4. Test the Same function.

Same(tree.New(1), tree.New(1)) should return true, and Same(tree.New(1), tree.New(2)) should return false.

package main

import "tourcode.google.com/p/go-tour/tree"

// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int)

// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool

func main() {
}

练习:Web 爬虫

在这个练习中,将会使用 Go 的并发特性来并行执行 web 爬虫。

修改 Crawl 函数来并行的抓取 URLs,并且保证不重复。


Exercise: Web Crawler

In this exercise you'll use Go's concurrency features to parallelize a web crawler.

Modify the Crawl function to fetch URLs in parallel without fetching the same URL twice.

package main

import (
	"fmt"
)

type Fetcher interface {
	// Fetch returns the body of URL and
	// a slice of URLs found on that page.
	Fetch(url string) (body string, urls []string, err error)
}

// Crawl uses fetcher to recursively crawl
// pages starting with url, to a maximum of depth.
func Crawl(url string, depth int, fetcher Fetcher) {
	// TODO: Fetch URLs in parallel.
	// TODO: Don't fetch the same URL twice.
	// This implementation doesn't do either:
	if depth <= 0 {
		return
	}
	body, urls, err := fetcher.Fetch(url)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("found: %s %q\n", url, body)
	for _, u := range urls {
		Crawl(u, depth-1, fetcher)
	}
	return
}

func main() {
	Crawl("http://golang.org/", 4, fetcher)
}


// fakeFetcher is Fetcher that returns canned results.
type fakeFetcher map[string]*fakeResult

type fakeResult struct {
	body string
	urls     []string
}

func (f *fakeFetcher) Fetch(url string) (string, []string, error) {
	if res, ok := (*f)[url]; ok {
		return res.body, res.urls, nil
	}
	return "", nil, fmt.Errorf("not found: %s", url)
}

// fetcher is a populated fakeFetcher.
var fetcher = &fakeFetcher{
	"http://golang.org/": &fakeResult{
		"The Go Programming Language",
		[]string{
			"http://golang.org/pkg/",
			"http://golang.org/cmd/",
		},
	},
	"http://golang.org/pkg/": &fakeResult{
		"Packages",
		[]string{
			"http://golang.org/",
			"http://golang.org/cmd/",
			"http://golang.org/pkg/fmt/",
			"http://golang.org/pkg/os/",
		},
	},
	"http://golang.org/pkg/fmt/": &fakeResult{
		"Package fmt",
		[]string{
			"http://golang.org/",
			"http://golang.org/pkg/",
		},
	},
	"http://golang.org/pkg/os/": &fakeResult{
		"Package os",
		[]string{
			"http://golang.org/",
			"http://golang.org/pkg/",
		},
	},
}

Where to Go from here...

可以从 installing Go 或者下载 Go App Engine SDK

一旦在机器上安装好了 Go, Go Documentation 是应当继续阅读的内容 作为开始。 它包含了参考、指南、视频等等更多资料。

在标准库上需要帮助的话,参考 package reference。语言本身的帮助,可以阅读令人愉快的 Language Spec

进一步探索 Go 的并发模型,参阅 Share Memory by Communicating 的代码之旅。

First Class Functions in Go 为 Go 的函数类型提供了有趣的展示。

Go Blog 有着众多的关于 Go 的文章信息。

访问 golang.org 了解更多内容。


Where to Go from here...

You can get started by installing Go or downloading the Go App Engine SDK.

Once you have Go on your machine, the The Go Documentation is a great place to continue start. It contains references, tutorials, videos, and more.

If you need help with the standard library, see the package reference. For help with the language itself, you might be surprised to find the Language Spec is quite readable.

To further explore Go's concurrency model, see the Share Memory by Communicating codewalk.

The First Class Functions in Go codewalk gives an interesting perspective on Go's function types.

The Go Blog has a large archive of informative Go articles.

Visit golang.org for more.