go tour 中文向導頁面代碼

灬猜想灬 12年前發布 | 3K 次閱讀 Ubuntu MATE 14.04 iOS 5 .NET開源

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" >
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Go 指南</title>

<!-- jQuery --> <script src="static/jquery.js"></script>

<!-- CodeMirror --> <link rel="stylesheet" href="/static/codemirror/lib/codemirror.css"> <script src="/static/codemirror/lib/codemirror.js"></script> <script src="/static/codemirror/lib/go.js"></script>

<!-- Tour --> <link rel="stylesheet" href="static/tour.css"> <script src="static/mode.js"></script> <script src="static/tour.js"></script>

</head> <body> <div id="wrap"> <div id="header"> <div id="slidenum">1</div> <a href="#toc" id="tocbtn" title="目錄"></a> <h1>Go 指南</h1> </div>

    <div id="slides" class="slides"><!-- begin slides -->


<div class="slide"> <h2>Hello, 世界</h2> <p> 歡迎來到<a target="_blank" href=" 編程語言</a>指南. <p> 該指南被分為三節。在每節的末尾有若干個練習需要讀者完成。 <p> 該指南可以進行交互。點擊“Run”按鈕(或按 Shift + Enter)可以在<span class="appengineMode">遠程服務器上</span><span class="localMode">你的電腦上</span>編譯并執行程序。 結果展示在代碼的下面。 <p> 這些例子程序展示了 Go 的各個方面。在指南中的程序可以成為你積累經驗的開始。 <p> 編輯程序并且再次執行它。 <p> 當你準備好繼續了,點擊“Next”按鈕或按 PageDown 鍵。 <pre class="source">package main

import "fmt"

func main() { fmt.Println("Hello, 世界") }</pre> </div>

<div class="slide nocode appengineMode"> <h2>Go 本地化</h2> <p> 該指南也有其他語言版本: <ul> <li><a href=" Portuguese &mdash; Português do Brasil</a></li> <li><a href=" &mdash; 普通話</a></li> <li><a href=" &mdash; 日本語</a></li> </ul> <p> (如果你愿意翻譯該指南到其他語言,從 <code>https://code.google.com/p/go-tour</code&gt; 檢出代碼,翻譯 <code>static/index.html</code>, 并根據 <code>appengine/README</code> 的介紹將其部署到 App Engine 上。) <p> 點擊“Next”按鈕或者按 PageDown 繼續。 </div>

<div class="slide nocode appengineMode"> <h2>離線 Go 指南</h2> <p> 在沒有互聯網接入的情況下該指南可以作為獨立的程序使用。 <p> 指南獨立運行會在本地的設備上構建和編譯代碼,這運行得更快。并且會包括一些在沙盒版本中沒有的體驗上的增強。 <p> 為了在本地運行指南首先要<a target="_blank" href=" Go</a>, 然后使用 <a target="_blank" href=" get</a> 安裝 <a target="_blank" href=" <pre> go get code.google.com/p/go-tour/gotour</pre> <p> 也可以安裝<a target="_blank" href=" <pre> go get bitbucket.org/mikespook/go-tour-zh/gotour</pre> <p> 最后執行安裝產生的 <code>gotour</code> 執行文件。 <p> 如果不安裝本地版本,點擊“Next”按鈕或者按 PageDown 繼續。 <p> <i>(可以在任何時候點擊“目錄”按鈕返回這個介紹。)</i> </div>

<div class="toc">簡介</div>

<div class="slide"> <h2>包</h2> <p> 每個 Go 程序都是由包組成的。 <p> 程序運行的入口是包 <code>main</code>。 <p> 這個程序使用并導入了包 <code>"fmt"</code> 和 <code>"math"</code>。 <p> 按照慣例,包名與導入路徑的最后一個目錄一致。 <pre class="source">package main

import ( "fmt" "math" )

func main() { fmt.Println("Happy", math.Pi, "Day") }</pre> </div>

<div class="slide"> <h2>導入</h2> <p> 這個代碼用圓括號組合了導入,這是“factored”導入語句。同樣可以編寫多個導入語句,例如: <pre>import "fmt" import "math"</pre> 不過通常都會用 factored 格式來使代碼工整。 <pre class="source">package main

import ( "fmt" "math" )

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

<div class="slide"> <h2>導出名</h2> <p> 在導入了一個包之后,就可以用其導出的名稱來調用它。 <p> 在 Go 中,首字母大寫的名稱是被導出的。 <p> <code>Foo</code> 和 <code>FOO</code> 都是被導出的名稱。 名稱 <code>foo</code> 是不會被導出的。 <p> 執行代碼。然后將 <code>math.pi</code> 改名為 <code>math.Pi</code> 再試著執行一下。 <pre class="source">package main

import ( "fmt" "math" )

func main() { fmt.Println(math.pi) }</pre> </div>

