SlideShare a Scribd company logo
1 of 82
Download to read offline
meebox@gmail.com
請實際上網搭配本投影片使用
http://tour.golang.org
package main
import (
"fmt"
"math/rand"
)
func main() {
fmt.Println("My favorite number is", rand.Intn(10))
}
程式由 main package 執行
匯入要使用的 package
import "fmt"
import "math/rand"
也可
寫成
參數 說明
%f 用十進位小數表示浮點數
%e 用科學記號表示浮點數
%E 同上, 用大寫 E 表示科學記號
%g 取 %e 與 %f 中較簡短的結果
%G 取 %E 與 %f 中較簡短的結果
%T 顯示資料型別
%v 以型別預設的格式顯示資料值
%s 字串
%d 十進位整數
%q 為字串加上雙引號及適當跳脫字元
package main
import (
"fmt"
"math"
)
func main() {
fmt.Println(math.Pi)
}
package 中大寫字首的名稱
會自動公開給外部使用
package main
import "fmt"
func add(x int, y int) int {
return x + y
}
func main() {
fmt.Println(add(42, 13))
}
函式宣告
型別都在名稱之後
函式的傳回值型別一
樣是放在函式名稱後
package main
import "fmt"
func add(x, y int) int {
return x + y
}
func main() {
fmt.Println(add(42, 13))
}
同時宣告兩個 int 型別的參數
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)
}
傳回兩個值
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))
}
具名的結果值參數
空的 return 會傳回具名
結果值參數的目前值
package main
import "fmt"
var i int
var c, python, java bool
func main() {
fmt.Println(i, c, python, java)
}
變數宣告 (型別一樣在名稱後面)
package main
import "fmt"
var i, j int = 1, 2
var c, python, java = true, false, "no!"
func main() {
fmt.Println(i, j, c, python, java)
}
宣告時可直接設定初始值
有設定初值時可省略型別
package main
import "fmt"
func main() {
var i, j int = 1, 2
k := 3
c, python, java := true, false, "no!"
fmt.Println(i, j, k, c, python, java)
}
使用 := 可省略 var , 並依照初值自動選用型別
package main
import (
"fmt"
"math/cmplx"
)
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)
}
int8 int16 int32 int64
uint8 uint16 uint32 uint64
float32 float64
complex64 complex128
package main
import (
"fmt"
"math"
)
func main() {
var x, y int = 3, 4
var f float64 = math.Sqrt(float64(3*3 + 4*4))
var z int = int(f)
fmt.Println(x, y, z)
}
T() 可轉型到 T
不同型別的運算一定要強制轉型
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)
}
宣告常數
常數不能用 := 運算器
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))
}
常數會隨語境自動選用適當的型別
如果執行 needInt(Big) 就會發生溢位
package main
import "fmt"
func main() {
sum := 0
for i := 0; i < 10; i++ {
sum += i
}
fmt.Println(sum)
}
基本用法如同 C 中的 for
但要注意不用加括號
package main
import "fmt"
func main() {
sum := 1
for sum < 1000 {
sum += sum
}
fmt.Println(sum)
}
package main
import "fmt"
func main() {
sum := 1
for ; sum < 1000; {
sum += sum
}
fmt.Println(sum)
}
省略迴圈條件判斷前、後的敘述 此時連分號都可以省略
package main
func main() {
for {
}
}
什麼都省略
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))
}
條件判斷式不用括號
一定要加大括號
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),
)
}
條件判斷式不用括號
v 只在 if 區塊中有效
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 >= %gn", v, lim)
}
return lim
}
func main() {
fmt.Println(
pow(3, 2, 10),
pow(3, 3, 20),
)
}
v 在 else 區塊中也有效
z = 𝑧 −
𝑧2 − 𝑥
2𝑧
package main
import (
"fmt"
)
func Sqrt(x float64) float64 {
z := 1.0
for i := 0;i < 10;i++ {
z = z - (z*z - x)/(2*z)
}
return z
}
func main() {
fmt.Println(Sqrt(2))
}
……
func Sqrt(x float64) (z float64,i int) {
z = 1.0
preZ := 0.0
for i = 0 ;math.Abs(z - preZ) > 0.000000001;i++ {
preZ = z
z = z - (z*z - x)/(2*z)
}
return
}
func main() {
fmt.Println(Sqrt(2))
}
z = 𝑧 −
𝑧2 − 𝑥
2𝑧
package main
import "fmt"
type Vertex struct {
X int
Y int
}
func main() {
v := Vertex{1, 2}
v.X = 4
fmt.Println(v.X)
}
Vertex 是新的型別
每個 Vertex 資料是一個結構體
結構體就是一組
資料欄位的集合
個別欄位使用 .
運算器存取 結構體使用大括號設定初值
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)
}
可宣告指向結
構體的指位器 使用指位器時會自動存取
(Transparent) 所指向的結構
體, 所以沒有 C 的指位器運算
package main
import "fmt"
type Vertex struct {
X, Y int
}
var (
p = Vertex{1, 2} // 以字面值建立 Vertex
q = &Vertex{1, 2} // 宣告指向 Vertex 的指位器
r = Vertex{X: 1} // 只指定 x 初值, Y:0 用預設值
s = Vertex{} // X:0 與 Y:0 都是預設值
)
func main() {
fmt.Println(p, q, r, s)
}
會顯示 &{1,2}
package main
import "fmt"
type Vertex struct {
X, Y int
}
func main() {
v := new(Vertex)
fmt.Println(v) // &{0,0}
v.X, v.Y = 11, 9
fmt.Println(v) // &{11,9}
}
也可寫成 var v = new(Vertex)
package main
import "fmt"
func main() {
var a [2]string
a[0] = "Hello"
a[1] = "World"
fmt.Println(a[0], a[1])
fmt.Println(a)
}
陣列長度要是常數
不可更改
不可設定初值
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])
// 省略起始索引值時預設為 0
fmt.Println("p[:3] ==", p[:3])
// 省略終點索引值時預設為 len(s)
fmt.Println("p[4:] ==", p[4:])
}
從 p[1] 到 p[3]
p[1:1] -> []
p[1,2] -> [3]
以字面值建立
整數陣列
取得指向後面字面
值陣列的切片
package main
import "fmt"
func main() {
a := make([]int, 5)
printSlice("a", a) // [0,0,0,0,0], len:5, cap:5
b := make([]int, 0, 5)
printSlice("b", b) // [], len:0, cap:5
c := b[:2]
printSlice("c", c) // [0,0], len:2, cap:5
d := c[2:5]
printSlice("d", d) // [0,0,0],len:3,cap:3
}
func printSlice(s string, x []int) {
fmt.Printf("%s len=%d cap=%d %vn",
s, len(x), cap(x), x)
}
先建立 1 個含有 5 個 0 值的陣列,
傳回指向此陣列所有元素的切片
先建立 1 個含有 5 個 0 值的陣列,
傳回指向此陣列 0 個元素的切片
此 3 個切片都指向同一個陣列
切片起點到陣列最後元素的長度
切片本身
的長度
package main
import "fmt"
func main() {
var z []int
fmt.Println(z, len(z), cap(z))// [],0,0
if z == nil {
fmt.Println("nil!")
}
}
沒有元素的切片
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 = %dn", i, v)
}
} i 依序為 0,1,2,3,4…
v 依序為 1,2,4,8,16…
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("%dn", value)
}
}
省略元素變數
只使用索引值
不需要索引變數
時, 要寫 _
package main
import "code.google.com/p/go-tour/pic"
func Pic(dx, dy int) [][]uint8 {
var pic = make([][]uint8,dy)
for i := 0;i < dy;i++ {
pic[i] = make([]uint8,dx)
}
for i:=0;i<dy;i++ {
for j:=0;j<dx;j++ {
pic[i][j] = uint8(i * j);
}
}
return pic
}
func main() {
pic.Show(Pic)
}
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"])
}
以字串為索引鍵、個別
元素為 Vertex 的 map
map 一定要 make 才能用
否則 m 為 nil, 無法設值
……
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)
}
索引鍵:元素值
注意元素後要有逗號
用大括號括起來
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)
}
個別元素設定時省略 型別名稱
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"] // 讀取元素, 元素存在時 ok 為 true
fmt.Println("The value:", v, "Present?", ok)
}
package main
import (
"code.google.com/p/go-tour/wc"
"strings"
)
func WordCount(s string) map[string]int {
wc := make(map[string]int)
for _,w := range strings.Fields(s) {
wc[w] = wc[w] + 1;
}
return wc
}
func main() {
wc.Test(WordCount)
}
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))
}
將定義好的函式設定給變數
package main
import "fmt"
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
叫用 adder 會把當時的 sum
變數封裝一份到傳回的函式中
func main() {
pos, neg := adder(), adder()
for i := 0; i < 10; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
}
}
pos 與 neg 各自擁有
一份專屬的 sum 變數
package main
import "fmt"
func fibonacci() func() int {
now, next := 0,1
return func() int {
now, next = next, now + next
return now
}
}
func main() {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Println(f())
}
}
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)
}
}
前置處理
……
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.")
}
}
不用 break
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.")
}
}
沒有用來當判斷條件的變數
package main
import (
"fmt"
"math/cmplx"
)
func Cbrt(x complex128) complex128 {
z := 1 + 0i
for i:= 0;i<10;i++ {
z = z - (z * z *z - x)/(3 * z * z)
}
return z
}
func main() {
fmt.Println(Cbrt(2))
fmt.Println(cmplx.Pow(2, 1.0/3)) // 驗證是否正確?
}
記得要用浮點數
z = 𝑧 −
𝑧3 − 𝑥
3𝑧2
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())
}
為 Vertex 定義方法
叫用 Vertex 的方法
……
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())
}
為 Vertex 定義方法
叫用 MyFloat 的方
法
……
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()) // &{15, 20}, 25
}
以指標形式為 Vertex 定義方法
會修改到 v 的內容
v 是指標, 所以
會修改到 v 所
指向的結構體
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)
}
……
type Abser interface {
Abs() float64
}
func main() {
var a Abser
f := MyFloat(-math.Sqrt2)
v := Vertex{3, 4}
a = f // MyFloat 是 Abser
a = &v // *Vertex 是 Abser
a = v // Vertex 不是 Abser
fmt.Println(a.Abs())
}
type MyFloat float64
無法成功編譯
相符的方法
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, writern")
}
實作介面並不需要任何宣告
實作介面的型別和介面定義所在的 package
不同, 兩者完全不相依賴, 可更正確定義抽象
化的介面
func run() error {
return &MyError{
time.Now(),
"it didn't work",
}
}
func main() {
if err := run(); err != nil {
fmt.Println(err)
}
}
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)
}
實作了
內建的
Println 遇到 error 介面時
會自動叫用介面中的 Error() 方法
type error interface {
Error() string
}
func Sqrt(f float64) (
float64, error) {
if f >= 0 {
z := 1.0
for i := 0;i < 10;i++ {
z = z –
(z*z - f)/(2*z)
}
return z, nil
}
var e ErrNegativeSqrt =
ErrNegativeSqrt(f)
return 0, e
}
package main
import "fmt"
type ErrNegativeSqrt float64
func (e ErrNegativeSqrt)
Error() string {
return fmt.Sprintf(
"無法求 %f 的平方根", e)
}
func main() {
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
}
實作 error 介面
傳回 error 介面
http package
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)
}
實作 net/http package中
所定義的 Handler 介面
type Handler interface {
ServeHTTP(
w ResponseWriter,
r *Request)
}
func (s *Struct) ServeHTTP(
w http.ResponseWriter,
r *http.Request) {
fmt.Fprint(w,
s.Greeting+s.Punct+s.Who)
}
func main() {
http.Handle("/string",
String("I'm a frayed knot."))
http.Handle("/struct",
&Struct{"Hello", ":",
"Gophers!"})
http.ListenAndServe(
"localhost:4000", nil)
}
package main
import "net/http"
import "fmt"
type String string
type Struct struct {
Greeting string
Punct string
Who string
}
func (s String) ServeHTTP(
w http.ResponseWriter,
r *http.Request) {
fmt.Fprint(w, s)
}
實作 Handler 介面
註冊處理路徑 已註冊處理個別路徑
不需再提供 Handler
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())
}
實作有 image 中所
定義的 Image 介面
type Image interface {
ColorModel() color.Model
Bounds() Rectangle
At(x, y int) color.Color
}
package main
import (
"code.google.com/p/go-tour/pic"
"image"
"image/color"
)
type Image struct{}
func (i Image) ColorModel()
color.Model {
return color.RGBAModel
}
func (i Image) Bounds()
image.Rectangle {
return image.Rect(0,0,200,200)
}
func (i Image) At(x, y int)
color.Color {
v := uint8(x * y)
return color.RGBA{
v, v, 255, 255
}
}
func main() {
m := Image{}
pic.ShowImage(m)
}
實作 Image 介面
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)
}
func (rot rot13Reader)
Read(p []byte)(n int, e error) {
n,e = rot.r.Read(p)
if n > 0 {
for i := 0;i < n;i++ {
switch {
case p[i] <= 'M':
p[i] += 13
case p[i] <= 'Z':
p[i] -= 13;
case p[i] <= 'm':
p[i] += 13
case p[i] <= 'z':
p[i] -= 13;
}
}
}
return n,e
}
實作 Reader 介面
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
ain(time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func m) {
go say("world")
say("hello")
} 以另一個執行緒執行 say
hello
world
hello
world
hello
world
hello
world
hello
執行結果 (咦?少個 world?)
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
fmt.Println(x, y, x+y)
}
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
}
① 建立傳遞
int 的通道
③ 等待通道有空位後
將加總值傳入通道
④ 等待通道有資料後
一一取出加總值
② 提供通道
給 gotoutine
package main
import "fmt"
func main() {
c := make(chan int, 2)
c <- 1
c <- 2
fmt.Println(<-c)
fmt.Println(<-c)
}
設定通道暫存區大小
等待通道中有資料時讀取等待通道有空位時送入資料
func main() {
c := make(chan int, 10)
go fibonacci(cap(c), c)
for i := range c {
fmt.Println(i)
}
}
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)
}
從通道依序取出資料, 直到
通道清空且傳送端關閉通道
只有傳送端可以關閉通道
若通道清空且傳送端關
閉通道時 ok 為 false
v, ok := <-ch
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)
}
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
}
}
}
專門用在通訊的 switch
多個 case 同時成立
時會隨機挑選執行
……
import "fmt"
import "time"
func main() {
tick := time.Tick(100 * time.Millisecond)
boom := time.After(500 * time.Millisecond)
for {
select {
case <-tick:
fmt.Println("tick.")
case <-boom:
fmt.Println("BOOM!")
return
default:
fmt.Println(" .")
time.Sleep(50 * time.Millisecond)
}
}
}
沒有可用通道時執行
在此區塊嘗試從 tick 或是 boom
通道讀取資料就會進入等待狀態
func Same(t1, t2 *tree.Tree) bool {
ch1 := make(chan int)
ch2 := make(chan int)
go Walk(t1,ch1)
go Walk(t2,ch2)
for i := 0;i <10;i++ {
if <-ch1 != <-ch2 {
return false
}
}
return true
}
func main() {
fmt.Println(Same(
tree.New(1),
tree.New(1)))
}
package main
import (
"code.google.com/p/go-
tour/tree"
"fmt"
)
func Walk(t *tree.Tree,
ch chan int) {
if t != nil {
Walk(t.Left, ch)
ch <- t.Value
Walk(t.Right, ch)
} else {
return
}
}
http://golang.org/
http://golang.org/pkg/
http://golang.org/cmd/
http://golang.org/pkg/fmt/
http://golang.org/pkg/os/
var crawledURLs = map[string]bool {} // 記錄已抓取網頁
func CrawlThis(url string, fetcher Fetcher,
foundURLs chan []string) { // 抓取網頁的 goroutine
body, urls, err := fetcher.Fetch(url) // 抓取網頁
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("found: %s %qn", url, body)
}
crawledURLs[url] = true; // 登記此網頁已抓取
foundURLs <- urls // 將此網頁上的連結陣列傳入通道
}
func Crawl(url string, depth int, fetcher Fetcher) {
foundURLs := make(chan []string) // 傳遞網頁內連結陣列的通道
go CrawlThis(url, fetcher, foundURLs) // 啟動 goroutine
Crawlers := 1 // 目前啟動了 1 個 goroutine
for i := 1;i < depth;i++ { // 依據 depth 控制抓取層數
n := Crawlers // 依 goroutine 數從通道取出傳回的連結陣列
for Crawlers = 0;n > 0;n-- { // 將 goroutine 數歸零
urls := <- foundURLs //從通道取出傳回的連結陣列
for _, u := range urls { // 從陣列中一一取出連結
if _, crawled := crawledURLs[u]; !crawled {
go CrawlThis(u, fetcher, foundURLs)
Crawlers++
}
}
}
}
return
}
如果此連結尚未抓取過
啟動 coroutine 抓取網頁
遞增 goroutine
數
A Tour of Go 學習筆記

