mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-10-24 07:21:41 +00:00
回到简体
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
## 13.3. 示例: 深度相等判斷
|
||||
## 13.3. 示例: 深度相等判断
|
||||
|
||||
來自reflect包的DeepEqual函數可以對兩個值進行深度相等判斷。DeepEqual函數使用內建的==比較操作符對基礎類型進行相等判斷,對於複合類型則遞歸該變量的每個基礎類型然後做類似的比較判斷。因爲它可以工作在任意的類型上,甚至對於一些不支持==操作運算符的類型也可以工作,因此在一些測試代碼中廣泛地使用該函數。比如下面的代碼是用DeepEqual函數比較兩個字符串數組是否相等。
|
||||
来自reflect包的DeepEqual函数可以对两个值进行深度相等判断。DeepEqual函数使用内建的==比较操作符对基础类型进行相等判断,对于复合类型则递归该变量的每个基础类型然后做类似的比较判断。因为它可以工作在任意的类型上,甚至对于一些不支持==操作运算符的类型也可以工作,因此在一些测试代码中广泛地使用该函数。比如下面的代码是用DeepEqual函数比较两个字符串数组是否相等。
|
||||
|
||||
```Go
|
||||
func TestSplit(t *testing.T) {
|
||||
@@ -10,7 +10,7 @@ func TestSplit(t *testing.T) {
|
||||
}
|
||||
```
|
||||
|
||||
盡管DeepEqual函數很方便,而且可以支持任意的數據類型,但是它也有不足之處。例如,它將一個nil值的map和非nil值但是空的map視作不相等,同樣nil值的slice 和非nil但是空的slice也視作不相等。
|
||||
尽管DeepEqual函数很方便,而且可以支持任意的数据类型,但是它也有不足之处。例如,它将一个nil值的map和非nil值但是空的map视作不相等,同样nil值的slice 和非nil但是空的slice也视作不相等。
|
||||
|
||||
```Go
|
||||
var a, b []string = nil, []string{}
|
||||
@@ -20,7 +20,7 @@ var c, d map[string]int = nil, make(map[string]int)
|
||||
fmt.Println(reflect.DeepEqual(c, d)) // "false"
|
||||
```
|
||||
|
||||
我們希望在這里實現一個自己的Equal函數,用於比較類型的值。和DeepEqual函數類似的地方是它也是基於slice和map的每個元素進行遞歸比較,不同之處是它將nil值的slice(map類似)和非nil值但是空的slice視作相等的值。基礎部分的比較可以基於reflect包完成,和12.3章的Display函數的實現方法類似。同樣,我們也定義了一個內部函數equal,用於內部的遞歸比較。讀者目前不用關心seen參數的具體含義。對於每一對需要比較的x和y,equal函數首先檢測它們是否都有效(或都無效),然後檢測它們是否是相同的類型。剩下的部分是一個鉅大的switch分支,用於相同基礎類型的元素比較。因爲頁面空間的限製,我們省略了一些相似的分支。
|
||||
我们希望在这里实现一个自己的Equal函数,用于比较类型的值。和DeepEqual函数类似的地方是它也是基于slice和map的每个元素进行递归比较,不同之处是它将nil值的slice(map类似)和非nil值但是空的slice视作相等的值。基础部分的比较可以基于reflect包完成,和12.3章的Display函数的实现方法类似。同样,我们也定义了一个内部函数equal,用于内部的递归比较。读者目前不用关心seen参数的具体含义。对于每一对需要比较的x和y,equal函数首先检测它们是否都有效(或都无效),然后检测它们是否是相同的类型。剩下的部分是一个巨大的switch分支,用于相同基础类型的元素比较。因为页面空间的限制,我们省略了一些相似的分支。
|
||||
|
||||
<u><i>gopl.io/ch13/equal</i></u>
|
||||
```Go
|
||||
@@ -63,7 +63,7 @@ func equal(x, y reflect.Value, seen map[comparison]bool) bool {
|
||||
}
|
||||
```
|
||||
|
||||
和前面的建議一樣,我們併不公開reflect包相關的接口,所以導出的函數需要在內部自己將變量轉爲reflect.Value類型。
|
||||
和前面的建议一样,我们并不公开reflect包相关的接口,所以导出的函数需要在内部自己将变量转为reflect.Value类型。
|
||||
|
||||
```Go
|
||||
// Equal reports whether x and y are deeply equal.
|
||||
@@ -78,7 +78,7 @@ type comparison struct {
|
||||
}
|
||||
```
|
||||
|
||||
爲了確保算法對於有環的數據結構也能正常退出,我們必須記録每次已經比較的變量,從而避免進入第二次的比較。Equal函數分配了一組用於比較的結構體,包含每對比較對象的地址(unsafe.Pointer形式保存)和類型。我們要記録類型的原因是,有些不同的變量可能對應相同的地址。例如,如果x和y都是數組類型,那麽x和x[0]將對應相同的地址,y和y[0]也是對應相同的地址,這可以用於區分x與y之間的比較或x[0]與y[0]之間的比較是否進行過了。
|
||||
为了确保算法对于有环的数据结构也能正常退出,我们必须记录每次已经比较的变量,从而避免进入第二次的比较。Equal函数分配了一组用于比较的结构体,包含每对比较对象的地址(unsafe.Pointer形式保存)和类型。我们要记录类型的原因是,有些不同的变量可能对应相同的地址。例如,如果x和y都是数组类型,那么x和x[0]将对应相同的地址,y和y[0]也是对应相同的地址,这可以用于区分x与y之间的比较或x[0]与y[0]之间的比较是否进行过了。
|
||||
|
||||
```Go
|
||||
// cycle check
|
||||
@@ -96,7 +96,7 @@ if x.CanAddr() && y.CanAddr() {
|
||||
}
|
||||
```
|
||||
|
||||
這是Equal函數用法的例子:
|
||||
这是Equal函数用法的例子:
|
||||
|
||||
```Go
|
||||
fmt.Println(Equal([]int{1, 2, 3}, []int{1, 2, 3})) // "true"
|
||||
@@ -105,7 +105,7 @@ fmt.Println(Equal([]string(nil), []string{})) // "true"
|
||||
fmt.Println(Equal(map[string]int(nil), map[string]int{})) // "true"
|
||||
```
|
||||
|
||||
Equal函數甚至可以處理類似12.3章中導致Display陷入陷入死循環的帶有環的數據。
|
||||
Equal函数甚至可以处理类似12.3章中导致Display陷入陷入死循环的带有环的数据。
|
||||
|
||||
```Go
|
||||
// Circular linked lists a -> b -> a and c -> c.
|
||||
@@ -122,6 +122,6 @@ fmt.Println(Equal(a, b)) // "false"
|
||||
fmt.Println(Equal(a, c)) // "false"
|
||||
```
|
||||
|
||||
**練習 13.1:** 定義一個深比較函數,對於十億以內的數字比較,忽略類型差異。
|
||||
**练习 13.1:** 定义一个深比较函数,对于十亿以内的数字比较,忽略类型差异。
|
||||
|
||||
**練習 13.2:** 編寫一個函數,報告其參數是否循環數據結構。
|
||||
**练习 13.2:** 编写一个函数,报告其参数是否循环数据结构。
|
||||
|
||||
Reference in New Issue
Block a user