<div class="slide"> <h2>函數</h2> <p> 函數可以沒有參數或接受多個參數。 <p> 在這個例子中,<code>add</code> 接受兩個 <code>int</code> 類型的參數。 <p> 注意類型在變量名<i>之后</i>。 <p> (參考<a target="_blank" href=" Go 語法定義的文章</a>了解類型以這種形式出現的原因。) <pre class="source">package main

import "fmt"

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

func main() { fmt.Println(add(42, 13)) }</pre> </div>

<div class="slide"> <h2>函數</h2> <p> 當兩個或多個連續的函數命名參數是同一類型,則除了最后一個類型之外,其他都可以省略。 <p> 在這個例子中 , <pre>x int, y int</pre> <p> 被縮寫為 <p> <pre>x, y int</pre> <pre class="source">package main

import "fmt"

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

func main() { fmt.Println(add(42, 13)) }</pre> </div>

<div class="slide"> <h2>函數</h2> <p> 函數可以返回任意數量的返回值。 <p> 這個函數返回了兩個字符串。 <pre class="source">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) }</pre> </div>

<div class="slide"> <h2>函數</h2> <p> 函數接受參數。在 Go 中,函數可以返回多個“結果參數”,而不僅僅是一個值。它們可以像變量那樣命名和使用。 <p> 如果命名了返回值參數,一個沒有參數的 <code>return</code> 語句,會將當前的值作為返回值返回。 <pre class="source">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)) }</pre> </div>

<div class="slide"> <h2>變量</h2> <p> <code>var</code> 語句定義了一個變量的列表;跟函數的參數列表一樣,類型在后面。

<pre class="source">package main

import "fmt"

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

func main() { fmt.Println(x, y, z, c, python, java) }</pre> </div>

<div class="slide"> <h2>變量</h2> <p> 變量定義可以包含初始值,每個變量對應一個。 <p> 如果初始化是使用表達式,則可以省略類型;變量從初始值中獲得類型。 <pre class="source">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) }</pre> </div>

<div class="slide"> <h2>變量</h2> <p> 在函數中,<code>:=</code> 簡潔賦值語句在明確類型的地方,可以用于替代 <code>var</code> 定義。 <p> (<code>:=</code> 結構不能使用在函數外,函數外的每個語法塊都必須以關鍵字開始。) <pre class="source">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)

}</pre> </div>

<div class="slide"> <h2>常量</h2> <p> 常量的定義與變量類似,只不過使用 <code>const</code> 關鍵字。 <p> 常量可以是字符、字符串、布爾或數字類型的值。 <pre class="source">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)

}</pre> </div>

<div class="slide"> <h2>數值常量</h2> <p> 數值常量是高精度的<i>值</i>。 <p> 一個未指定類型的常量由上下文來決定其類型。 <p> 也嘗試一下輸出 <code>needInt(Big)</code>吧。 <pre class="source">package main

import "fmt"

const ( Big = 1&lt;&lt;100 Small = Big&gt;&gt;99 )

func needInt(x int) int { return x10 + 1 } func needFloat(x float64) float64 { return x0.1 }

func main() { fmt.Println(needInt(Small)) fmt.Println(needFloat(Small)) fmt.Println(needFloat(Big)) }</pre> </div>

<div class="slide"> <h2>For</h2> <p> Go 只有一種循環結構,<code>for</code> 循環。 <p> 基本的 <code>for</code> 循環看起來跟 C 或者 Java 中做的一樣,除了沒有了 <code>( )</code> 之外(甚至強制不能使用它們),而 <code>{ }</code> 是必須的。 <pre class="source">package main

import "fmt"

func main() { sum := 0 for i := 0; i &lt; 10; i++ { sum += i } fmt.Println(sum) }</pre> </div>

<div class="slide"> <h2>For</h2> <p> 跟 C 或者 Java 中一樣,可以讓前置、后置語句為空。 <pre class="source">package main

import "fmt"

func main() { sum := 1 for ; sum &lt; 1000; { sum += sum } fmt.Println(sum) }</pre> </div>

<div class="slide"> <h2>For</h2> <p> 基于此可以省略分號: C 的 <code>while</code> 在 Go 中也是用 <code>for</code> 實現。 <pre class="source">package main

import "fmt"

func main() { sum := 1 for sum &lt; 1000 { sum += sum } fmt.Println(sum) }</pre> </div>

<div class="slide"> <h2>For</h2> <p> 如果省略了循環條件,它就是個死循環源。 <pre class="source">package main

func main() { for ; ; { } }</pre> </div>

<div class="slide"> <h2>For</h2> <p> 而為了避免累贅,分號可以省略,因此一個死循環可以簡潔地表達。 <pre class="source">package main

func main() { for { } }</pre> </div>

<div class="slide"> <h2>If</h2> <p> <code>if</code> 語句看起來跟 C 或者 Java 中的一樣,除了沒有了 <code>( )</code> 之外(甚至強制不能使用它們),而 <code>{ }</code> 是必須的。 <p> (耳熟嗎?) <pre class="source">package main

import ( "fmt" "math" )

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

func main() { fmt.Println(sqrt(2), sqrt(-4)) }</pre> </div>

