关于
最近刚开始接触Go语言,这里做一些记录,这不是一篇介绍Go语言特性的文章,也不是讲述实际应用的文章。
前言:
Go语言,作为Better C,目前适用领域多是在高并发的BE程序。成熟的产品有Docker(现在改名为Moby)等。设计思想上相比Cpp做加法,它是作减法。在整合新的语言特性基础上,改良了C一些不易理解的地方。作为静态语言,效率自不用说,可以方便地调用C的库。同时因为简洁的语法,开发效率很高。
Playground
https://tour.golang.org/moretypes/18
Exercise: Slices
Implement
Pic
. It should return a slice of lengthdy
, each element of which is a slice ofdx
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)/2
,x*y
, andx^y
.(You need to use a loop to allocate each
[]uint8
inside the[][]uint8
.)(Use
uint8(intValue)
to convert between types.)
这里尽量把之前学到的特性都用上


1 package main 2 3 import "golang.org/x/tour/pic" 4 5 func Pic(dx, dy int) [][]uint8 { 6 local := make([][]uint8,0,0) 7 for i:=0;i<dy;i++{ 8 local=append(local,make([]uint8,dx)) 9 } 10 for i,_:= range local{ 11 for j,_ :=range local[i]{ 12 local[i][j]=uint8((i+j)/2) 13 } 14 } 15 return local 16 } 17 18 func main() { 19 pic.Show(Pic) 20 }
这里一开始用local := make([][]uint8,dy)遇到一个out of range的panic,可能是测试脚本调用返回的二维数组取值时越界了。
这个语句已经生成了dy个空的类型为[]uint8的元素,之后只能用for循环依次赋值,而直接用append会在dy个元素之后再累加dy个一维数组。
https://tour.golang.org/moretypes/23
Exercise: Maps
Implement
WordCount
. It should return a map of the counts of each “word” in the strings
. Thewc.Test
function runs a test suite against the provided function and prints success or failure.You might find strings.Fields helpful.