More Related Content

What's hot (20)

C程式-函式與巨集
C程式-函式與巨集C程式-函式與巨集
C程式-函式與巨集
 
Ch5
Ch5Ch5
Ch5
 
C程式-陣列與指標
C程式-陣列與指標C程式-陣列與指標
C程式-陣列與指標
 
Ch10 範例
Ch10 範例Ch10 範例
Ch10 範例
 
Ch11 教學
Ch11 教學Ch11 教學
Ch11 教學
 
Ppt 138-142
Ppt 138-142Ppt 138-142
Ppt 138-142
 
Python入門:5大概念初心者必備
Python入門:5大概念初心者必備Python入門:5大概念初心者必備
Python入門:5大概念初心者必備
 
Python入門:5大概念初心者必備 2021/11/18
Python入門:5大概念初心者必備 2021/11/18Python入門:5大概念初心者必備 2021/11/18
Python入門:5大概念初心者必備 2021/11/18
 
Ch4 教學
Ch4 教學Ch4 教學
Ch4 教學
 
Ppt 1-50
Ppt 1-50Ppt 1-50
Ppt 1-50
 
Sym py edu
Sym py eduSym py edu
Sym py edu
 
Ch6 教學
Ch6 教學Ch6 教學
Ch6 教學
 
Ch9
Ch9Ch9
Ch9
 