<div class="slide"> <h2>If</h2> <p> 跟 <code>for</code> 一樣,<code>if</code> 語句可以在條件之前執行一個簡單的語句。 <p> 由這個語句定義的變量的作用域僅在 <code>if</code> 范圍之內。 <p> (在最后的 <code>return</code> 語句處使用 <code>v</code> 看看。) <pre class="source">package main

import ( "fmt" "math" )

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

func main() { fmt.Println( pow(3, 2, 10), pow(3, 3, 20), ) }</pre> </div>

<div class="slide"> <h2>If</h2> <p> 在 <code>if</code> 的簡單語句處定義的變量同樣可以在任何對應的 <code>else</code> 塊中使用。 <pre class="source">package main

import ( "fmt" "math" )

func pow(x, n, lim float64) float64 { if v := math.Pow(x, n); v &lt; lim { return v } else { fmt.Printf("%g >= %g\n", v, lim) } // 不能在這里使用 v,因此 return lim }

func main() { fmt.Println( pow(3, 2, 10), pow(3, 3, 20), ) }</pre> </div>

<div class="slide"> <h2>基本類型</h2> <p> Go 的基本類型有 <pre>bool

string

int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptr

byte // uint8 的別名

rune // int32 的別名 // 代表一個Unicode碼點

float32 float64

complex64 complex128</pre> <pre class="source">package main

import ( "math/cmplx" "fmt" )

var ( ToBe bool = false MaxInt uint64 = 1&lt;&lt;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) }</pre> </div>

<div class="slide"> <h2>結構體</h2> <p> 一個結構體(<code>struct</code>)就是一個字段的集合。 <p> (而 <code>type</code> 定義跟其字面意思相符。) <pre class="source">package main

import "fmt"

type Vertex struct { X int Y int }

func main() { fmt.Println(Vertex{1, 2}) }</pre> </div>

<div class="slide"> <h2>結構體字段</h2> <p> 結構體字段使用點號來訪問。 <pre class="source">package main

import "fmt"

type Vertex struct { X int Y int }

func main() { v := Vertex{1, 2} v.X = 4 fmt.Println(v.X) }</pre> </div>

<div class="slide"> <h2>指針</h2> <p> Go 有指針,但是沒有指針運算。 <p> 結構體字段可以通過結構體指針來訪問。通過指針間接的訪問是透明的。 <pre class="source">package main

import "fmt"

type Vertex struct { X int Y int }

func main() { p := Vertex{1, 2} q := &amp;p q.X = 1e9 fmt.Println(p) }</pre> </div>

<div class="slide"> <h2>結構體文法</h2> <p> 結構體文法表示通過結構體字段的值作為列表來新分配一個結構體。 <p> 使用 <code>Name:</code> 語法可以僅列出部分字段。(字段名的順序無關。) <p> 特殊的前綴 <code>&amp;</code> 構造了指向結構體文法的指針。 <pre class="source">package main

import "fmt"

type Vertex struct { X, Y int }

var ( p = Vertex{1, 2} // has type Vertex q = &amp;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) }</pre> </div>

<div class="slide"> <h2>new 函數</h2> <p> 表達式 <code>new(T)</code> 分配了一個零初始化的 <code>T</code> 值,并返回指向它的指針。 <pre>var t *T = new(T)</pre> <p> 或 <pre>t := new(T)</pre> <pre class="source">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) }</pre> </div>

<div class="slide"> <h2>Map</h2> <p> map 映射鍵到值。 <p> <!-- TODO: empty part not true in compilers yet --> map 在使用之前必須用 <code>make</code> 來創建(不是 <code>new</code>);一個值為 <code>nil</code> 的 map 是空的,并且不能賦值。 <pre class="source">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"]) }</pre> </div>

<div class="slide"> <h2>Map</h2> <p> map 的文法跟結構體文法相似,不過鍵名是必須的。 <pre class="source">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) }</pre> </div>

<div class="slide"> <h2>Map</h2> <p> 如果頂層類型只有類型名的話,可以在文法的元素中省略鍵名。 <pre class="source">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) }</pre> </div>

<div class="slide"> <h2>修改 map</h2> <p> 在 map <code>m</code> 中插入或修改一個元素: <pre>m[key] = elem</pre> <p> 獲得元素: <pre>elem = m[key]</pre> <p> 刪除元素: <pre>delete(m, key)</pre> <p> 通過雙賦值檢測某個鍵存在: <pre>elem, ok = m[key]</pre> <p> 如果 <code>key</code> 在 <code>m</code> 中, <code>ok</code> 是 <code>true</code>。 否則,<code>ok</code> 是 <code>false</code> 并且 <code>elem</code> 是 map 的元素類型的零值。 <p> 同樣的,當從 map 中讀取某個不存在的鍵時,結果是 map 的元素類型的零值。 <pre class="source">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)

}</pre> </div>

<div class="slide"> <h2>slice</h2> <p> slice 指向數組的值,并且同時包含了長度信息。 <p> <code>[]T</code> 是一個元素類型為 <code>T</code> 的 slice。 <pre class="source">package main

import "fmt"

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

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

}</pre> </div>

