为了练习函数与循环,我们来实现一个平方根函数:用牛顿法实现平方根函数。
计算机通常使用循环来计算 x 的平方根。从某个猜测的值 z 开始,我们可以根据 z² 与 x 的近似度来调整 z,产生一个更好的猜测:
z -= (z*z - x) / (2*z)
重复调整的过程,猜测的结果会越来越精确,得到的答案也会尽可能接近实际的平方根。
在提供的 func Sqrt 中实现它。无论输入是什么,对 z 的一个恰当的猜测为 1。 要开始,请重复计算 10 次并随之打印每次的 z 值。观察对于不同的值 x(1、2、3 …), 你得到的答案是如何逼近结果的,猜测提升的速度有多快。
提示:用类型转换或浮点数语法来声明并初始化一个浮点数值:
z := 1.0
z := float64(1)
然后,修改循环条件,使得当值停止改变(或改变非常小)的时候退出循环。观察迭代次数大于还是小于 10。 尝试改变 z 的初始猜测,如 x 或 x/2。你的函数结果与标准库中的 math.Sqrt 接近吗?
(注:如果你对该算法的细节感兴趣,上面的 z² − x 是 z² 到它所要到达的值(即 x)的距离,除以的 2z 为 z² 的导数, 我们通过 z² 的变化速度来改变 z 的调整量。这种通用方法叫做牛顿法。 它对很多函数,特别是平方根而言非常有效。)
(2)答案
循环10次:
package main
import("fmt")funcSqrt(x float64)float64{
z :=float64(1)for i :=0; i <=10; i++{
z = z -(z*z - x)/(2* z)}return z
}funcmain(){
fmt.Println(Sqrt(2))}
无限接近:
package main
import("fmt""math")funcSqrt(x float64)float64{
z :=float64(1)for{
res := z -(z*z-x)/(2*z)if math.Abs(res-z)<1e-10{return res
}
z = res
}return z
}funcmain(){
fmt.Println(Sqrt(2))
fmt.Println(math.Sqrt(2))}
package main
import("tour/wc""strings")funcWordCount(s string)map[string]int{
ret :=make(map[string]int)
arr := strings.Fields(s)for_, val :=range arr {
ret[val]++}return ret
}funcmain(){
wc.Test(WordCount)}
(3)运行结果
PASS
f("I ate a donut. Then I ate another donut.")=map[string]int{"donut.":2,"Then":1,"another":1,"I":2,"ate":2,"a":1}
PASS
f("A man a plan a canal panama.")=map[string]int{"a":2,"plan":1,"canal":1,"panama.":1,"A":1,"man":1}
Program exited.
package main
import"fmt"// fibonacci is a function that returns// a function that returns an int.funcfibonacci()func()int{
res1 :=0
res2 :=1returnfunc()int{
tmp := res1
res1, res2 = res2,(res1 + res2)return tmp
}}funcmain(){
f :=fibonacci()for i :=0; i <10; i++{
fmt.Println(f())}}
package main
import("io""os""strings")type rot13Reader struct{
r io.Reader
}funcrot13(b byte)byte{switch{case'A'<= b && b <='M':
b = b +13case'M'< b && b <='Z':
b = b -13case'a'<= b && b <='m':
b = b +13case'm'< b && b <='z':
b = b -13}return b
}func(mr rot13Reader)Read(b []byte)(int,error){
n, e := mr.r.Read(b)for i :=0; i < n; i++{
b[i]=rot13(b[i])}return n, e
}funcmain(){
s := strings.NewReader("Lbh penpxrq gur pbqr!")
r := rot13Reader{s}
io.Copy(os.Stdout,&r)}
package main
import("image""image/color""golang.org/x/tour/pic")type Image struct{
W int
H int}func(i Image)Bounds() image.Rectangle {return image.Rect(0,0, i.W, i.H)}func(i Image)ColorModel() color.Model {return color.RGBAModel
}func(self Image)At(x, y int) color.Color {return color.RGBA{uint8(x),uint8(y),255,255}}funcmain(){
m := Image{200,200}
pic.ShowImage(m)}
(3)运行结果
10、练习:等价二叉查找树
(1)题目
不同二叉树的叶节点上可以保存相同的值序列。例如,以下两个二叉树都保存了序列 1,1,2,3,5,8,13 。
在大多数语言中,检查两个二叉树是否保存了相同序列的函数都相当复杂。 我们将使用 Go 的并发和信道来编写一个简单的解法。
本例使用了 tree 包,它定义了类型:
type Tree struct {
Left *Tree
Value int
Right *Tree
}
1. 实现 Walk 函数。
2. 测试 Walk 函数。
函数 tree.New(k) 用于构造一个随机结构的已排序二叉查找树,它保存了值 k 、 2k 、 3k … 10k 。
创建一个新的信道 ch 并且对其进行步进:
go Walk(tree.New(1), ch)
然后从信道中读取并打印 10 个值。应当是数字 1, 2, 3, ..., 10 。
3. 用 Walk 实现 Same 函数来检测 t1 和 t2 是否存储了相同的值。
4. 测试 Same 函数。
Same(tree.New(1), tree.New(1)) 应当返回 true ,而 Same(tree.New(1), tree.New(2)) 应当返回 false 。
Tree 的文档可在这里找到。
(2)答案
package main
import("fmt""golang.org/x/tour/tree")// Walk 步进 tree t 将所有的值从 tree 发送到 channel ch。funcWalk(t *tree.Tree, ch chanint){if t ==nil{return}Walk(t.Left, ch)
ch <- t.Value
Walk(t.Right, ch)}// Same 检测树 t1 和 t2 是否含有相同的值。funcSame(t1, t2 *tree.Tree)bool{
ch1 :=make(chanint)
ch2 :=make(chanint)goWalk(t1, ch1)goWalk(t2, ch2)for i :=0; i <10; i++{
x, y :=<-ch1,<-ch2
fmt.Println(x, y)if x != y {returnfalse}}returntrue}funcmain(){// fmt.Println(Same(tree.New(1), tree.New(2)))
fmt.Println(Same(tree.New(1), tree.New(1)))}
(3)运行结果
1122334455667788991010true
Program exited.
11、练习:Web 爬虫
(1)题目
在这个练习中,我们将会使用 Go 的并发特性来并行化一个 Web 爬虫。
修改 Crawl 函数来并行地抓取 URL,并且保证不重复。
提示: 你可以用一个 map 来缓存已经获取的 URL,但是要注意 map 本身并不是并发安全的!
(2)答案
package main
import("fmt""sync")type Fetcher interface{Fetch(url string)(body string, urls []string, err error)}funcCrawl(url string, depth int, fetcher Fetcher, wg *sync.WaitGroup){defer wg.Done()if depth <=0{return}if cache.has(url){// fmt.Println("already exist.")return}
body, urls, err := fetcher.Fetch(url)if err !=nil{
fmt.Println(err)return}
fmt.Printf("found: %s %q
", url, body)for_, u :=range urls {
wg.Add(1)goCrawl(u, depth-1, fetcher, wg)}return}type Cache struct{
cache map[string]bool
mutex sync.Mutex
}func(cache *Cache)add(url string){
cache.mutex.Lock()
cache.cache[url]=true
cache.mutex.Unlock()}func(cache *Cache)has(url string)bool{
cache.mutex.Lock()defer cache.mutex.Unlock()_, ok := cache.cache[url]if!ok {
cache.cache[url]=true}return ok
}var cache Cache = Cache{
cache:make(map[string]bool),}funcmain(){var wg sync.WaitGroup
wg.Add(1)goCrawl("https://golang.org/",4, fetcher,&wg)
wg.Wait()}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)}var fetcher = fakeFetcher{"https://golang.org/":&fakeResult{"The Go Programming Language",[]string{"https://golang.org/pkg/",