Ch12 範例
Ch12 範例Ch12 範例
Ch12 範例
 
Ch11
Ch11Ch11
Ch11
 
Num py basic(2) - v01
Num py   basic(2) - v01Num py   basic(2) - v01
Num py basic(2) - v01
 
Ppt 127-135
Ppt 127-135Ppt 127-135
Ppt 127-135
 
Ch5 教學
Ch5 教學Ch5 教學
Ch5 教學
 
Ch2 教學
Ch2 教學Ch2 教學
Ch2 教學
 
Ch8 教學
Ch8 教學Ch8 教學
Ch8 教學
 

Viewers also liked

Fission Introduction
Fission IntroductionFission Introduction
Fission IntroductionTa Ching Chen
 
Linux Performance Analysis: New Tools and Old Secrets
Linux Performance Analysis: New Tools and Old SecretsLinux Performance Analysis: New Tools and Old Secrets
Linux Performance Analysis: New Tools and Old SecretsBrendan Gregg
 
Linux Systems Performance 2016
Linux Systems Performance 2016Linux Systems Performance 2016
Linux Systems Performance 2016Brendan Gregg
 
Broken Linux Performance Tools 2016
Broken Linux Performance Tools 2016Broken Linux Performance Tools 2016
Broken Linux Performance Tools 2016Brendan Gregg
 
