练习:循环与函数

(1)题目

为了练习函数与循环,我们来实现一个平方根函数:用牛顿法实现平方根函数。

计算机通常使用循环来计算 x 的平方根。从某个猜测的值 z 开始,我们可以根据 z² 与 x 的近似度来调整 z,产生一个更好的猜测:

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。
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 接近吗?

(2)代码

循环10次:

【A tour of go】练习题 随笔 第1张
package main

import (
	"fmt"
	"math"
)

func Sqrt(x float64) float64 {
	z := 1.0
	for i:= 0;i < 10;i++{
		z -= (z*z-x)/(2*z)
		fmt.Printf("i = %d, z = %f\n", i, z)
	}
	return z
}

func main() {
	fmt.Println(Sqrt(2))
	fmt.Println(math.Sqrt(2))
}
View Code

无限逼近:

【A tour of go】练习题 随笔 第3张
package main

import (
	"fmt"
	"math"
)

func Sqrt(x float64) float64 {
	z := 1.0
	i := 0
	for math.Abs( z*z-x) > 1e-10 {
		z -= (z*z-x)/(2*z)
		fmt.Printf("i = %d, z = %f\n", i, z)
		i += 1
	}
	return z
}

func main() {
	fmt.Println(Sqrt(2))
	fmt.Println(math.Sqrt(2))
}
View Code

 

练习:切片

(1)切片

实现 Pic。它应当返回一个长度为 dy 的切片,其中每个元素是一个长度为 dx,元素类型为 uint8 的切片。当你运行此程序时,它会将每个整数解释为灰度值(好吧,其实是蓝度值)并显示它所对应的图像。

图像的选择由你来定。几个有趣的函数包括 (x+y)/2x*yx^yx*log(y) 和 x%(y+1)

(提示:需要使用循环来分配 [][]uint8 中的每个 []uint8;请使用 uint8(intValue) 在类型之间转换;你可能会用到 math 包中的函数。)

(2)代码

【A tour of go】练习题 随笔 第5张
package main

import "golang.org/x/tour/pic"

func Pic(dx, dy int) [][]uint8 {
	result := make([][]uint8,dy)
	for i := 0; i < dy; i++{
		result[i] = make([]uint8,dx)
		for j := 0; j < dx; j++{
              //更改此句,可得到不同的结果
			result[i][j] = uint8(i*j)
		}
	}
	return result
}

func main() {
	pic.Show(Pic)
}
View Code

(3)结果

【A tour of go】练习题 随笔 第7张

 

练习:映射

(1)题目

实现 WordCount。它应当返回一个映射,其中包含字符串 s 中每个“单词”的个数。函数 wc.Test 会对此函数执行一系列测试用例,并输出成功还是失败。

你会发现 strings.Fields 很有帮助。

(2)代码

【A tour of go】练习题 随笔 第8张
package main

import (
	"golang.org/x/tour/wc"
	"strings"
)

func WordCount(s string) map[string]int {
	result := strings.Fields(s)
	m := make(map[string]int)
	for _, word := range result{
		m[word]++
	}	
	return m
}

func main() {
	wc.Test(WordCount)
}
View Code

(3)结果

【A tour of go】练习题 随笔 第10张
PASS
 f("I am learning Go!") = 
  map[string]int{"Go!":1, "I":1, "am":1, "learning":1}
PASS
 f("The quick brown fox jumped over the lazy dog.") = 
  map[string]int{"The":1, "brown":1, "dog.":1, "fox":1, "jumped":1, "lazy":1, "over":1, "quick":1, "the":1}
PASS
 f("I ate a donut. Then I ate another donut.") = 
  map[string]int{"I":2, "Then":1, "a":1, "another":1, "ate":2, "donut.":2}
PASS
 f("A man a plan a canal panama.") = 
  map[string]int{"A":1, "a":2, "canal":1, "man":1, "panama.":1, "plan":1}
View Code

 

练习:斐波那契闭包

(1)题目

让我们用函数做些好玩的事情。

实现一个 fibonacci 函数,它返回一个函数(闭包),该闭包返回一个斐波纳契数列 `(0, 1, 1, 2, 3, 5, ...)`。

(2)代码

【A tour of go】练习题 随笔 第12张
package main

import "fmt"

