mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-12-19 20:24:20 +08:00
rebuild
This commit is contained in:
120
ch6/ch6-05.html
120
ch6/ch6-05.html
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="6.5" data-chapter-title="示例: Bit數組" data-filepath="ch6/ch6-05.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="6.5" data-chapter-title="示例: Bit數組" data-filepath="ch6/ch6-05.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,8 +2024,8 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="65-示例-bit數組">6.5. 示例: Bit數組</h2>
|
||||
<p>Go語言裏的集閤一般會用map[T]bool這種形式來錶示,T代錶元素類型。集閤用map類型來錶示雖然非常靈活,但我們可以以一種更好的形式來錶示它。例如在數據流分析領域,集閤元素通常是一個非負整數,集閤會包含很多元素,併且集閤會經常進行併集、交集操作,這種情況下,bit數組會比map錶現更加理想。(譯註:這裏再補充一個例子,比如我們執行一個http下載任務,把文件按照16kb一塊劃分為很多塊,需要有一個全侷變量來標識哪些塊下載完成了,這種時候也需要用到bit數組)</p>
|
||||
<p>一個bit數組通常會用一個無符號數或者稱之為“字”的slice或者來錶示,每一個元素的每一位都錶示集閤裏的一個值。當集閤的第i位被設置時,我們纔說這個集閤包含元素i。下麫的這個程序展示了一個簡單的bit數組類型,併且實現了三個函數來對這個bit數組來進行操作:</p>
|
||||
<p>Go語言里的集合一般會用map[T]bool這種形式來表示,T代表元素類型。集合用map類型來表示雖然非常靈活,但我們可以以一種更好的形式來表示它。例如在數據流分析領域,集合元素通常是一個非負整數,集合會包含很多元素,併且集合會經常進行併集、交集操作,這種情況下,bit數組會比map表現更加理想。(譯註:這里再補充一個例子,比如我們執行一個http下載任務,把文件按照16kb一塊劃分爲很多塊,需要有一個全局變量來標識哪些塊下載完成了,這種時候也需要用到bit數組)</p>
|
||||
<p>一個bit數組通常會用一個無符號數或者稱之爲“字”的slice或者來表示,每一個元素的每一位都表示集合里的一個值。當集合的第i位被設置時,我們纔説這個集合包含元素i。下面的這個程序展示了一個簡單的bit數組類型,併且實現了三個函數來對這個bit數組來進行操作:</p>
|
||||
<pre><code class="lang-go">gopl.io/ch6/intset
|
||||
<span class="hljs-comment">// An IntSet is a set of small non-negative integers.</span>
|
||||
<span class="hljs-comment">// Its zero value represents the empty set.</span>
|
||||
@@ -2095,8 +2059,8 @@
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>因為每一個字都有64個二進製位,所以為了定位x的bit位,我們用了x/64的商作為字的下標,併且用x%64得到的值作為這個字內的bit的所在位置。UnionWith這個方法裏用到了bit位的“或”邏輯操作符號|來一次完成64個元素的或計算。(在練習6.5中我們還會程序用到這個64位字的例子。)</p>
|
||||
<p>當前這個實現還缺少了很多必要的特性,我們把其中一些作為練習題列在本小節之後。但是有一個方法如果缺失的話我們的bit數組可能會比較難混:將IntSet作為一個字符串來打印。這裏我們來實現它,讓我們來給上麫的例子添加一個String方法,類似2.5節中做的那樣:</p>
|
||||
<p>因爲每一個字都有64個二進製位,所以爲了定位x的bit位,我們用了x/64的商作爲字的下標,併且用x%64得到的值作爲這個字內的bit的所在位置。UnionWith這個方法里用到了bit位的“或”邏輯操作符號|來一次完成64個元素的或計算。(在練習6.5中我們還會程序用到這個64位字的例子。)</p>
|
||||
<p>當前這個實現還缺少了很多必要的特性,我們把其中一些作爲練習題列在本小節之後。但是有一個方法如果缺失的話我們的bit數組可能會比較難混:將IntSet作爲一個字符串來打印。這里我們來實現它,讓我們來給上面的例子添加一個String方法,類似2.5節中做的那樣:</p>
|
||||
<pre><code class="lang-go"><span class="hljs-comment">// String returns the set as a string of the form "{1 2 3}".</span>
|
||||
<span class="hljs-keyword">func</span> (s *IntSet) String() <span class="hljs-typename">string</span> {
|
||||
<span class="hljs-keyword">var</span> buf bytes.Buffer
|
||||
@@ -2118,8 +2082,8 @@
|
||||
<span class="hljs-keyword">return</span> buf.String()
|
||||
}
|
||||
</code></pre>
|
||||
<p>這裏留意一下String方法,是不是和3.5.4節中的intsToString方法很相似;bytes.Buffer在String方法裏經常這麼用。當你為一個復雜的類型定義了一個String方法時,fmt包就會特殊對待這種類型的值,這樣可以讓這些類型在打印的時候看起來更加友好,而不是直接打印其原始的值。fmt會直接調用用戶定義的String方法。這種機製依賴於接口和類型斷言,在第7章中我們會詳細介紹。</p>
|
||||
<p>現在我們就可以在實戰中直接用上麫定義好的IntSet了:</p>
|
||||
<p>這里留意一下String方法,是不是和3.5.4節中的intsToString方法很相似;bytes.Buffer在String方法里經常這麽用。當你爲一個複雜的類型定義了一個String方法時,fmt包就會特殊對待這種類型的值,這樣可以讓這些類型在打印的時候看起來更加友好,而不是直接打印其原始的值。fmt會直接調用用戶定義的String方法。這種機製依賴於接口和類型斷言,在第7章中我們會詳細介紹。</p>
|
||||
<p>現在我們就可以在實戰中直接用上面定義好的IntSet了:</p>
|
||||
<pre><code class="lang-go"><span class="hljs-keyword">var</span> x, y IntSet
|
||||
x.Add(<span class="hljs-number">1</span>)
|
||||
x.Add(<span class="hljs-number">144</span>)
|
||||
@@ -2134,22 +2098,22 @@ x.UnionWith(&y)
|
||||
fmt.Println(x.String()) <span class="hljs-comment">// "{1 9 42 144}"</span>
|
||||
fmt.Println(x.Has(<span class="hljs-number">9</span>), x.Has(<span class="hljs-number">123</span>)) <span class="hljs-comment">// "true false"</span>
|
||||
</code></pre>
|
||||
<p>這裏要註意:我們聲明的String和Has兩個方法都是以指鍼類型*IntSet來作為接收器的,但實際上對於這兩個類型來說,把接收器聲明為指鍼類型也沒什麼必要。不過另外兩個函數就不是這樣了,因為另外兩個函數操作的是s.words對象,如果你不把接收器聲明為指鍼對象,那麼實際操作的是拷貝對象,而不是原來的那個對象。因此,因為我們的String方法定義在IntSet指鍼上,所以當我們的變量是IntSet類型而不是IntSet指鍼時,可能會有下麫這樣讓人意外的情況:</p>
|
||||
<p>這里要註意:我們聲明的String和Has兩個方法都是以指針類型*IntSet來作爲接收器的,但實際上對於這兩個類型來説,把接收器聲明爲指針類型也沒什麽必要。不過另外兩個函數就不是這樣了,因爲另外兩個函數操作的是s.words對象,如果你不把接收器聲明爲指針對象,那麽實際操作的是拷貝對象,而不是原來的那個對象。因此,因爲我們的String方法定義在IntSet指針上,所以當我們的變量是IntSet類型而不是IntSet指針時,可能會有下面這樣讓人意外的情況:</p>
|
||||
<pre><code class="lang-go">fmt.Println(&x) <span class="hljs-comment">// "{1 9 42 144}"</span>
|
||||
fmt.Println(x.String()) <span class="hljs-comment">// "{1 9 42 144}"</span>
|
||||
fmt.Println(x) <span class="hljs-comment">// "{[4398046511618 0 65536]}"</span>
|
||||
</code></pre>
|
||||
<p>在第一個Println中,我們打印一個*IntSet的指鍼,這個類型的指鍼確實有自定義的String方法。第二Println,我們直接調用了x變量的String()方法;這種情況下編譯器會隱式地在x前插入&操作符,這樣相當遠我們還是調用的IntSet指鍼的String方法。在第三個Println中,因為IntSet類型沒有String方法,所以Println方法會直接以原始的方式理解併打印。所以在這種情況下&符號是不能忘的。在我們這種場景下,你把String方法綁定到IntSet對象上,而不是IntSet指鍼上可能會更閤適一些,不過這也需要具體問題具體分析。</p>
|
||||
<p>練習6.1: 為bit數組實現下麫這些方法</p>
|
||||
<p>在第一個Println中,我們打印一個*IntSet的指針,這個類型的指針確實有自定義的String方法。第二Println,我們直接調用了x變量的String()方法;這種情況下編譯器會隱式地在x前插入&操作符,這樣相當遠我們還是調用的IntSet指針的String方法。在第三個Println中,因爲IntSet類型沒有String方法,所以Println方法會直接以原始的方式理解併打印。所以在這種情況下&符號是不能忘的。在我們這種場景下,你把String方法綁定到IntSet對象上,而不是IntSet指針上可能會更合適一些,不過這也需要具體問題具體分析。</p>
|
||||
<p>練習6.1: 爲bit數組實現下面這些方法</p>
|
||||
<pre><code class="lang-go"><span class="hljs-keyword">func</span> (*IntSet) Len() <span class="hljs-typename">int</span> <span class="hljs-comment">// return the number of elements</span>
|
||||
<span class="hljs-keyword">func</span> (*IntSet) Remove(x <span class="hljs-typename">int</span>) <span class="hljs-comment">// remove x from the set</span>
|
||||
<span class="hljs-keyword">func</span> (*IntSet) Clear() <span class="hljs-comment">// remove all elements from the set</span>
|
||||
<span class="hljs-keyword">func</span> (*IntSet) Copy() *IntSet <span class="hljs-comment">// return a copy of the set</span>
|
||||
</code></pre>
|
||||
<p>練習6.2: 定義一個變參方法(*IntSet).AddAll(...int),這個方法可以為一組IntSet值求和,比如s.AddAll(1,2,3)。</p>
|
||||
<p>練習6.3: (*IntSet).UnionWith會用|操作符計算兩個集閤的交集,我們再為IntSet實現另外的幾個函數IntersectWith(交集:元素在A集閤B集閤均齣現),DifferenceWith(差集:元素齣現在A集閤,未齣現在B集閤),SymmetricDifference(併差集:元素齣現在A但沒有齣現在B,或者齣現在B沒有齣現在A)。
|
||||
練習6.4: 實現一個Elems方法,返迴集閤中的所有元素,用於做一些range之類的遍歷操作。</p>
|
||||
<p>練習6.5: 我們這章定義的IntSet裏的每個字都是用的uint64類型,但是64位的數值可能在32位的平颱上不高效。脩改程序,使其使用uint類型,這種類型對於32位平颱來說更閤適。當然了,這裏我們可以不用簡單粗暴地除64,可以定義一個常量來決定是用32還是64,這裏你可能會用到平颱的自動判斷的一個智能錶達式:32 << (^uint(0) >> 63)</p>
|
||||
<p>練習6.2: 定義一個變參方法(*IntSet).AddAll(...int),這個方法可以爲一組IntSet值求和,比如s.AddAll(1,2,3)。</p>
|
||||
<p>練習6.3: (*IntSet).UnionWith會用|操作符計算兩個集合的交集,我們再爲IntSet實現另外的幾個函數IntersectWith(交集:元素在A集合B集合均齣現),DifferenceWith(差集:元素齣現在A集合,未齣現在B集合),SymmetricDifference(併差集:元素齣現在A但沒有齣現在B,或者齣現在B沒有齣現在A)。
|
||||
練習6.4: 實現一個Elems方法,返迴集合中的所有元素,用於做一些range之類的遍歷操作。</p>
|
||||
<p>練習6.5: 我們這章定義的IntSet里的每個字都是用的uint64類型,但是64位的數值可能在32位的平颱上不高效。脩改程序,使其使用uint類型,這種類型對於32位平颱來説更合適。當然了,這里我們可以不用簡單粗暴地除64,可以定義一個常量來決定是用32還是64,這里你可能會用到平颱的自動判斷的一個智能表達式:32 << (^uint(0) >> 63)</p>
|
||||
|
||||
|
||||
</section>
|
||||
@@ -2160,7 +2124,7 @@ fmt.Println(x) <span class="hljs-comment">// "{[4398046511618 0 65
|
||||
</div>
|
||||
|
||||
|
||||
<a href="../ch6/ch6-04.html" class="navigation navigation-prev " aria-label="Previous page: 方法值和方法錶達式"><i class="fa fa-angle-left"></i></a>
|
||||
<a href="../ch6/ch6-04.html" class="navigation navigation-prev " aria-label="Previous page: 方法值和方法表達式"><i class="fa fa-angle-left"></i></a>
|
||||
|
||||
|
||||
<a href="../ch6/ch6-06.html" class="navigation navigation-next " aria-label="Next page: 封裝"><i class="fa fa-angle-right"></i></a>
|
||||
|
||||
Reference in New Issue
Block a user