BPF: Tracing and more
BPF: Tracing and moreBPF: Tracing and more
BPF: Tracing and moreBrendan Gregg
 
Velocity 2015 linux perf tools
Velocity 2015 linux perf toolsVelocity 2015 linux perf tools
Velocity 2015 linux perf toolsBrendan Gregg
 
Linux Profiling at Netflix
Linux Profiling at NetflixLinux Profiling at Netflix
Linux Profiling at NetflixBrendan Gregg
 

Viewers also liked (7)

Fission Introduction
Fission IntroductionFission Introduction
Fission Introduction
 
Linux Performance Analysis: New Tools and Old Secrets
Linux Performance Analysis: New Tools and Old SecretsLinux Performance Analysis: New Tools and Old Secrets
Linux Performance Analysis: New Tools and Old Secrets
 
Linux Systems Performance 2016
Linux Systems Performance 2016Linux Systems Performance 2016
Linux Systems Performance 2016
 
Broken Linux Performance Tools 2016
Broken Linux Performance Tools 2016Broken Linux Performance Tools 2016
Broken Linux Performance Tools 2016
 
BPF: Tracing and more
BPF: Tracing and moreBPF: Tracing and more
BPF: Tracing and more
 
Velocity 2015 linux perf tools
Velocity 2015 linux perf toolsVelocity 2015 linux perf tools
Velocity 2015 linux perf tools
 