<div class="slide"> <h2>Slice</h2> <p> slice 可以重新切片,創建一個新的 slice 值指向相同的數組。 <p> 表達式 <pre>s[lo:hi]</pre> <p> 表示從 <code>lo</code> 到 <code>hi-1</code> 的 slice 元素,含有兩端。 因此 <pre>s[lo:lo]</pre> <p> 是空的,而 <pre>s[lo:lo+1]</pre> <p> 有一個元素。 <pre class="source">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:])

}</pre> </div>

<div class="slide"> <h2>Slice</h2> <p> slice 由函數 <code>make</code> 創建。這會分配一個零長度的數組并且返回一個 slice 指向這個數組: <pre>a := make([]int, 5) // len(a)=5</pre> slice 有長度和容量。slice 的容量是底層數組可以增長的最大長度。 <p> 為了指定容量,可傳遞第三個參數到 <code>make</code>: <p> <pre>b := make([]int, 0, 5) // len(b)=0, cap(b)=5</pre> slice 可以通過“重新切片”來擴容(增長到容量上限): <p> <pre>b = b[:cap(b)] // len(b)=5, cap(b)=5 b = b[1:] // len(b)=4, cap(b)=4</pre> <pre class="source">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) }</pre> </div>

<div class="slide"> <h2>Slice</h2> <p> slice 的零值是 <code>nil</code>。 <p> 一個 nil 的 slice 的長度和容量是 0。 <p> (了解更多關于 slice 的內容,參閱文章 “<a target="_blank" href=" <pre class="source">package main

import "fmt"

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

<div class="slide"> <h2>函數</h2> <p> 函數也是值。 <pre class="source">package main

import ( "fmt" "math" )

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

fmt.Println(hypot(3, 4))

}</pre> </div>

<div class="slide"> <h2>函數</h2> <p> 并且函數是完全閉包的。 <p> 函數 <code>adder</code> 返回一個閉包。每個閉包被綁定到了特定的 <code>sum</code> 變量上。 <pre class="source">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 &lt; 10; i++ { fmt.Println( pos(i), neg(-2*i), ) } }</pre> </div>

<div class="slide"> <h2>Range</h2> <p> <code>for</code> 循環的 <code>range</code> 格式可以對 slice 或者 map 進行迭代循環。 <pre class="source">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) } }</pre> </div>

<div class="slide"> <h2>Range</h2> <p> 可以將值賦值給 <code>_</code> 來忽略序號和值。 <p> 如果只需要索引值,去掉&ldquo;<code>, value</code>&rdquo;的部分即可。 <pre class="source">package main

import "fmt"

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

<div class="slide"> <h2>Switch</h2> <p> 你可能已經猜到 <code>switch</code> 可能的形式了。 <p> case 體會自動終止,除非用 <code>fallthrough</code> 語句作為結尾。

<pre class="source">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) } }</pre> </div>

<div class="slide"> <h2>Switch</h2> <p> switch 的條件從上到下的執行,當匹配成功的時候停止。 <p> (例如, <pre>switch i { case 0: case f(): }</pre> <p> 當 <code>i==0</code> 時不會調用 <code>f</code>。)

<pre class="source">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.") } }</pre> </div>

<div class="slide"> <h2>Switch</h2> <p> 沒有條件的 switch 同 <code>switch true</code> 一樣。 <p> 這一構造使得可以用更清晰的形式來編寫長的 if-then-else 鏈。 <pre class="source">package main

import ( "fmt" "time" )

func main() { t := time.Now() switch { case t.Hour() &lt; 12: fmt.Println("Good morning!") case t.Hour() &lt; 17: fmt.Println("Good afternoon.") default: fmt.Println("Good evening.") } }</pre> </div>

<div class="slide"> <h2>練習:循環和函數</h2> <p> 作為練習函數和循環的簡單途徑,用牛頓法實現開方函數。 <p> 在這個例子中,牛頓法是通過選擇一個初始點 <i>z</i> 然后重復這一過程求 <code>Sqrt(x)</code> 的近似值: <div style="text-align: center"> <img src="

import ( "fmt" )

func Sqrt(x float64) float64 { }

func main() { fmt.Println(Sqrt(2)) }</pre> </div>

<div class="slide"> <h2>練習:Map</h2> <p> 實現 <code>WordCount</code>。它應當返回一個含有 <code>s</code> 中每個 &ldquo;word&rdquo; 數量的 map。函數 <code>wc.Test</code> 針對這個函數執行一個測試用例,并打印成功或者失敗。 <p> 你會發現 <a target="_blank" href="; 很有幫助。

<pre class="source">package main

import ( "<span class="appengineMode">tour</span><span class="localMode">bitbucket.org/mikespook/go-tour-zh</span>/wc" )

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

func main() { wc.Test(WordCount) }</pre> </div>

<div class="slide"> <h2>練習:Slice</h2> <p> 實現 <code>Pic</code>。它應當接受一個 slice 的長度 <code>dy</code>,和 slice 中每個元素的長度的 8 位無符號整數 <code>dx</code>。當執行這個程序,它會將整數轉換為灰度(好吧,藍度)圖片進行展示。 <p> 圖片的實現已經完成。可能用到的函數包括 <code>x^y</code>,<code>(x+y)/2</code> 和 <code>x*y</code>。 <p> (需要使用循環來分配 <code>[][]uint8</code> 中的每個 <code>[]uint8</code>。) <p> (使用 <code>uint8(intValue)</code> 在類型之間轉換。)

