Download - A Tour of Go 學習筆記
使用 A Tour of Go 線上教學
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 中較簡短的結果
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 >= %g\n", v, lim)
}
return lim
}
func main() {
fmt.Println(
pow(3, 2, 10),
pow(3, 3, 20),
)
}
v 在 else 區塊中也有效
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))
}
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 %v\n",
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 = %d\n", 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("%d\n", 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)) // 驗證是否正確?}
記得要用浮點數
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, writer\n")
}
實作介面並不需要任何宣告
實作介面的型別和介面定義所在的 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
}
}