Linux Profiling at Netflix
Linux Profiling at NetflixLinux Profiling at Netflix
Linux Profiling at Netflix
 

Similar to A Tour of Go 學習筆記 (20)

ncuma_SymPy符號運算套件.pptx
ncuma_SymPy符號運算套件.pptxncuma_SymPy符號運算套件.pptx
ncuma_SymPy符號運算套件.pptx
 
第4章函数
第4章函数第4章函数
第4章函数
 
实验一 Mathematica软件简介
实验一   Mathematica软件简介实验一   Mathematica软件简介
实验一 Mathematica软件简介
 
实验一 Mathematica软件简介
实验一   Mathematica软件简介实验一   Mathematica软件简介
实验一 Mathematica软件简介
 
Ch8
Ch8Ch8
Ch8
 
SymPy在微積分上的應用.ppt
SymPy在微積分上的應用.pptSymPy在微積分上的應用.ppt
SymPy在微積分上的應用.ppt
 
C語言標準輸出入函式
C語言標準輸出入函式C語言標準輸出入函式
C語言標準輸出入函式
 
Ch11 範例
Ch11 範例Ch11 範例
Ch11 範例
 
第5章数组
第5章数组第5章数组
第5章数组
 
Ch12
Ch12Ch12
Ch12
 
Ch7
Ch7Ch7
Ch7
 
Ch 6
Ch 6Ch 6
Ch 6
 
Python變數與資料運算
Python變數與資料運算Python變數與資料運算
Python變數與資料運算
 
Ch5
Ch5Ch5
Ch5
 
竞赛中C++语言拾遗
竞赛中C++语言拾遗竞赛中C++语言拾遗
竞赛中C++语言拾遗
 
Python元組,字典,集合
Python元組,字典,集合Python元組,字典,集合
Python元組,字典,集合
 
Python学习笔记
Python学习笔记Python学习笔记
Python学习笔记
 
香港六合彩
香港六合彩香港六合彩
香港六合彩
 
Fux8923
Fux8923Fux8923
Fux8923
 
香港六合彩
香港六合彩香港六合彩
香港六合彩
 