<pre class="source">package main

import "<span class="appengineMode">tour</span><span class="localMode">bitbucket.org/mikespook/go-tour-zh</span>/pic"

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

func main() { pic.Show(Pic) }</pre> </div>

<div class="slide"> <h2>練習:斐波納契閉包</h2> <p> 現在來通過函數找些樂趣。 <p> 實現一個 <code>fibonacci</code> 函數,返回一個函數(一個閉包)可以返回連續的斐波納契數。

<pre class="source">package main

import "fmt"

// fibonacci 函數會返回一個返回 int 的函數。 func fibonacci() func() int { }

func main() { f := fibonacci() for i := 0; i &lt; 10; i++ { fmt.Println(f()) } }</pre> </div>

<div class="slide"> <h2>進階練習:復數立方根</h2> <p> 讓我們通過 <code>complex64</code> 和 <code>complex128</code> 來探索一下 Go 內建的復數。 對于立方根,牛頓法需要大量循環: <div style="text-align: center"> <img src="

import "fmt"

func Cbrt(x complex128) complex128 { }

func main() { fmt.Println(Cbrt(2)) }</pre> </div>

<div class="toc">方法和接口</div>

<div class="slide nocode"> <h2>方法和接口</h2> </div>

<div class="slide"> <h2>方法</h2> <p> Go 沒有類。然而,仍然可以在結構體類型上定義方法。 <p> <i>方法接收者</i>出現在 <code>func</code> 關鍵字和方法名之間的參數中。 <pre class="source">package main

import ( "fmt" "math" )

type Vertex struct { X, Y float64 }

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

func main() { v := &amp;Vertex{3, 4} fmt.Println(v.Abs()) }</pre> </div>

<div class="slide"> <h2>方法</h2> <p> 事實上,可以對包中的<i>任意</i>類型定義任意方法,而不僅僅是結構體。 <p> 不能對來自其他包的類型或基礎類型定義方法。 <pre class="source">package main

import ( "fmt" "math" )

type MyFloat float64

func (f MyFloat) Abs() float64 { if f &lt; 0 { return float64(-f) } return float64(f) }

func main() { f := MyFloat(-math.Sqrt2) fmt.Println(f.Abs()) }</pre> </div>

<div class="slide"> <h2>接收者為指針的方法</h2> <p> 方法可以與命名類型或命名類型的指針關聯。 <p> 剛剛看到的兩個 <code>Abs</code> 方法。一個是在 <code>Vertex</code> 指針類型上,而另一個在 <code>MyFloat</code> 值類型上。 <p> 有兩個原因需要使用指針接收者。首先避免在每個方法調用中拷貝值(如果值類型是大的結構體的話會更有效率)。其次,方法可以修改接收者指向的值。 <p> 嘗試修改 <code>Abs</code> 的定義,同時 <code>Scale</code> 方法使用 <code>Vertex</code> 代替 <code>Vertex</code> 作為接收者。 <p> 當 <code>v</code> 是 <code>Vertex</code> 的時候 <code>Scale</code> 方法沒有任何作用。<code>Scale</code> 修改 <code>v</code>。當 <code>v</code> 是一個值(非指針),方法看到的是 <code>Vertex</code> 的副本,并且無法修改原始值。 <p> <code>Abs</code> 的工作方式是一樣的。只不過,僅僅讀取 <code>v</code>。所以讀取的是原始值(通過指針)還是那個值的副本并沒有關系。 <pre class="source">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.Xv.X + v.Y*v.Y) }

func main() { v := &amp;Vertex{3, 4} v.Scale(5) fmt.Println(v, v.Abs()) }</pre> </div>

<div class="slide"> <h2>接口</h2> <p> 接口類型是由一組方法定義的集合。 <p> 接口類型的值可以存放實現這些方法的任何值。

<pre class="source">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 = &amp;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 &lt; 0 { return float64(-f) } return float64(f) }

type Vertex struct { X, Y float64 }

func (v Vertex) Abs() float64 { return math.Sqrt(v.Xv.X + v.Y*v.Y) }</pre> </div>

<div class="slide"> <h2>接口</h2> <p> 類型通過實現那些方法來實現接口。 <p> <i>沒有顯式聲明的必要。</i> <p> 隱式接口解藕了實現接口的包和定義接口的包:互不依賴。 <p> 因此,也就無需在每一個實現上增加新的接口名稱,這樣同時也鼓勵了明確的接口定義。(對比其他語言) <p> <a target="_blank" href=" io</a> 定義了 <code>Reader</code> 和 <code>Writer</code>;不一定要這么做。 <pre class="source">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")

}</pre> </div>

