這兩天學完了 A Tour of Go 官方的語法教學,裏面有不少的 Excercise(訓練題)。但願對你們有用,若是有其餘人也寫過,並以爲我寫的不對的,求教!❤️golang
x
,咱們經過 loop 和 function 來找到其平方根 z
,即 z² = x
package main
import (
"fmt"
"math"
)
func Sqrt(x float64) float64 {
z := x/2
for i:= 0; math.Abs(z*z - x) > 0.0000000001; i++ {
z -= (z*z - x) / (2*z)
fmt.Println(i, "z:", z, "z^2 -x:", z*z - x)
}
return z
}
func main() {
fmt.Println(Sqrt(1000))
}
複製代碼
z := x/2
這個是猜想的初始值(也能夠像是題目裏的 hint 寫的設置成 1)math.Abs(z*z - x) > 0.0000000001
用最優解的邏輯就是給了一個 tolerance 0.0000000001
,即咱們用計算公式算出來的 z²
與 x
的差值已經足夠小,咱們認定預估的 z
算是一個近似準確值。Pic
函數來生成一個 [][]uint8
的 2D 圖片(便可說是 Array of Array)。它的大小由參數 (dx, dy int)
決定,這個有 dy
個數組,每一個數組裏又有一個長度爲 dx
的數組。而相關的位置上 pic[y][x]
是這個圖片的 bluescale(只有藍色)數值,格式爲 uint8
。package main
import "golang.org/x/tour/pic"
func Pic(dx, dy int) [][]uint8 {
pic := make([][]uint8, dy)
for i := range pic {
pic[i] = make([]uint8, dx)
for j := range pic[i] {
pic[i][j] = uint8(i*j + j*j)
}
}
return pic
}
func main() {
pic.Show(Pic)
}
複製代碼
pic := make([][]uint8, dy)
先建一個數組,長度是 dy
,數組裏每一個元素的內容是一個數組 []uint8
pic[i] = make([]uint8, dx)
在數組裏的第 i
個元素裏,咱們再創造一個 []uint8
數組,長度爲 dx
pic[i][j] = uint8(i*j + j*j)
表示咱們設計的 bluesacle 計算公式裏,pic[i][j]
位置的數值是 uint8(i*j + j*j)
(這裏你能夠隨意改幾個,能看到不少不一樣的效果哦!)WordCount
,它能夠回覆一個 map
裏面包含輸入字符串中出現的單詞 word 及相應出現的次數。map[string]int{"I":1, "love":1, "you":3}
package main
import (
"golang.org/x/tour/wc"
"strings"
)
func WordCount(s string) map[string]int {
m := make(map[string]int)
words := strings.Fields(s)
for _, word := range words {
m[word] = m[word] + 1
}
return m
}
func main() {
wc.Test(WordCount)
}
複製代碼
strings.Files(s)
這個函數會自動切分一個字符串到一個數組,每一個數組裏是一個 wordmap
而後在數組裏每當某一個 word
出現,就相應的增長 1fibonacci
函數,使其返回一個函數package main
import "fmt"
// fibonacci is a function that returns
// a function that returns an int.
func fibonacci() func() int {
a, b := 0, 1
return func() int {
c := a
a, b = b, a+b
return c
}
}
func main() {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Println(f())
}
}
複製代碼
func fibonacci() func() int
中,返回的是一個函數 func() int
,而這個函數每次運行返回的是一個 int
fibonacci()
中,變量 a
和 b
定義在函數 fibonacci()
裏,並被此函數返回的 return func() int { ... }
函數引用到,也就是說在返回的函數裏 a
和 b
兩個變量一直存儲在內存中,且數值會一直變化f := fibonacci()
中 f
是 fibonacci()
返回的函數,在初始狀況中,此時的 a, b := 0, 1
f()
調用爲例:
c := a
: c
賦值爲 a
即 0a, b = b, a+b
:a
賦值爲 b
即 1,b
賦值爲 a+b
即 1return c
,返回 0,也就是斐波那契數列的第一個值f()
調用,注意此時 a
是 1,b
是 1:
c := a
: c
賦值爲 a
即 1a, b = b, a+b
:a
賦值爲 b
即 1,b
賦值爲 a+b
即 2return c
,返回 1,也就是斐波那契數列的第二個值f
函數被調用了 10 次,輸出了斐波那契數列前 10 個值type IPAddr [4]byte
增長 Stringer Interface 函數來輸出字符串,即 IPAddr{1, 2, 3, 4}
print 爲 1.2.3.4
package main
import (
"fmt"
"strings"
"strconv"
)
type IPAddr [4]byte
// TODO: Add a "String() string" method to IPAddr.
func (ip IPAddr) String() string {
s := make([]string, len(ip))
for i, val := range ip {
s[i] = strconv.Itoa(int(val))
}
return fmt.Sprintf(strings.Join(s, "."))
}
func main() {
hosts := map[string]IPAddr{
"loopback": {127, 0, 0, 1},
"googleDNS": {8, 8, 8, 8},
}
for name, ip := range hosts {
fmt.Printf("%v: %v\n", name, ip)
}
}
複製代碼
strconv.Itoa
,int to stringIPAddr
是一個大小爲 4 的 []byte
,咱們生成一個 [4]string
數組 s
,每個元素是 IP 地址中的一位,而且咱們用 strconv.Itoa(int(val))
把它轉換爲字符串strings.Join(s, ".")
將字符串數組用 "."
連起來fmt.Printf("%v: %v\n", name, ip)
時,會對 type IPAddr
默認調用其 Stringer interface
下定義的 String()
函數來輸出sqrt
函數,當參數是一個負數時,添加 type ErrNegativeSqrt float64
,並經過定義 func (e ErrNegativeSqrt) Error() string
從而使其爲 error
package main
import (
"fmt"
"math"
)
type ErrNegativeSqrt float64
func (e ErrNegativeSqrt) Error() string {
return fmt.Sprintf("cannot Sqrt negative number: %v", float64(e))
}
func Sqrt(x float64) (float64, error) {
if (x > 0) {
z:= x/2
for i:= 0; math.Abs(z*z - x) > 0.0000000001; i++ {
z -= (z*z - x) / (2*z)
fmt.Println(i, "z:", z, "z^2 -x:", z*z - x)
}
return z, nil
} else {
return 0, ErrNegativeSqrt(x)
}
}
複製代碼
error
類型是一個 built-in interface,須要定義 Error() string
函數,測試一個 error
type 是不是 nil
是定義函數返回是否出錯的方法。例如: i, err := strconv.Atoi("42")
返回值中 i
表明函數返回數值,而 err
若是不是 nil
的話,則表示有錯誤發生func (e ErrNegativeSqrt) Error() string
定義了 ErrNegativeSqrt
屬於 error
的 Error()
函數,也就簡潔說明 ErrNegativeSqrt
是一個 error
func Sqrt(x float64) (float64, error)
函數返回兩個數值,前者爲參數的平方根,後者爲 error
,當後者不是 nil
的時候,在 Println
中會自動調用 Error()
輸出相應的錯誤信息字符串。Reader
type,以輸出一個無限個 'A'
的字符流package main
import (
"fmt"
"golang.org/x/tour/reader"
)
type MyReader struct{}
type ErrEmptyBuffer []byte
func (b ErrEmptyBuffer) Error() string {
return fmt.Sprintf("cannot read an empty buffer: %v", b)
}
// TODO: Add a Read([]byte) (int, error) method to MyReader.
func (reader MyReader) Read(b []byte) (int, error) {
bLength := len(b)
if (bLength == 0) {
return 0, ErrEmptyBuffer(b)
}
for i := range b {
b[i] = 'A'
}
return bLength, nil
}
func main() {
reader.Validate(MyReader{})
}
複製代碼
MyReader
會輸出無限個 'A'
,所以只要輸入參數 b []byte
不是一個空的 Buffer,就會寫滿bLength == 0
也就是 Buffer b
是空時,返回 ErrEmptyBuffer(b)
錯誤'A'
並返回 bLength, nil
rot13Reader
type 使其包含一個 io.Reader
使得其在執行 Read
函數時,會自動根據 rot13
來轉化相應的字母字符。package main
import (
"io"
"os"
"strings"
)
type rot13Reader struct {
r io.Reader
}
func rot13(c byte) byte {
switch {
case (c >= 'A' && c <= 'M') || (c >= 'a' && c <= 'm'):
c += 13
case (c >= 'N' && c <= 'Z') || (c >= 'n' && c <= 'z'):
c -= 13
}
return c
}
func (reader *rot13Reader) Read(b []byte) (n int, err error) {
n, err := reader.r.Read(b)
for i := range b {
b[i] = rot13(b[i])
}
return n, err
}
func main() {
s := strings.NewReader("Lbh penpxrq gur pbqr!")
r := rot13Reader{s}
io.Copy(os.Stdout, &r)
}
複製代碼
rot13
規則實現函數 func rot13(c byte) byte
,也就是 A-M
與 N-Z
的兌換,以及 a-m
與 n-z
的兌換rot13Reader
的 Read
函數,首先使用期包含的 r
Reader 來讀取數據,而後每個數據都經過 rot13
轉換,最終返回相應的數字結果image.Image
圖像而不單單是一個二維數組數據Image
type,並定義相應的 image.Image
interface,其中
ColorModel
使用 color.RGBAModel
Bounds
使用 image.Rectangle
type,並用 image.Rect(0, 0, w, h)
定義At
會返回具體圖片像素點上的顏色,最終用 color.RGBA{v, v, 255, 255}
定義package main
import (
"golang.org/x/tour/pic"
"image"
"image/color"
)
type Image struct{
W int
H int
}
func (i Image) ColorModel() color.Model {
return color.RGBAModel
}
func (i Image) Bounds() image.Rectangle {
return image.Rect(0, 0, i.W, i.H)
}
func (i Image) At(x, y int) color.Color {
v := uint8(x*y + y*y)
return color.RGBA{v, v, 255, 255}
}
func main() {
m := Image{200, 200}
pic.ShowImage(m)
}
複製代碼
image.Rect
, color.RGBAModel
, color.RGBA
所以,咱們引入 "image"
和 "image/color"
packagesAt
函數我沿用了以前的圖片顏色計算公式 v := uint8(x*y + y*y)
Tree
類型,並實現以下函數來測試兩個 Tree
是否存儲一樣的數列。
Tree
的 Walk
method,使其會按照樹內存儲數列順序逐一走完相關數字,方式是逐一傳入 ch chan int
中,也就是 func Walk(t *tree.Tree, ch chan int)
,而這個過程放入 goroutine 中完成 go Walk(tree.New(1), ch)
。注意:tree.New(k)
會隨機生成一個結構不一的樹,但都會存儲相同的數列 k
, 2k
, ..., 10k
。Same
函數並調用 Walk
方法,使其能夠比較兩個 Tree
是否存儲相同的數列。例如:Same(tree.New(1), tree.New(1))
應該返回 true
,由於裏面都存着 1
, 2
, ... 10
。而 Same(tree.New(1), tree.New(2))
應該返回 false
package main
import (
"fmt"
"golang.org/x/tour/tree"
)
// type Tree struct {
// Left *Tree
// Value int
// Right *Tree
// }
// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
if t.Left != nil {
Walk(t.Left, ch)
}
ch <- t.Value
if t.Right != nil {
Walk(t.Right, ch)
}
}
// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
var v1, v2 int
c1 := make(chan int)
c2 := make(chan int)
go Walk(t1, c1)
go Walk(t2, c2)
for i := 0; i < 10; i++ {
v1 = <-c1
v2 = <-c2
if v1 != v2 {
return false
}
}
return true
}
func main() {
ch := make(chan int)
go Walk(tree.New(10), ch)
for i := 0; i < 10; i++ {
fmt.Println(<-ch)
}
fmt.Println(Same(tree.New(1), tree.New(1)))
}
複製代碼
Walk
方法中,咱們按照 t.Left
, ch <- t.Value
,t.Right
順序逐一將數據傳入 ch
Same
方法中,由於咱們知道 tree.New(k)
生成的樹包含 10 個節點,所以咱們生成兩個 Channels 分別存儲兩個 Tree
在 Walk
時的數據,而後一個循環 10 次的 Loop 每次從裏面分別拿出一個數值,比較相應數列下的數值是否相同,若是出現不一樣,則表述兩個樹不相同。居然能看到這裏,那有必要加個微信了 ymkalasoo,咱們在找優秀的 Go 開發者 網頁爬蟲
Crawl
函數使其能夠並行抓去 URL
但不重複抓取fakeFetcher
是一個假的數據集,表示能夠用來被測試的爬蟲抓取數據package main
import (
"fmt"
"sync"
)
type Fetcher interface {
// Fetch returns the body of URL and
// a slice of URLs found on that page.
Fetch(url string) (body string, urls []string, err error)
}
type UrlChecker struct {
urls map[string]bool
mux sync.Mutex
}
func (c *UrlChecker) Crawled(url string) bool {
c.mux.Lock()
if c.urls[url] {
defer c.mux.Unlock()
return true
}
c.urls[url] = true
defer c.mux.Unlock()
return false
}
var uc = UrlChecker{urls: make(map[string]bool)}
// Crawl uses fetcher to recursively crawl
// pages starting with url, to a maximum of depth.
func Crawl(url string, depth int, fetcher Fetcher, ret chan string) {
// TODO: Fetch URLs in parallel.
// TODO: Don't fetch the same URL twice.
// This implementation doesn't do either:
defer close(ret)
if depth <= 0 {
return
}
if uc.Crawled(url) {
return
}
body, urls, err := fetcher.Fetch(url)
if err != nil {
fmt.Println(err)
return
}
ret <- fmt.Sprintf("found: %s %q\n", url, body)
results := make([]chan string, len(urls))
for i, u := range urls {
results[i] = make(chan string)
go Crawl(u, depth-1, fetcher, results[i])
}
for _, result := range results {
for s := range result {
ret <- s
}
}
return
}
func main() {
result := make(chan string)
go Crawl("https://golang.org/", 4, fetcher, result)
for s := range result {
fmt.Println(s)
}
}
// fakeFetcher is Fetcher that returns canned results.
type fakeFetcher map[string]*fakeResult
type fakeResult struct {
body string
urls []string
}
func (f fakeFetcher) Fetch(url string) (string, []string, error) {
if res, ok := f[url]; ok {
return res.body, res.urls, nil
}
return "", nil, fmt.Errorf("not found: %s", url)
}
// fetcher is a populated fakeFetcher.
var fetcher = fakeFetcher{
"https://golang.org/": &fakeResult{
"The Go Programming Language",
[]string{
"https://golang.org/pkg/",
"https://golang.org/cmd/",
},
},
"https://golang.org/pkg/": &fakeResult{
"Packages",
[]string{
"https://golang.org/",
"https://golang.org/cmd/",
"https://golang.org/pkg/fmt/",
"https://golang.org/pkg/os/",
},
},
"https://golang.org/pkg/fmt/": &fakeResult{
"Package fmt",
[]string{
"https://golang.org/",
"https://golang.org/pkg/",
},
},
"https://golang.org/pkg/os/": &fakeResult{
"Package os",
[]string{
"https://golang.org/",
"https://golang.org/pkg/",
},
},
}
複製代碼
UrlChecker
是用來測試一個 url
是否已經被 fetch
過,其中包含一個 urls map[string]bool
存儲 URL 抓取狀況,另 mux sync.Mutex
來防止數據被重複修改,進而不會出現,fetcher 在 並行的時候,由於此時某一個 URL 都沒有在 UrlChecker
被標註 fetch 過,進而同時 fetch。func (c *UrlChecker) Crawled(url string) bool
來存儲一個 URL 已經被抓取的狀態。在測試過程當中 c.mux.Lock()
使得,此段數據被阻斷其餘 goroutine 修改。 defer c.mux.Unlock()
意思是在 return
後再執行解開 Mutual Exclusion 鎖。var uc = UrlChecker{urls: make(map[string]bool)}
,當 uc.Crawled(url)
是 true
的時候,再也不抓取相應 url。func Crawl(url string, depth int, fetcher Fetcher, ret chan string)
最後一個參數 ret chan string
傳入一個 channel 來存儲抓取結果results := make([]chan string, len(urls))
,每個 url 下的須要更深一層抓取的 urls 生成相應的多個 channels,並逐一抓取go Crawl(u, depth-1, fetcher, results[i])
循環抓取 urls複製代碼
for _, result := range results { for s := range result { ret <- s } } ``` 每個抓取到的數據從相應的 channel s
傳入最上層的 channel ret
,完成全部的抓取數組
最終的輸出結果:bash
not found: https://golang.org/cmd/
found: https://golang.org/ "The Go Programming Language"
found: https://golang.org/pkg/ "Packages"
found: https://golang.org/pkg/fmt/ "Package fmt"
found: https://golang.org/pkg/os/ "Package os"
複製代碼
辛苦啦,我也在學 Go 哦!微信