A Tour of Go 學習筆記

  • 2.
  • 3. package main import ( "fmt" "math/rand" ) func main() { fmt.Println("My favorite number is", rand.Intn(10)) } 程式由 main package 執行 匯入要使用的 package import "fmt" import "math/rand" 也可 寫成
  • 4. 參數 說明 %f 用十進位小數表示浮點數 %e 用科學記號表示浮點數 %E 同上, 用大寫 E 表示科學記號 %g 取 %e 與 %f 中較簡短的結果 %G 取 %E 與 %f 中較簡短的結果 %T 顯示資料型別 %v 以型別預設的格式顯示資料值 %s 字串 %d 十進位整數 %q 為字串加上雙引號及適當跳脫字元
  • 5. package main import ( "fmt" "math" ) func main() { fmt.Println(math.Pi) } package 中大寫字首的名稱 會自動公開給外部使用
  • 6.
  • 7. package main import "fmt" func add(x int, y int) int { return x + y } func main() { fmt.Println(add(42, 13)) } 函式宣告 型別都在名稱之後 函式的傳回值型別一 樣是放在函式名稱後
  • 8. package main import "fmt" func add(x, y int) int { return x + y } func main() { fmt.Println(add(42, 13)) } 同時宣告兩個 int 型別的參數
  • 9. 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) } 傳回兩個值
  • 10. 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)) } 具名的結果值參數 空的 return 會傳回具名 結果值參數的目前值
  • 11. package main import "fmt" var i int var c, python, java bool func main() { fmt.Println(i, c, python, java) } 變數宣告 (型別一樣在名稱後面)
  • 12. package main import "fmt" var i, j int = 1, 2 var c, python, java = true, false, "no!" func main() { fmt.Println(i, j, c, python, java) } 宣告時可直接設定初始值 有設定初值時可省略型別
  • 13. package main import "fmt" func main() { var i, j int = 1, 2 k := 3 c, python, java := true, false, "no!" fmt.Println(i, j, k, c, python, java) } 使用 := 可省略 var , 並依照初值自動選用型別
  • 14. package main import ( "fmt" "math/cmplx" ) 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) } int8 int16 int32 int64 uint8 uint16 uint32 uint64 float32 float64 complex64 complex128
  • 15. package main import ( "fmt" "math" ) func main() { var x, y int = 3, 4 var f float64 = math.Sqrt(float64(3*3 + 4*4)) var z int = int(f) fmt.Println(x, y, z) } T() 可轉型到 T 不同型別的運算一定要強制轉型
  • 16. 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) } 宣告常數 常數不能用 := 運算器
  • 17. 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)) } 常數會隨語境自動選用適當的型別 如果執行 needInt(Big) 就會發生溢位
  • 18.
  • 19. package main import "fmt" func main() { sum := 0 for i := 0; i < 10; i++ { sum += i } fmt.Println(sum) } 基本用法如同 C 中的 for 但要注意不用加括號
  • 20. package main import "fmt" func main() { sum := 1 for sum < 1000 { sum += sum } fmt.Println(sum) } package main import "fmt" func main() { sum := 1 for ; sum < 1000; { sum += sum } fmt.Println(sum) } 省略迴圈條件判斷前、後的敘述 此時連分號都可以省略
  • 21. package main func main() { for { } } 什麼都省略
  • 22. 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)) } 條件判斷式不用括號 一定要加大括號
  • 23. 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), ) } 條件判斷式不用括號 v 只在 if 區塊中有效
  • 24. 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 >= %gn", v, lim) } return lim } func main() { fmt.Println( pow(3, 2, 10), pow(3, 3, 20), ) } v 在 else 區塊中也有效
  • 25. z = 𝑧 − 𝑧2 − 𝑥 2𝑧 package main import ( "fmt" ) func Sqrt(x float64) float64 { z := 1.0 for i := 0;i < 10;i++ { z = z - (z*z - x)/(2*z) } return z } func main() { fmt.Println(Sqrt(2)) }
  • 26. …… func Sqrt(x float64) (z float64,i int) { z = 1.0 preZ := 0.0 for i = 0 ;math.Abs(z - preZ) > 0.000000001;i++ { preZ = z z = z - (z*z - x)/(2*z) } return } func main() { fmt.Println(Sqrt(2)) } z = 𝑧 − 𝑧2 − 𝑥 2𝑧
  • 27.
  • 28. package main import "fmt" type Vertex struct { X int Y int } func main() { v := Vertex{1, 2} v.X = 4 fmt.Println(v.X) } Vertex 是新的型別 每個 Vertex 資料是一個結構體 結構體就是一組 資料欄位的集合 個別欄位使用 . 運算器存取 結構體使用大括號設定初值
  • 29. 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) } 可宣告指向結 構體的指位器 使用指位器時會自動存取 (Transparent) 所指向的結構 體, 所以沒有 C 的指位器運算
  • 30. package main import "fmt" type Vertex struct { X, Y int } var ( p = Vertex{1, 2} // 以字面值建立 Vertex q = &Vertex{1, 2} // 宣告指向 Vertex 的指位器 r = Vertex{X: 1} // 只指定 x 初值, Y:0 用預設值 s = Vertex{} // X:0 與 Y:0 都是預設值 ) func main() { fmt.Println(p, q, r, s) } 會顯示 &{1,2}
  • 31. package main import "fmt" type Vertex struct { X, Y int } func main() { v := new(Vertex) fmt.Println(v) // &{0,0} v.X, v.Y = 11, 9 fmt.Println(v) // &{11,9} } 也可寫成 var v = new(Vertex)
  • 32.
  • 33. package main import "fmt" func main() { var a [2]string a[0] = "Hello" a[1] = "World" fmt.Println(a[0], a[1]) fmt.Println(a) } 陣列長度要是常數 不可更改 不可設定初值
  • 34. 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]) // 省略起始索引值時預設為 0 fmt.Println("p[:3] ==", p[:3]) // 省略終點索引值時預設為 len(s) fmt.Println("p[4:] ==", p[4:]) } 從 p[1] 到 p[3] p[1:1] -> [] p[1,2] -> [3] 以字面值建立 整數陣列 取得指向後面字面 值陣列的切片
  • 35. package main import "fmt" func main() { a := make([]int, 5) printSlice("a", a) // [0,0,0,0,0], len:5, cap:5 b := make([]int, 0, 5) printSlice("b", b) // [], len:0, cap:5 c := b[:2] printSlice("c", c) // [0,0], len:2, cap:5 d := c[2:5] printSlice("d", d) // [0,0,0],len:3,cap:3 } func printSlice(s string, x []int) { fmt.Printf("%s len=%d cap=%d %vn", s, len(x), cap(x), x) } 先建立 1 個含有 5 個 0 值的陣列, 傳回指向此陣列所有元素的切片 先建立 1 個含有 5 個 0 值的陣列, 傳回指向此陣列 0 個元素的切片 此 3 個切片都指向同一個陣列 切片起點到陣列最後元素的長度 切片本身 的長度
  • 36. package main import "fmt" func main() { var z []int fmt.Println(z, len(z), cap(z))// [],0,0 if z == nil { fmt.Println("nil!") } } 沒有元素的切片
  • 37.
  • 38. 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 = %dn", i, v) } } i 依序為 0,1,2,3,4… v 依序為 1,2,4,8,16…
  • 39. 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("%dn", value) } } 省略元素變數 只使用索引值 不需要索引變數 時, 要寫 _
  • 40. package main import "code.google.com/p/go-tour/pic" func Pic(dx, dy int) [][]uint8 { var pic = make([][]uint8,dy) for i := 0;i < dy;i++ { pic[i] = make([]uint8,dx) } for i:=0;i<dy;i++ { for j:=0;j<dx;j++ { pic[i][j] = uint8(i * j); } } return pic } func main() { pic.Show(Pic) }
  • 41.
  • 42. 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"]) } 以字串為索引鍵、個別 元素為 Vertex 的 map map 一定要 make 才能用 否則 m 為 nil, 無法設值
  • 43. …… 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) } 索引鍵:元素值 注意元素後要有逗號 用大括號括起來
  • 44. 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) } 個別元素設定時省略 型別名稱
  • 45. 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"] // 讀取元素, 元素存在時 ok 為 true fmt.Println("The value:", v, "Present?", ok) }
  • 46. package main import ( "code.google.com/p/go-tour/wc" "strings" ) func WordCount(s string) map[string]int { wc := make(map[string]int) for _,w := range strings.Fields(s) { wc[w] = wc[w] + 1; } return wc } func main() { wc.Test(WordCount) }
  • 47.
  • 48. 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)) } 將定義好的函式設定給變數
  • 49. package main import "fmt" func adder() func(int) int { sum := 0 return func(x int) int { sum += x return sum } } 叫用 adder 會把當時的 sum 變數封裝一份到傳回的函式中 func main() { pos, neg := adder(), adder() for i := 0; i < 10; i++ { fmt.Println( pos(i), neg(-2*i), ) } } pos 與 neg 各自擁有 一份專屬的 sum 變數
  • 50. package main import "fmt" func fibonacci() func() int { now, next := 0,1 return func() int { now, next = next, now + next return now } } func main() { f := fibonacci() for i := 0; i < 10; i++ { fmt.Println(f()) } }
  • 51.
  • 52. 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) } } 前置處理
  • 53. …… 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.") } } 不用 break
  • 54. 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.") } } 沒有用來當判斷條件的變數
  • 55. package main import ( "fmt" "math/cmplx" ) func Cbrt(x complex128) complex128 { z := 1 + 0i for i:= 0;i<10;i++ { z = z - (z * z *z - x)/(3 * z * z) } return z } func main() { fmt.Println(Cbrt(2)) fmt.Println(cmplx.Pow(2, 1.0/3)) // 驗證是否正確? } 記得要用浮點數 z = 𝑧 − 𝑧3 − 𝑥 3𝑧2
  • 56.
  • 57. 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()) } 為 Vertex 定義方法 叫用 Vertex 的方法
  • 58. …… 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()) } 為 Vertex 定義方法 叫用 MyFloat 的方 法
  • 59. …… 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()) // &{15, 20}, 25 } 以指標形式為 Vertex 定義方法 會修改到 v 的內容 v 是指標, 所以 會修改到 v 所 指向的結構體
  • 60. 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) } …… type Abser interface { Abs() float64 } func main() { var a Abser f := MyFloat(-math.Sqrt2) v := Vertex{3, 4} a = f // MyFloat 是 Abser a = &v // *Vertex 是 Abser a = v // Vertex 不是 Abser fmt.Println(a.Abs()) } type MyFloat float64 無法成功編譯 相符的方法
  • 61. 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, writern") } 實作介面並不需要任何宣告 實作介面的型別和介面定義所在的 package 不同, 兩者完全不相依賴, 可更正確定義抽象 化的介面
  • 62. func run() error { return &MyError{ time.Now(), "it didn't work", } } func main() { if err := run(); err != nil { fmt.Println(err) } } 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) } 實作了 內建的 Println 遇到 error 介面時 會自動叫用介面中的 Error() 方法 type error interface { Error() string }
  • 63. func Sqrt(f float64) ( float64, error) { if f >= 0 { z := 1.0 for i := 0;i < 10;i++ { z = z – (z*z - f)/(2*z) } return z, nil } var e ErrNegativeSqrt = ErrNegativeSqrt(f) return 0, e } package main import "fmt" type ErrNegativeSqrt float64 func (e ErrNegativeSqrt) Error() string { return fmt.Sprintf( "無法求 %f 的平方根", e) } func main() { fmt.Println(Sqrt(2)) fmt.Println(Sqrt(-2)) } 實作 error 介面 傳回 error 介面
  • 65. 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) } 實作 net/http package中 所定義的 Handler 介面 type Handler interface { ServeHTTP( w ResponseWriter, r *Request) }
  • 66. func (s *Struct) ServeHTTP( w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, s.Greeting+s.Punct+s.Who) } func main() { http.Handle("/string", String("I'm a frayed knot.")) http.Handle("/struct", &Struct{"Hello", ":", "Gophers!"}) http.ListenAndServe( "localhost:4000", nil) } package main import "net/http" import "fmt" type String string type Struct struct { Greeting string Punct string Who string } func (s String) ServeHTTP( w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, s) } 實作 Handler 介面 註冊處理路徑 已註冊處理個別路徑 不需再提供 Handler
  • 67.
  • 68. 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()) } 實作有 image 中所 定義的 Image 介面 type Image interface { ColorModel() color.Model Bounds() Rectangle At(x, y int) color.Color }
  • 69. package main import ( "code.google.com/p/go-tour/pic" "image" "image/color" ) type Image struct{} func (i Image) ColorModel() color.Model { return color.RGBAModel } func (i Image) Bounds() image.Rectangle { return image.Rect(0,0,200,200) } func (i Image) At(x, y int) color.Color { v := uint8(x * y) return color.RGBA{ v, v, 255, 255 } } func main() { m := Image{} pic.ShowImage(m) } 實作 Image 介面
  • 70. 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) } func (rot rot13Reader) Read(p []byte)(n int, e error) { n,e = rot.r.Read(p) if n > 0 { for i := 0;i < n;i++ { switch { case p[i] <= 'M': p[i] += 13 case p[i] <= 'Z': p[i] -= 13; case p[i] <= 'm': p[i] += 13 case p[i] <= 'z': p[i] -= 13; } } } return n,e } 實作 Reader 介面
  • 71.
  • 72. package main import ( "fmt" "time" ) func say(s string) { for i := 0; i < 5; i++ { ain(time.Sleep(100 * time.Millisecond) fmt.Println(s) } } func m) { go say("world") say("hello") } 以另一個執行緒執行 say hello world hello world hello world hello world hello 執行結果 (咦?少個 world?)
  • 73. 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 fmt.Println(x, y, x+y) } 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 } ① 建立傳遞 int 的通道 ③ 等待通道有空位後 將加總值傳入通道 ④ 等待通道有資料後 一一取出加總值 ② 提供通道 給 gotoutine
  • 74. package main import "fmt" func main() { c := make(chan int, 2) c <- 1 c <- 2 fmt.Println(<-c) fmt.Println(<-c) } 設定通道暫存區大小 等待通道中有資料時讀取等待通道有空位時送入資料
  • 75. func main() { c := make(chan int, 10) go fibonacci(cap(c), c) for i := range c { fmt.Println(i) } } 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) } 從通道依序取出資料, 直到 通道清空且傳送端關閉通道 只有傳送端可以關閉通道 若通道清空且傳送端關 閉通道時 ok 為 false v, ok := <-ch
  • 76. 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) } 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 } } } 專門用在通訊的 switch 多個 case 同時成立 時會隨機挑選執行
  • 77. …… import "fmt" import "time" func main() { tick := time.Tick(100 * time.Millisecond) boom := time.After(500 * time.Millisecond) for { select { case <-tick: fmt.Println("tick.") case <-boom: fmt.Println("BOOM!") return default: fmt.Println(" .") time.Sleep(50 * time.Millisecond) } } } 沒有可用通道時執行 在此區塊嘗試從 tick 或是 boom 通道讀取資料就會進入等待狀態
  • 78. func Same(t1, t2 *tree.Tree) bool { ch1 := make(chan int) ch2 := make(chan int) go Walk(t1,ch1) go Walk(t2,ch2) for i := 0;i <10;i++ { if <-ch1 != <-ch2 { return false } } return true } func main() { fmt.Println(Same( tree.New(1), tree.New(1))) } package main import ( "code.google.com/p/go- tour/tree" "fmt" ) func Walk(t *tree.Tree, ch chan int) { if t != nil { Walk(t.Left, ch) ch <- t.Value Walk(t.Right, ch) } else { return } }
  • 80. var crawledURLs = map[string]bool {} // 記錄已抓取網頁 func CrawlThis(url string, fetcher Fetcher, foundURLs chan []string) { // 抓取網頁的 goroutine body, urls, err := fetcher.Fetch(url) // 抓取網頁 if err != nil { fmt.Println(err) } else { fmt.Printf("found: %s %qn", url, body) } crawledURLs[url] = true; // 登記此網頁已抓取 foundURLs <- urls // 將此網頁上的連結陣列傳入通道 }
  • 81. func Crawl(url string, depth int, fetcher Fetcher) { foundURLs := make(chan []string) // 傳遞網頁內連結陣列的通道 go CrawlThis(url, fetcher, foundURLs) // 啟動 goroutine Crawlers := 1 // 目前啟動了 1 個 goroutine for i := 1;i < depth;i++ { // 依據 depth 控制抓取層數 n := Crawlers // 依 goroutine 數從通道取出傳回的連結陣列 for Crawlers = 0;n > 0;n-- { // 將 goroutine 數歸零 urls := <- foundURLs //從通道取出傳回的連結陣列 for _, u := range urls { // 從陣列中一一取出連結 if _, crawled := crawledURLs[u]; !crawled { go CrawlThis(u, fetcher, foundURLs) Crawlers++ } } } } return } 如果此連結尚未抓取過 啟動 coroutine 抓取網頁 遞增 goroutine 數