<div class="slide"> <h2>錯誤</h2> <p> 錯誤是可以用字符串描述自己的任何東西。 主要思路是由預定義的內建接口類型 <code>error</code>,和其返回返回字符串竄的方法 <code>Error</code> 構成。 <pre>type error interface { Error() string }</pre>

<p>
當用 <code>fmt</code> 包的多種不同的打印函數輸出一個 <code>error</code> 
時,會自動的調用該方法。

<pre class="source">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 &amp;MyError{ time.Now(), "it didn't work", } }

func main() { if err := run(); err != nil { fmt.Println(err) } }</pre> </div>

<div class="slide"> <h2>Web 服務器</h2> <p> <a target="_blank" href=" http</a> 通過任何實現了 <code>http.Handler</code> 的值來響應 HTTP 請求: <pre>package http

type Handler interface { ServeHTTP(w ResponseWriter, r *Request) }</pre> <p> 在這個例子中,類型 <code>Hello</code> 實現了 <code>http.Handler</code>。 <p> <span class="localMode"> 訪問 <a href="http://localhost:4000/" target="_blank">http://localhost:4000/</a&gt; 會看到來自程序的問候。 </span> <span class="appengineMode"> <b>注意:</b> 這個例子無法在基于 web 的指南用戶界面運行。為了嘗試編寫 web 服務器,可能需要<a target="_blank" href=" Go</a>。 </span> <pre class="source">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) }</pre> </div>

<div class="slide"> <h2>圖片</h2> <p> <a target="_blank" href=" image</a> 定義了 <code>Image</code> 接口: <pre>package image

type Image interface { ColorModel() color.Model Bounds() Rectangle At(x, y int) color.Color }</pre> <p> (參閱<a target="_blank" href=" <p> 同樣,<code>color.Color</code> 和 <code>color.Model</code> 也是接口,但是通常因為直接使用預定義的實現 <code>image.RGBAColor</code> 和 <code>image.RGBAColorModel</code> 而忽略它們。

<pre class="source">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()) }</pre> </div>

<div class="slide"> <h2>練習:錯誤</h2> <p> 從之前的練習中復制 <code>Sqrt</code> 函數,并修改使其返回 <code>error</code> 值。 <p> <code>Sqrt</code> 接收到一個負數時,應當返回一個非 nil 的錯誤值。復數同樣也不被支持。 <p> 創建一個新類型 <pre>type ErrNegativeSqrt float64</pre> <p> 使其成為一個 <code>error</code>,通過 <pre>func (e ErrNegativeSqrt) Error() string</pre> <p> 方法就可以讓 <code>ErrNegativeSqrt(-2).Error()</code> 返回 <code>"cannot Sqrt negative number: -2"</code>。 <p> <b>注意:</b> 在 <code>Error</code> 方法內調用 <code>fmt.Print(e)</code> 將會讓程序陷入死循環。 可以通過先轉換 <code>e</code> 來避免這個: <code>fmt.Print(float64(e))</code>。為什么? <p> 修改 <code>Sqrt</code> 函數,使其接受一個負數時,返回 <code>ErrNegativeSqrt</code> 值。 <pre class="source">package main

import ( "fmt" )

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

func main() { fmt.Println(Sqrt(2)) fmt.Println(Sqrt(-2)) }</pre> </div>

<div class="slide localMode"> <h2>練習:HTTP 處理</h2> <p> 實現下面的類型,并在其上定義 ServeHTTP 方法。 在 web 服務器中注冊它們來處理指定的路徑。 <pre>type String string

type Struct struct { Greeting string Punct string Who string }</pre> <p> 例如,可以使用如下方式注冊處理方法: <pre>http.Handle("/string", String("I'm a frayed knot.")) http.Handle("/struct", &amp;Struct{"Hello", ":", "Gophers!"})</pre> <pre class="source">package main

import ( "net/http" )

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

<div class="slide"> <h2>練習:圖片</h2> <p> 還記得之前編寫的圖片生成器嗎? 現在來編寫另一個,不過這次將會返回一個 <code>image.Image</code> 的實現來代替 slice 的數據。 <p> 自定義的 <code>Image</code> 類型,要實現<a href="

<pre class="source">package main

import ( "image" "<span class="appengineMode">tour</span><span class="localMode">bitbucket.org/mikespook/go-tour-zh</span>/pic" )

type Image struct{}

func main() { m := Image{} pic.ShowImage(m) }</pre> </div>

<div class="slide"> <h2>練習:Rot13 讀取器</h2> <p> 一般的模式是 <a target="_blank" href="; 包裹另一個 <code>io.Reader</code>,用某些途徑修改特定的流。 <p> 例如, <a target="_blank" href="; 函數輸入一個 <code>io.Reader</code>(gzip 的數據流)并且返回一個同樣實現了 <code>io.Reader</code> 的 <code>*gzip.Reader</code>(解壓縮后的數據流)。 <p> 實現一個實現了 <code>io.Reader</code> 的 <code>rot13Reader</code>,用 <a target="_blank" href="; 修改數據流中的所有的字母進行密文替換。 <p> <code>rot13Reader</code> 已經提供。通過實現其 <code>Read</code> 方法使得它匹配 <code>io.Reader</code>。 <pre class="source">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, &amp;r) }</pre> </div>

<div class="toc">并發</div>

<div class="slide nocode"> <h2>并發</h2> </div>

<div class="slide"> <h2>Goroutine</h2> <p> <i>goroutine</i> 是由 Go 運行時環境管理的輕量級線程。 <pre>go f(x, y, z)</pre> <p> 開啟一個新的 goroutine 執行 <pre>f(x, y, z)</pre> <p> <code>f</code>,<code>x</code>,<code>y</code> 和 <code>z</code> 是當前 goroutine 中定義的,但是在新的 goroutine 中運行 <code>f</code>。 <p> goroutine 在相同的地址空間中運行,因此訪問共享內存必須進行同步。 <code><a href="

import ( "fmt" "<span class="appengineMode">runtime</span><span class="localMode">time</span>" )

func say(s string) { for i := 0; i &lt; 5; i++ { <span class="appengineMode">runtime.Gosched()</span><span class="localMode">time.Sleep(100 * time.Millisecond)</span> fmt.Println(s) } }

func main() { go say("world") say("hello") }</pre> </div>

<div class="slide"> <h2>Channel</h2>

    <p>
    channel 是有類型的管道,可以用 channel 操作符 <code>&lt;-</code> 對其發送或者接收值。

<pre>ch &lt;- v // 將 v 送入 channel ch。 v := &lt;-ch // 從 ch 接收,并且賦值給 v。</pre> <p> (“箭頭”就是數據流的方向。)

<p>
    和 map 與 slice 一樣,channel 使用前必須創建:

<pre>ch := make(chan int)</pre>

<p>
    默認情況下,在另一端準備好之前,發送和接收都會阻塞。這使得 
    goroutine 可以在沒有明確的鎖或競態變量的情況下進行同步。

<pre class="source">package main

import "fmt"

func sum(a []int, c chan int) { sum := 0 for _, v := range a { sum += v } c &lt;- 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 := &lt;-c, &lt;-c  // 從 c 中接收

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

}</pre> </div>

<div class="slide"> <h2>緩沖 channel</h2>

<p>
    channel 可以是<i>帶緩沖的</i>。為 <code>make</code> 提供第二個參數作為緩沖長度來初始化一個緩沖 channel:

<pre>ch := make(chan int, 100)</pre>

<p>
    向緩沖 channel 發送數據的時候,只有在緩沖區滿的時候才會阻塞。當緩沖區清空的時候接受阻塞。

    <p>
    修改例子使得緩沖區被填滿,然后看看會發生什么。

<pre class="source">package main

import "fmt"

func main() { c := make(chan int, 2) c &lt;- 1 c &lt;- 2 fmt.Println(&lt;-c) fmt.Println(&lt;-c) }</pre> </div>

<div class="slide"> <h2>Range 和 Close</h2> <p> 發送者可以 <code>close</code> 一個 channel 來表示再沒有值會被發送了。接收者可以通過賦值語句的第二參數來測試 channel 是否被關閉:當沒有值可以接收并且 channel 已經被關閉,那么 <pre>v, ok := &lt;-ch</pre> <p> <code>ok</code> 會被設置為 <code>false</code>。 <p> 循環 <code>for i := range c</code> 會不斷從 channel 接收值,直到它被關閉。 <p> <b>注意:</b> 只有發送者才能關閉 channel,而不是接收者。向一個已經關閉的 channel 發送數據會引起 panic。 <p> <b>還要注意</b>:channel 與文件不同;通常情況下無需關閉它們。只有在需要告訴接收者沒有更多的數據的時候才有必要進行關閉,例如中斷一個 <code>range</code>。 <pre class="source">package main

import ( "fmt" )

func fibonacci(n int, c chan int) { x, y := 0, 1 for i := 0; i &lt; n; i++ { c &lt;- 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) } }</pre> </div>

<div class="slide"> <h2>Select</h2> <p> <code>select</code> 語句使得一個 goroutine 在多個通訊操作上等待。 <p> <code>select</code> 會阻塞,直到條件分支中的某個可以繼續執行,這時就會執行那個條件分支。當多個都準備好的時候,會隨機選擇一個。 <pre class="source">package main

import "fmt"

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

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

