json.Unmarshal不区分大小写的坑

这个坑隐藏得很深,绝大多数情况下都不会碰到。在转换JSON的过程中,Unmarshal会按字段名来转换,碰到同名但大小写不一样的字段会当做同一字段处理。在官方文档中亦有说明(加粗部分):

To unmarshal JSON into a struct, Unmarshal matches incoming object keys to the keys used by Marshal (either the struct field name or its tag), preferring an exact match but also accepting a case-insensitive match. By default, object keys which don’t have a corresponding struct field are ignored (see Decoder.DisallowUnknownFields for an alternative).

精确匹配最好,但是也接受大小写不敏感的匹配。只能说“包容性”太好了,但我认为这并不是一个好的设计。…

Tags:

用ChatGPT写了段无法通过编译的Go代码

这几天ChatGPT突然之间火得一塌糊涂。这个AI能够写小说、剧本、散文,回答各种问题,当然它肯定也能写代码,还能扮演Linux虚拟机来执行各种命令、编译代码。由于它过于强大,有人用它来回答stackoverflow上的各种问题,导致官方采取临时措施,把这个AI给禁止了。不得不说,它回答这些问题比google要好用多了。

我也折腾了一个账号,上去试了试。作为程序员,我让ChatGPT用Go写了段快速排序的代码,总体看起来还是很不错的:

func quickSort(arr []int) []int {
  // base case: if the length of the array is 0 or 1, it is already sorted
  if len(arr) < 2 {
    return arr
  }

  // choose the pivot element as the middle element of the array
  pivot := arr[len(arr)

Go指南练习11-Web爬虫

这个是A Tour of Go这个教程里面的最后一个练习,如下:

在这个练习中,我们将会使用 Go 的并发特性来并行化一个 Web 爬虫。

修改 Crawl 函数来并行地抓取 URL,并且保证不重复。

提示:你可以用一个 map 来缓存已经获取的 URL,但是要注意 map 本身并不是并发安全的!

原题代码模板如下:

package main

import (
	"fmt"
)

type Fetcher interface {
	// Fetch 返回 URL 的 body 内容,并且将在这个页面上找到的 URL 放到一个 slice 中。
	Fetch(url string) (body string, urls []string, err error)
}

// Crawl 使用 fetcher 从某个 URL 开始递归的爬取页面,直到达到最大深度。
func Crawl(url string, depth int, fetcher Fetcher) {
	// TODO: 并行的抓取 URL。
Tags:

Go指南练习10-等价二叉查找树

这个是A Tour of Go这个教程里面的第十个练习,原题如下:

不同二叉树的叶节点上可以保存相同的值序列。例如,以下两个二叉树都保存了序列 `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) 用于构造一个随机结构的已排序二叉查找树,它保存了值 k2k3k, …, 10k

创建一个新的信道 ch 并且对其进行步进:

go Walk(tree.New(1),
Tags:

Go指南练习9-图像

这个是A Tour of Go这个教程里面的第九个练习,原题如下:

还记得之前编写的图片生成器 吗?我们再来编写另外一个,不过这次它将会返回一个 image.Image 的实现而非一个数据切片。

定义你自己的 Image 类型,实现必要的方法并调用 pic.ShowImage

Bounds 应当返回一个 image.Rectangle ,例如 image.Rect(0, 0, w, h)

ColorModel 应当返回 color.RGBAModel

At 应当返回一个颜色。上一个图片生成器的值 v 对应于此次的 color.RGBA{v, v, 255, 255}

原题模板代码如下:

package main

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

Go指南练习8-rot13Reader

这个是A Tour of Go这个教程里面的第八个练习,原题如下:

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

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

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

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

原题模板代码如下:

package
Tags:

Go指南练习7-Reader

这个是A Tour of Go这个教程里面的第七个练习,原题如下:

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

原题代码模板如下:

package main

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

type MyReader struct{}

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

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

这题其实就是要实现一个Read接口:

func (T) Read(b []byte) (n int, err error)

然后在这个接口里面一直返回b的长度,给b注入相应长度的A的ASCII码就好:

package main

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

type MyReader
Tags:

 Go指南练习6-错误

这个是A Tour of Go这个教程里面的第六个练习,原题如下:

之前的练习中复制 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 来避免这个问题:

Tags:

Go指南练习5-Stringer

这个是A Tour of Go这个教程里面的第五个练习,原题如下:

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

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

原题模板如下:

package main

import "fmt"

type IPAddr [4]byte

// TODO: Add a "String() string" method to IPAddr.

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

这个程序可以直接运行,结果如下:

loopback: [127 
Tags:

Go指南练习4-斐波那契闭包

这个是A Tour of Go这个教程里面的第四个练习,原题如下:

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

原题代码模板如下:

package main

import "fmt"

// fibonacci is a function that returns
// a function that returns an int.
func fibonacci() func() int {
}

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

这题需要fibonacci函数返回一个闭包函数,这个闭包函数需要引用函数体外的变量,并对其进行修改,以保存中间结果——该数列的最新的两个值。实现代码如下:

package main

import "fmt"

// 返回一个“返回int的函数”
func fibonacci()
Tags: