mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-12-18 11:44:20 +08:00
rebuild
This commit is contained in:
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="13.3" data-chapter-title="示例: 深度相等判斷" data-filepath="ch13/ch13-03.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="13.3" data-chapter-title="示例: 深度相等判斷" data-filepath="ch13/ch13-03.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2024,23 +2024,21 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="133-示例-深度相等判斷">13.3. 示例: 深度相等判斷</h2>
|
||||
<p>來自 reflect 包的 DeepEqual 對兩個值進行深度相等判斷. DeepEqual 使用內建的 <code>==</code> 操作符對基礎類型進行相等判斷, 對於複合類型則遞歸變量每個基礎類型然後做類似的比較判斷. 因爲它工作在任意的類型上, 甚至對一些不支持 <code>==</code> 操作符的類型也可以工作, 因此在一些測試代碼中被廣泛地使用. 比如下面的代碼是用 DeepEqual 比較兩個字符串數組是否等價.</p>
|
||||
<p>來自reflect包的DeepEqual函數可以對兩個值進行深度相等判斷。DeepEqual函數使用內建的==比較操作符對基礎類型進行相等判斷,對於複合類型則遞歸該變量的每個基礎類型然後做類似的比較判斷。因爲它可以工作在任意的類型上,甚至對於一些不支持==操作運算符的類型也可以工作,因此在一些測試代碼中廣泛地使用該函數。比如下面的代碼是用DeepEqual函數比較兩個字符串數組是否相等。</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> TestSplit(t *testing.T) {
|
||||
got := strings.Split(<span class="hljs-string">"a:b:c"</span>, <span class="hljs-string">":"</span>)
|
||||
want := []<span class="hljs-typename">string</span>{<span class="hljs-string">"a"</span>, <span class="hljs-string">"b"</span>, <span class="hljs-string">"c"</span>};
|
||||
<span class="hljs-keyword">if</span> !reflect.DeepEqual(got, want) { <span class="hljs-comment">/* ... */</span> }
|
||||
}
|
||||
</code></pre>
|
||||
<p>盡管 DeepEqual 很方便, 而且可以支持任意的類型, 但是也有不足之處.
|
||||
例如, 它將一個 nil map 和 非 nil 的空的 map 視作不相等,
|
||||
同樣 nil slice 和 非 nil 的空的 slice 也不相等.</p>
|
||||
<p>盡管DeepEqual函數很方便,而且可以支持任意的數據類型,但是它也有不足之處。例如,它將一個nil值的map和非nil值但是空的map視作不相等,同樣nil值的slice 和非nil但是空的slice也視作不相等。</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> a, b []<span class="hljs-typename">string</span> = <span class="hljs-constant">nil</span>, []<span class="hljs-typename">string</span>{}
|
||||
fmt.Println(reflect.DeepEqual(a, b)) <span class="hljs-comment">// "false"</span>
|
||||
|
||||
<span class="hljs-keyword">var</span> c, d <span class="hljs-keyword">map</span>[<span class="hljs-typename">string</span>]<span class="hljs-typename">int</span> = <span class="hljs-constant">nil</span>, <span class="hljs-built_in">make</span>(<span class="hljs-keyword">map</span>[<span class="hljs-typename">string</span>]<span class="hljs-typename">int</span>)
|
||||
fmt.Println(reflect.DeepEqual(c, d)) <span class="hljs-comment">// "false"</span>
|
||||
</code></pre>
|
||||
<p>在這里定義一個自己的 Equal 函數用於比較人員的值. 和 DeepEqual 類似的是它也是基於 slice 和 map 的元素進行遞歸比較, 不同之處是它將 nil slice(map類似) 和非 nil 的空 slice 視作相等的值. 基礎部分的比較可以基於反射完成, 和 12.3 章的 Display 實現方法類似. 同樣, 我們頂一個一個內部函數 equal, 用於內部的遞歸比較. 目前不用關心 seen 參數. 對於每一對需要比較的 x 和 y, equal 函數 首先檢測它們是否都有效(或都無效), 然後檢測它們是否是相同的類型. 剩下的部分是一個大的 switch 分支, 用於擁有相同基礎類型的比較. 因爲頁面空間的限製, 我們省略了一些類似的分支.</p>
|
||||
<p>我們希望在這里實現一個自己的Equal函數,用於比較類型的值。和DeepEqual函數類似的地方是它也是基於slice和map的每個元素進行遞歸比較,不同之處是它將nil值的slice(map類似)和非nil值但是空的slice視作相等的值。基礎部分的比較可以基於reflect包完成,和12.3章的Display函數的實現方法類似。同樣,我們也定義了一個內部函數equal,用於內部的遞歸比較。讀者目前不用關心seen參數的具體含義。對於每一對需要比較的x和y,equal函數首先檢測它們是否都有效(或都無效),然後檢測它們是否是相同的類型。剩下的部分是一個鉅大的switch分支,用於相同基礎類型的元素比較。因爲頁面空間的限製,我們省略了一些相似的分支。</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch13/equal
|
||||
<span class="hljs-keyword">func</span> equal(x, y reflect.Value, seen <span class="hljs-keyword">map</span>[comparison]<span class="hljs-typename">bool</span>) <span class="hljs-typename">bool</span> {
|
||||
<span class="hljs-keyword">if</span> !x.IsValid() || !y.IsValid() {
|
||||
@@ -2080,8 +2078,7 @@ fmt.Println(reflect.DeepEqual(c, d)) <span class="hljs-comment">// "false&q
|
||||
<span class="hljs-built_in">panic</span>(<span class="hljs-string">"unreachable"</span>)
|
||||
}
|
||||
</code></pre>
|
||||
<p>和前面的建議一樣, 我們不公開使用反射相關的接口,
|
||||
所以導齣的函數需要在內部自己將變量轉爲 reflect.Value 類型.</p>
|
||||
<p>和前面的建議一樣,我們併不公開reflect包相關的接口,所以導齣的函數需要在內部自己將變量轉爲reflect.Value類型。</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-comment">// Equal reports whether x and y are deeply equal.</span>
|
||||
<span class="hljs-keyword">func</span> Equal(x, y <span class="hljs-keyword">interface</span>{}) <span class="hljs-typename">bool</span> {
|
||||
seen := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">map</span>[comparison]<span class="hljs-typename">bool</span>)
|
||||
@@ -2093,7 +2090,7 @@ fmt.Println(reflect.DeepEqual(c, d)) <span class="hljs-comment">// "false&q
|
||||
treflect.Type
|
||||
}
|
||||
</code></pre>
|
||||
<p>爲了確保算法對於循環數據結構也能正常退齣, 我們必鬚記録每次已經比較的變量, 從而避免進入第二次的比較. Equal 函數分配了一組用於比較的結構體, 包含每對比較對象的地址(unsafe.Pointer形式保存)和類型. 我們記録類型的原因是, 有些不同的變量可能對應相同的地址. 例如, 如果 x 和 y 都是數組類型, 那麽 x 和 <code>x[0]</code> 將對應相同的地址, y 和 <code>y[0]</code> 也是對應相同的地址, 這可以用於判斷 對x 和 y 比較 或 x[0] 和 y[0] 的是否進行過了.</p>
|
||||
<p>爲了確保算法對於有環的數據結構也能正常退齣,我們必鬚記録每次已經比較的變量,從而避免進入第二次的比較。Equal函數分配了一組用於比較的結構體,包含每對比較對象的地址(unsafe.Pointer形式保存)和類型。我們要記録類型的原因是,有些不同的變量可能對應相同的地址。例如,如果x和y都是數組類型,那麽x和x[0]將對應相同的地址,y和y[0]也是對應相同的地址,這可以用於區分x與y之間的比較或x[0]與y[0]之間的比較是否進行過了。</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-comment">// cycle check</span>
|
||||
<span class="hljs-keyword">if</span> x.CanAddr() && y.CanAddr() {
|
||||
xptr := unsafe.Pointer(x.UnsafeAddr())
|
||||
@@ -2108,13 +2105,13 @@ fmt.Println(reflect.DeepEqual(c, d)) <span class="hljs-comment">// "false&q
|
||||
seen[c] = <span class="hljs-constant">true</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>這是 Equal 函數的使用的例子:</p>
|
||||
<p>這是Equal函數用法的例子:</p>
|
||||
<pre><code class="lang-Go">fmt.Println(Equal([]<span class="hljs-typename">int</span>{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>}, []<span class="hljs-typename">int</span>{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>})) <span class="hljs-comment">// "true"</span>
|
||||
fmt.Println(Equal([]<span class="hljs-typename">string</span>{<span class="hljs-string">"foo"</span>}, []<span class="hljs-typename">string</span>{<span class="hljs-string">"bar"</span>})) <span class="hljs-comment">// "false"</span>
|
||||
fmt.Println(Equal([]<span class="hljs-typename">string</span>(<span class="hljs-constant">nil</span>), []<span class="hljs-typename">string</span>{})) <span class="hljs-comment">// "true"</span>
|
||||
fmt.Println(Equal(<span class="hljs-keyword">map</span>[<span class="hljs-typename">string</span>]<span class="hljs-typename">int</span>(<span class="hljs-constant">nil</span>), <span class="hljs-keyword">map</span>[<span class="hljs-typename">string</span>]<span class="hljs-typename">int</span>{})) <span class="hljs-comment">// "true"</span>
|
||||
</code></pre>
|
||||
<p>它甚至可以處理類似12.3章中導致Display陷入死循環的數據.</p>
|
||||
<p>Equal函數甚至可以處理類似12.3章中導致Display陷入陷入死循環的帶有環的數據。</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-comment">// Circular linked lists a -> b -> a and c -> c.</span>
|
||||
<span class="hljs-keyword">type</span> link <span class="hljs-keyword">struct</span> {
|
||||
value <span class="hljs-typename">string</span>
|
||||
@@ -2128,9 +2125,9 @@ fmt.Println(Equal(c, c)) <span class="hljs-comment">// "true"</span>
|
||||
fmt.Println(Equal(a, b)) <span class="hljs-comment">// "false"</span>
|
||||
fmt.Println(Equal(a, c)) <span class="hljs-comment">// "false"</span>
|
||||
</code></pre>
|
||||
<pre><code>練習 13.1: 定義一個深比較函數, 對於十億以內的數字比較, 忽略類型差異.
|
||||
練習 13.2: 編寫一個函數, 報告其參數是否循環數據結構.
|
||||
</code></pre>
|
||||
<p><strong>練習 13.1:</strong> 定義一個深比較函數,對於十億以內的數字比較,忽略類型差異。</p>
|
||||
<p><strong>練習 13.2:</strong> 編寫一個函數,報告其參數是否循環數據結構。</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user