<div class="slide"> <h2>默認選擇</h2> <p> 當 <code>select</code> 中的其他條件分支都沒有準備好的時候,<code>default</code> 分支會被執行。 <p> 為了非阻塞的發送或者接收,可使用 <code>default</code> 分支: <pre>select { case i := &lt;-c: // use i default: // receiving from c would block }</pre> <p> <span class="appengineMode"> <b>注意:</b> 這個例子無法在指南的基于 web 的用戶界面中運行,因為沙盒環境是沒有定時器概念的。需要<a target="_blank" href=" Go</a> 來了解這個例子如何運行。 </span> <pre class="source">package main

import ( "fmt" "time" )

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

<div class="slide nocode"> <h2>練習:等價二叉樹</h2> <p> 可以用多種不同的二叉樹的葉子節點存儲相同的數列值。例如,這里有兩個二叉樹保存了序列 1,1,2,3,5,8,13。 <img src="static/fig4.png" alt="二叉樹"> <p> 用于檢查兩個二叉樹是否存儲了相同的序列的函數在多數語言中都是相當復雜的。這里將使用 Go 的并發和 channel 來編寫一個簡單的解法。 <p> 這個例子使用了 <code>tree</code> 包,定義了類型: <pre>type Tree struct { Left Tree Value int Right Tree }</pre> </div>

<div class="slide"> <h2>練習:等價二叉樹</h2> <p> <b>1.</b> 實現 <code>Walk</code> 函數。 <p> <b>2.</b> 測試 <code>Walk</code> 函數。 <p> 函數 <code>tree.New(k)</code> 構造了一個隨機結構的二叉樹,保存了值 <code>k</code>,<code>2k</code>,<code>3k</code>,...,<code>10k</code>。 <p> 創建一個新的 channel <code>ch</code> 并且對其進行步進: <pre>go Walk(tree.New(1), ch)</pre> <p> 然后從 channel 中讀取并且打印 10 個值。應當是值 1,2,3,...,10。 <p> <b>3.</b> 用 <code>Walk</code> 實現 <code>Same</code> 函數來檢測是否 <code>t1</code> 和 <code>t2</code> 存儲了相同的值。 <p> <b>4.</b> 測試 <code>Same</code> 函數。 <p> <code>Same(tree.New(1), tree.New(1))</code> 應當返回 true,而 <code>Same(tree.New(1), tree.New(2))</code> 應當返回 false。

<pre class="source">package main

import "<span class="appengineMode">tour</span><span class="localMode">bitbucket.org/mikespook/go-tour-zh</span>/tree"

// Walk 步進 tree t 將所有的值從 tree 發送到 channel ch。 func Walk(t *tree.Tree, ch chan int)

// Same 檢測樹 t1 和 t2 是否含有相同的值。 func Same(t1, t2 *tree.Tree) bool

func main() { }</pre> </div>

<div class="slide"> <h2>練習:Web 爬蟲</h2> <p> 在這個練習中,將會使用 Go 的并發特性來并行執行 web 爬蟲。 <p> 修改 <code>Crawl</code> 函數來并行的抓取 URLs,并且保證不重復。 <pre class="source">package main

import ( "fmt" )

type Fetcher interface { // Fetch 返回 URL 的 body 內容,并且將在這個頁面上找到的 URL 放到一個 slice 中。 Fetch(url string) (body string, urls []string, err error) }

// Crawl 使用 fetcher 從某個 URL 開始遞歸的爬取頁面,直到達到最大深度。 func Crawl(url string, depth int, fetcher Fetcher) { // TODO: 并行的抓取 URL。 // TODO: 不重復抓取頁面。 // 下面并沒有實現上面兩種情況: if depth &lt;= 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("

// fakeFetcher 是返回若干結果的 Fetcher。 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 是填充后的 fakeFetcher。 var fetcher = &amp;fakeFetcher{ "

<div class="slide nocode"> <h2>Where to Go from here...</h2> <p class="appengineMode"> 可以從 <a href=" Go</a> 或者下載 <a href=" App Engine SDK</a>。 </p> <p> <span class="appengineMode">一旦在機器上安裝好了 Go,</span> <span class="localMode"></span> <a target="_blank" href=" Documentation</a>

<span class="appengineMode">是應當繼續閱讀的內容</span>
<span class="localMode">作為開始</span>。
    它包含了參考、指南、視頻等等更多資料。
<p>
    在標準庫上需要幫助的話,參考
<a target="_blank" >package
        reference</a>。語言本身的幫助,可以閱讀令人愉快的
<a target="_blank" >Language
Spec</a>。
<p>
進一步探索 Go 的并發模型,參閱
<a target="_blank" >Share Memory by Communicating</a>
的代碼之旅。
<p>
    <a target="_blank" >First Class Functions in Go</a>
    為 Go 的函數類型提供了有趣的展示。
<p>
<a target="_blank" >Go Blog</a> 有著眾多的關于 Go 的文章信息。
<p>
訪問 <a target="_blank" >golang.org</a> 了解更多內容。

</div>

</div><!-- end slides -->

<div id="workspace"> <div class="controls"> <div><a id="run" href="#run" title="編譯并運行">運行</a><a href="#more" id="more" title="選項">▼</a></div> <ul class="more"> <li><a href="#" id="reset">重置幻燈</a></li> <li><a href="#" id="format">格式化代碼</a></li> <li><a href="#" id="kill" class="localMode">終止程序</a></li> <li><hr></li> <li><a href="#" id="togglesyntax">語法高亮:關閉</a></li> <li><a href="#" id="togglelineno">行號:開啟</a></li> </ul> </div>

<div id="workspace-top">
    <div id="workspace-editor">
        <textarea id="editor" spellcheck="false"></textarea>
    </div>
</div>

<div id="workspace-bottom">
    <div id="output"></div>
</div>

</div> </div>

</body> </html></pre>

 本文由用戶 灬猜想灬 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!