《Go 语言之旅》是一个不错的 Golang 教程,适合拥有其他语言的编程经验、但是没有学过 Golang 的同学学习。该教程中含有一些简单的练习题,通过动手写代码的方式达到快速巩固知识点的目的。

本文是《Go 语言之旅》中“基础(Basics)”章节中的练习题,并附上了我自己在学习这部教程时写出的解答代码,供大家参考。

教程其他章节的练习题解答可以点击链接查看:

  1. 基础(本文)
  2. 方法和接口
  3. 并发

练习:循环与函数

题目

题目链接:中文 | English

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

计算机通常使用循环来计算 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 接近吗?

解答

package main

import (
    "fmt"
    "math"
)

func Sqrt(x float64) float64 {
    z := 1.0    // 猜测初始值
    prev := 0.0 // 上次迭代的结果

    for math.Abs(z-prev) > 1e-5 { // 当值改变非常小的时候退出循环
        prev = z
        z -= (z*z - x) / (2 * z)
    }
    return z
}

func main() {
    fmt.Println(Sqrt(2))
    fmt.Println(math.Sqrt(2)) // 与标准库函数的结果比较
}

运行结果如下,可以看到自己实现的 Sqrt 与标准库 math.Sqrt 给出的结果相近。

1.4142135623746899
1.4142135623730951

练习:切片

题目

题目链接:中文 | English

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

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

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

解答

package main

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

func Pic(dx, dy int) [][]uint8 {
    image := make([][]uint8, dy)
    for y := range image { // 外层循环,对于每一行
        image[y] = make([]uint8, dx) // 创建这一行的切片
        for x := range image[y] { // 内层循环,对于一行中的每一列
            image[y][x] = uint8(x ^ y) // 修改这里以产生不同的图像
        }
    }
    return image
}

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

运行结果如下。另外,修改第 10 行的公式(见题目),可以产生不同的图像。

本文地址:https://www.jeddd.com/article/a-tour-of-go-exercises-basics.html

练习:映射

题目

题目链接:中文 | English

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

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

解答

package main

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

func WordCount(s string) map[string]int {
    mapping := make(map[string]int)
    wordlist := strings.Fields(s) // 用空格分割为单词的切片
    for _, word := range wordlist {
        mapping[word] += 1 // 默认为0,所以可以直接+1
    }
    return mapping
}

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

运行结果如下,可以通过测试。

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}

练习:斐波那契闭包

题目

题目链接:中文 | English

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

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

解答

package main

import "fmt"

// 返回一个“返回int的函数”
func fibonacci() func() int {
    prev, current := -1, 1 // 初始值可由期待产生的序列倒推出来
    return func() int {
        prev, current = current, prev+current
        return current
    }
}

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

运行结果如下。

0
1
1
2
3
5
8
13
21
34

References

  1. Go 语言之旅
  2. A Tour of Go
  3. 头图来自 https://stackify.com/learn-go-tutorials/

本文地址:https://www.jeddd.com/article/a-tour-of-go-exercises-basics.html