1 package main 2 3 import ( 4 "golang.org/x/tour/wc" 5 "strings" 6 ) 7 8 func WordCount(s string) map[string]int { 9 stringArr:=strings.Fields(s) 10 localMap :=make(map[string]int) 11 for _,value:=range stringArr{ 12 _,ok:=localMap[value] 13 if ok { 14 localMap[value]++ 15 }else{ 16 localMap[value]=1 17 } 18 } 19 return localMap//map[string]int{"x": 1} 20 } 21 22 func main() { 23 wc.Test(WordCount) 24 }
不同于c++,这里用下标运算符查询元素并不会插入元素,所以可以安全作find查询,go语言的逻辑语句左花括号必须在同一行
https://tour.golang.org/moretypes/26
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 (0, 1, 1, 2, 3, 5, ...).


1 package main 2 3 import "fmt" 4 5 // fibonacci is a function that returns 6 // a function that returns an int. 7 func fibonacci() func() int { 8 before1:=0 9 before2:=1 10 count:=0 11 return func() int { 12 if(count==0){ 13 count++ 14 return 0 15 } 16 if(count==1){ 17 count++ 18 return 1 19 } 20 before1,before2=before2,before1+before2 21 return before2 22 } 23 } 24 25 func main() { 26 f := fibonacci() 27 for i := 0; i < 10; i++ { 28 fmt.Println(f()) 29 } 30 }
这里是用闭包来实现斐波那契数列,应该还可以简化一些,赋值预算语句左右似乎并不指向同一对象,可以把右侧当做一个副本
https://tour.golang.org/methods/18
Exercise: Stringers
Make the
IPAddr
type implementfmt.Stringer
to print the address as a dotted quad.
For instance,
IPAddr{1, 2, 3, 4}
should print as"1.2.3.4"
.


1 package main 2 3 import ( 4 "fmt" 5 "strconv" 6 "strings" 7 ) 8 9 type IPAddr [4]byte 10 11 // TODO: Add a "String() string" method to IPAddr. 12 func (ip IPAddr) String() string{ 13 s:=make([]string,len(ip)) 14 for i:=range ip{ 15 s[i] = strconv.Itoa(int(ip[i])) 16 } 17 return strings.Join(s,".") 18 } 19 func main() { 20 hosts := map[string]IPAddr{ 21 "loopback": {127, 0, 0, 1}, 22 "googleDNS": {8, 8, 8, 8}, 23 } 24 for name, ip := range hosts { 25 fmt.Printf("%v: %v\n", name, ip) 26 } 27 }
https://tour.golang.org/flowcontrol/8
Exercise: Loops and Functions
As a way to play with functions and loops, let's implement a square root function: given a number x, we want to find the number z for which z² is most nearly x.
Computers typically compute the square root of x using a loop. Starting with some guess z, we can adjust z based on how close z² is to x, producing a better guess:
z -= (z*z - x) / (2*z)Repeating this adjustment makes the guess better and better until we reach an answer that is as close to the actual square root as can be.
Implement this in the
func Sqrt
provided. A decent starting guess for z is 1, no matter what the input. To begin with, repeat the calculation 10 times and print each z along the way. See how close you get to the answer for various values of x (1, 2, 3, ...) and how quickly the guess improves.Hint: To declare and initialize a floating point value, give it floating point syntax or use a conversion:
z := 1.0 z := float64(1)Next, change the loop condition to stop once the value has stopped changing (or only changes by a very small amount). See if that's more or fewer than 10 iterations. Try other initial guesses for z, like x, or x/2. How close are your function's results to the math.Sqrt in the standard library?
(Note: If you are interested in the details of the algorithm, the z² − x above is how far away z² is from where it needs to be (x), and the division by 2z is the derivative of z², to scale how much we adjust z by how quickly z² is changing. This general approach is called Newton's method. It works well for many functions but especially well for square root.)


1 package main 2 3 import ( 4 "fmt" 5 ) 6 7 var( 8 z = float64(1) 9 ) 10 func Sqrt(x float64) float64 { 11 for i,z := 0,float64(1);i<100;i++ { 12 z-=(z*z-x)/2*z 13 fmt.Println(z); 14 } 15 return 0 16 } 17 18 func main() { 19 fmt.Println(Sqrt(2)) 20 }
https://tour.golang.org/methods/20
Exercise: Errors
Copy your
Sqrt
function from the earlier exercise and modify it to return anerror
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 float64and make it an
error
by giving it afunc (e ErrNegativeSqrt) Error() stringmethod such that
ErrNegativeSqrt(-2).Error()
returns"cannot Sqrt negative number: -2"
.Note: a call to
fmt.Sprint(e)
inside theError
method will send the program into an infinite loop. You can avoid this by convertinge
first:fmt.Sprint(float64(e))
. Why?Change your
Sqrt
function to return anErrNegativeSqrt
value when given a negative number.


1 package main 2 3 import ( 4 "fmt" 5 ) 6 7 type ErrNegativeSqrt float64 8 9 func (e ErrNegativeSqrt) Error() string { 10 return fmt.Sprint("cannot Sqrt negative number: ", float64(e)) //infinite call error? 11 } 12 13 func Sqrt(x float64) (float64, error) { 14 if x < 0 { 15 return 0, ErrNegativeSqrt(x) 16 } 17 18 for i, z := 0, float64(1); i < 100; i++ { 19 z -= (z*z - x) / 2 * z 20 fmt.Println(z) 21 } 22 return 0, nil 23 } 24 25 func main() { 26 if _, err := Sqrt(2); err != nil { 27 fmt.Println(err) 28 } 29 if _, err := Sqrt(-2); err != nil { 30 fmt.Println(err) 31 } 32 //fmt.Println(Sqrt(2)) 33 //fmt.Println(Sqrt(-2)) 34 }
https://tour.golang.org/methods/22
Exercise: Readers
Implement a
Reader
type that emits an infinite stream of the ASCII character'A'
.


1 package main 2 3 import ( 4 "golang.org/x/tour/reader" 5 ) 6 7 type MyReader struct{} 8 9 // TODO: Add a Read([]byte) (int, error) method to MyReader. 10 func (reader MyReader) Read(b []byte) (int, error) { 11 for i:=range b{ 12 b[i] = 65 13 } 14 return len(b),nil 15 } 16 func main() { 17 reader.Validate(MyReader{}) 18 }
https://tour.golang.org/methods/23
Exercise: rot13Reader
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 compressed data) and returns a*gzip.Reader
that also implementsio.Reader
(a stream of the decompressed data).Implement a
rot13Reader
that implementsio.Reader
and reads from anio.Reader
, modifying the stream by applying the rot13 substitution cipher to all alphabetical characters.The
rot13Reader
type is provided for you. Make it anio.Reader
by implementing itsRead
method.


1 package main 2 3 import ( 4 "io" 5 "os" 6 "strings" 7 ) 8 9 type rot13Reader struct { 10 r io.Reader 11 } 12 13 func (reader rot13Reader) Read(b []byte) (int, error) { 14 ret,err:=reader.r.Read(b) 15 return ret,err 16 } 17 18 func main() { 19 s := strings.NewReader("Lbh penpxrq gur pbqr!") 20 r := rot13Reader{s} 21 io.Copy(os.Stdout, &r) 22 }
https://tour.golang.org/methods/25
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 callpic.ShowImage
.
Bounds
should return aimage.Rectangle
, likeimage.Rect(0, 0, w, h)
.
ColorModel
should returncolor.RGBAModel
.
At
should return a color; the valuev
in the last picture generator corresponds tocolor.RGBA{v, v, 255, 255}
in this one.


1 package main 2 3 import ( 4 "golang.org/x/tour/pic" 5 "image" 6 "image/color" 7 ) 8 9 type Image struct{ 10 x int 11 y int 12 w int 13 h int 14 r uint8 15 g uint8 16 b uint8 17 a uint8 18 } 19 20 func (img Image) ColorModel() color.Model{ 21 return color.RGBAModel 22 } 23 func (img Image) Bounds() image.Rectangle{ 24 return image.Rect(img.x,img.y,img.w,img.h) 25 } 26 func (img Image) At(x, y int) color.Color{ 27 return color.RGBA{img.r,img.g,img.b,img.a} 28 } 29 func main() { 30 m := Image{0,0,200,200,20,50,30,128} 31 pic.ShowImage(m) 32 }
https://tour.golang.org/concurrency/7
Exercise: Equivalent Binary Trees
1 package main 2 3 import ( 4 "golang.org/x/tour/tree" 5 "fmt" 6 ) 7 8 // Walk walks the tree t sending all values 9 // from the tree to the channel ch. 10 func Walk(t *tree.Tree, ch chan int){ 11 if t!=nil{ 12 if t.Left != nil{ 13 Walk(t.Left,ch) 14 } 15 ch<-t.Value 16 if t.Right !=nil{ 17 Walk(t.Right,ch) 18 } 19 } 20 } 21 22 // Same determines whether the trees 23 // t1 and t2 contain the same values. 24 func Same(t1, t2 *tree.Tree) bool{ 25 ch1:=make(chan int) 26 ch2:=make(chan int) 27 go Walk(t1,ch1) 28 go Walk(t2,ch2) 29 for i:=0;i<10;i++{ 30 if (<-ch1) != (<-ch2){ 31 return false 32 } 33 } 34 return true 35 } 36 37 func main() { 38 ch:=make(chan int) 39 go Walk(tree.New(1),ch) 40 for i:=0;i<10;i++{ 41 fmt.Println(<-ch) 42 } 43 44 fmt.Printf("is tree.New(1) equal with tree.New(1)?%v\n",Same(tree.New(1),tree.New(1))) 45 fmt.Printf("is tree.New(1) equal with tree.New(2)?%v\n",Same(tree.New(1),tree.New(2))) 46 }
这里使用递归,查看Tree的实现代码,是把比根节点小的放在左节点,比它小的放在右节点
于是只要实现一个二叉树的中序遍历即可顺序输出
https://tour.golang.org/concurrency/10
Exercise: Web Crawler


1 package main 2 3 import ( 4 "fmt" 5 "sync" 6 ) 7 8 type Fetcher interface { 9 // Fetch returns the body of URL and 10 // a slice of URLs found on that page. 11 Fetch(url string) (body string, urls []string, err error) 12 } 13 14 // Crawl uses fetcher to recursively crawl 15 // pages starting with url, to a maximum of depth. 16 func Crawl(url string, depth int, fetcher Fetcher, c *urlSync, wg *sync.WaitGroup) { 17 // TODO: Fetch URLs in parallel. 18 // TODO: Don't fetch the same URL twice. 19 // This implementation doesn't do either: 20 defer wg.Done() 21 if depth <= 0 { 22 return 23 } 24 25 c.mux.Lock() 26 if _, ok := c.v[url]; ok { 27 c.mux.Unlock() 28 return 29 } 30 c.v[url] = 1 31 body, urls, err := fetcher.Fetch(url) 32 c.mux.Unlock() 33 34 if err != nil { 35 fmt.Println(err) 36 return 37 } 38 fmt.Printf("found: %s %q\n", url, body) 39 40 for _, u := range urls { 41 wg.Add(1) 42 go Crawl(u, depth-1, fetcher, c, wg) 43 } 44 return 45 } 46 47 func main() { 48 urlCache := urlSync{v: make(map[string]int)} 49 var wg sync.WaitGroup 50 wg.Add(1) 51 go Crawl("http://golang.org/", 4, fetcher, &urlCache, &wg) 52 wg.Wait() 53 } 54 55 // fakeFetcher is Fetcher that returns canned results. 56 type fakeFetcher map[string]*fakeResult 57 58 //url cache 59 type urlSync struct { 60 v map[string]int 61 mux sync.Mutex 62 } 63 64 type fakeResult struct { 65 body string 66 urls []string 67 } 68 69 func (f fakeFetcher) Fetch(url string) (string, []string, error) { 70 if res, ok := f[url]; ok { 71 return res.body, res.urls, nil 72 } 73 return "", nil, fmt.Errorf("not found: %s", url) 74 } 75 76 // fetcher is a populated fakeFetcher. 77 var fetcher = fakeFetcher{ 78 "http://golang.org/": &fakeResult{ 79 "The Go Programming Language", 80 []string{ 81 "http://golang.org/pkg/", 82 "http://golang.org/cmd/", 83 }, 84 }, 85 "http://golang.org/pkg/": &fakeResult{ 86 "Packages", 87 []string{ 88 "http://golang.org/", 89 "http://golang.org/cmd/", 90 "http://golang.org/pkg/fmt/", 91 "http://golang.org/pkg/os/", 92 }, 93 }, 94 "http://golang.org/pkg/fmt/": &fakeResult{ 95 "Package fmt", 96 []string{ 97 "http://golang.org/", 98 "http://golang.org/pkg/", 99 }, 100 }, 101 "http://golang.org/pkg/os/": &fakeResult{ 102 "Package os", 103 []string{ 104 "http://golang.org/", 105 "http://golang.org/pkg/", 106 }, 107 }, 108 }