// 返回一个“返回int的函数”
func fibonacci() func() int {
	sum := 0
	res1 := 0
	res2 := 1
	i := 0
	return func() int{
		if i == 0{
			sum = res1
		}else if i == 1{
			sum = res2
		}else{
			sum = res1 + res2
			res1 = res2
			res2 = sum
		}
		i += 1
		return sum
	}
}

func main() {
	f := fibonacci()
	for i := 0; i < 10; i++ {
		fmt.Println(f())
	}
}
View Code

 

练习:Stringer

(1)题目

通过让 IPAddr 类型实现 fmt.Stringer 来打印点号分隔的地址。

例如,IPAddr{1, 2, 3, 4} 应当打印为 "1.2.3.4"

(2)代码

【A tour of go】练习题 随笔 第14张
package main

import "fmt"

type IPAddr [4]byte

// TODO: 给 IPAddr 添加一个 "String() string" 方法
func (addr IPAddr) String() string{
	return fmt.Sprintf("%v.%v.%v.%v\n",addr[0],addr[1],addr[2],addr[3])
}

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)
	}
}
View Code

 

练习:错误

(1)题目

从之前的练习中复制 Sqrt 函数,修改它使其返回 error 值。

Sqrt 接受到一个负数时,应当返回一个非 nil 的错误值。复数同样也不被支持。

创建一个新的类型

type ErrNegativeSqrt float64

并为其实现

func (e ErrNegativeSqrt) Error() string

方法使其拥有 error 值,通过 ErrNegativeSqrt(-2).Error() 调用该方法应返回 "cannot Sqrt negative number: -2"

注意: 在 Error 方法内调用 fmt.Sprint(e) 会让程序陷入死循环。可以通过先转换 e 来避免这个问题:fmt.Sprint(float64(e))。这是为什么呢?

修改 Sqrt 函数,使其接受一个负数时,返回 ErrNegativeSqrt 值。

(2)代码

【A tour of go】练习题 随笔 第16张
package main

import (
	"fmt"
	"math"
)

func Sqrt(x float64) (float64, error) {
	if(x < 0){
		return x, ErrNegativeSqrt(x)
	}
	return math.Sqrt(x), nil
}


func main() {
	fmt.Println(Sqrt(2))
	fmt.Println(Sqrt(-2))
}

type ErrNegativeSqrt float64

func (num ErrNegativeSqrt) Error() string{
	return fmt.Sprintf("cannot Sqrt negative number : %f\n", num)
}
View Code

 

练习:Reader

(1)题目

实现一个 Reader 类型,它产生一个 ASCII 字符 'A' 的无限流。

(2)代码

【A tour of go】练习题 随笔 第18张
package main

import "golang.org/x/tour/reader"

type MyReader struct{}

// TODO: 给 MyReader 添加一个 Read([]byte) (int, error) 方法

func (read MyReader) Read(b []byte) (int, error) {
	b[0] = 'A'
	return 1, nil
}

func main() {
	reader.Validate(MyReader{})
}
View Code

(3)结果

OK!

Program exited.

 

 

练习:rot13Reader

(1)题目

有种常见的模式是一个 io.Reader 包装另一个 io.Reader,然后通过某种方式修改其数据流。

例如,gzip.NewReader 函数接受一个 io.Reader(已压缩的数据流)并返回一个同样实现了 io.Reader 的 *gzip.Reader(解压后的数据流)。

编写一个实现了 io.Reader 并从另一个 io.Reader 中读取数据的 rot13Reader,通过应用 rot13 代换密码对数据流进行修改。

rot13Reader 类型已经提供。实现 Read 方法以满足 io.Reader

(2)代码

【A tour of go】练习题 随笔 第20张
package main

import (
	"io"
	"os"
	"strings"
)

type rot13Reader struct {
	r io.Reader
}

func rot13(b byte) byte{
	if (b >= 'A' && b <= 'M') || (b >= 'a' && b <= 'm'){
		b += 13
	}else if (b >= 'N' && b <= 'Z') || (b >= 'n' && b <= 'z'){
		b -= 13
	}
	return b
}

func (reader rot13Reader) Read(b []byte)(int, error){
	n, e := reader.r.Read(b)
	for i := 0; i < n;i++{
		b[i] = rot13(b[i])
	}
	return n,e
}

func main() {
	s := strings.NewReader("Lbh penpxrq gur pbqr!")
	r := rot13Reader{s}
	io.Copy(os.Stdout, &r)
}
View Code

(3)结果

You cracked the code!
Program exited.

 

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