mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-12-18 19:54:21 +08:00
rebuild
This commit is contained in:
@@ -21,6 +21,10 @@
|
||||
<link rel="stylesheet" href="../gitbook/style.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../gitbook/plugins/gitbook-plugin-katex/katex.min.css">
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../gitbook/plugins/gitbook-plugin-highlight/website.css">
|
||||
|
||||
|
||||
@@ -44,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="3.1" data-chapter-title="整型" data-filepath="ch3/ch3-01.md" data-basepath=".." data-revision="Fri Dec 25 2015 12:32:44 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="3.1" data-chapter-title="整型" data-filepath="ch3/ch3-01.md" data-basepath=".." data-revision="Mon Dec 28 2015 16:03:52 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -238,7 +242,7 @@
|
||||
|
||||
<b>1.5.</b>
|
||||
|
||||
穫取URL
|
||||
獲取URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -253,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併發穫取多個URL
|
||||
併發獲取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -802,7 +806,7 @@
|
||||
|
||||
<b>5.10.</b>
|
||||
|
||||
Recover捕穫異常
|
||||
Recover捕獲異常
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1315,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併發的退齣
|
||||
併發的退出
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1834,7 +1838,7 @@
|
||||
|
||||
<b>12.7.</b>
|
||||
|
||||
穫取結構體字段標識
|
||||
獲取結構體字段標識
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2020,50 +2024,50 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="31-整型">3.1. 整型</h2>
|
||||
<p>Go語言的數值類型包括幾種不同大小的整形數, 浮點數, 和複數. 每種數值類型都決定了對應的大小范圍和是否有正負符號. 讓我們先從整形數類型開始介紹.</p>
|
||||
<p>Go同時提供了有符號和無符號的整數運算. 這里有四種int8, int16, int32 和 int64截然不同大小的有符號整形數類型, 分别對應 8, 16, 32, 64 bit 大小的有符號整形數, 與此對應的是 uint8, uint16, uint32, 和 uint64 四種無符號整形數類型.</p>
|
||||
<p>這里還有兩種對應特定平颱最天然或最有效率的大小有符號和無符號整數int和uint; 其中int是應用最廣泛的數值類型. 這兩種類型都有同樣的大小, 32 或 64 bit, 但是我們不能對此做任何的假設; 因爲不同的編譯器在相同的硬件平颱上可能産生不同的大小.</p>
|
||||
<p>字符rune類型是和int32等價的類型, 通常用於表示一個Unicode碼點. 這兩個名稱可以互換使用. 同樣byte也是uint8類型的等價類型, byte類型用於強調數值是一個原始的數據而不是一個小的整數.</p>
|
||||
<p>最好, 還有一個無符號的整數類型 uintptr, 沒有指定具體的bit大小但是足以容納指針. uintptr 類型隻有在底層編程是纔需要, 特别是Go語言和C函數庫或操作繫統相交互的地方. 我們將在第十三章的 unsafe 包相關部分看到類似的例子.</p>
|
||||
<p>不管它們的大小, int, uint, 和 uintptr 是不同類型大小的兄弟類型. 其中 int 和 int32 也是不同的類型, 卽使int的大小也是32bit, 在需要將int當作int32類型的地方需要一個顯式的類型轉換, 反之亦然.</p>
|
||||
<p>有符號數采用2的補碼形式表示, 也就是最高位用作符號位, 一個nbit的有符號數的值域是 <code>-2^(n-1)</code> 到 <code>(2^(n-1)) - 1</code>. 無符號整數的所有bit位都用於表示非負數, 值域是 0 到 <code>(2^n) - 1</code>. 例如, int8 的值域是 -128 到 127, 而 uint8 的值域是 0 到 255.</p>
|
||||
<p>下面是Go中關於算術, 邏輯和比較的二元運算符按照先級遞減的順序的列表:</p>
|
||||
<p>Go語言的數值類型包括幾種不同大小的整形數、浮點數和複數。每種數值類型都決定了對應的大小范圍和是否支持正負符號。讓我們先從整形數類型開始介紹。</p>
|
||||
<p>Go語言同時提供了有符號和無符號類型的整數運算。這里有int8、int16、int32和int64四種截然不同大小的有符號整形數類型,分别對應8、16、32、64bit大小的有符號整形數,與此對應的是uint8、uint16、uint32和uint64四種無符號整形數類型。</p>
|
||||
<p>這里還有兩種一般對應特定CPU平台機器字大小的有符號和無符號整數int和uint;其中int是應用最廣泛的數值類型。這兩種類型都有同樣的大小,32或64bit,但是我們不能對此做任何的假設;因爲不同的編譯器卽使在相同的硬件平台上可能産生不同的大小。</p>
|
||||
<p>Unicode字符rune類型是和int32等價的類型,通常用於表示一個Unicode碼點。這兩個名稱可以互換使用。同樣byte也是uint8類型的等價類型,byte類型一般用於強調數值是一個原始的數據而不是一個小的整數。</p>
|
||||
<p>最後,還有一種無符號的整數類型uintptr,沒有指定具體的bit大小但是足以容納指針。uintptr類型隻有在底層編程是才需要,特别是Go語言和C語言函數庫或操作繫統接口相交互的地方。我們將在第十三章的unsafe包相關部分看到類似的例子。</p>
|
||||
<p>不管它們的具體大小,int、uint和uintptr是不同類型的兄弟類型。其中int和int32也是不同的類型,卽使int的大小也是32bit,在需要將int當作int32類型的地方需要一個顯式的類型轉換操作,反之亦然。</p>
|
||||
<p>其中有符號整數采用2的補碼形式表示,也就是最高bit位用作表示符號位,一個n-bit的有符號數的值域是從<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mo>−</mo><msup><mn>2</mn><mrow><mi>n</mi><mo>−</mo><mn>1</mn></mrow></msup></mrow><annotation encoding="application/x-tex">-2^{n-1}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="strut" style="height:0.8141079999999999em;"></span><span class="strut bottom" style="height:0.897438em;vertical-align:-0.08333em;"></span><span class="base textstyle uncramped"><span class="mord">−</span><span class="mord"><span class="mord mathrm">2</span><span class="vlist"><span style="top:-0.363em;margin-right:0.05em;"><span class="fontsize-ensurer reset-size5 size5"><span style="font-size:0em;">​</span></span><span class="reset-textstyle scriptstyle uncramped"><span class="mord scriptstyle uncramped"><span class="mord mathit">n</span><span class="mbin">−</span><span class="mord mathrm">1</span></span></span></span><span class="baseline-fix"><span class="fontsize-ensurer reset-size5 size5"><span style="font-size:0em;">​</span></span>​</span></span></span></span></span></span>到<span class="katex"><span class="katex-mathml"><math><semantics><mrow><msup><mn>2</mn><mrow><mi>n</mi><mo>−</mo><mn>1</mn></mrow></msup><mo>−</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">2^{n-1}-1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="strut" style="height:0.8141079999999999em;"></span><span class="strut bottom" style="height:0.897438em;vertical-align:-0.08333em;"></span><span class="base textstyle uncramped"><span class="mord"><span class="mord mathrm">2</span><span class="vlist"><span style="top:-0.363em;margin-right:0.05em;"><span class="fontsize-ensurer reset-size5 size5"><span style="font-size:0em;">​</span></span><span class="reset-textstyle scriptstyle uncramped"><span class="mord scriptstyle uncramped"><span class="mord mathit">n</span><span class="mbin">−</span><span class="mord mathrm">1</span></span></span></span><span class="baseline-fix"><span class="fontsize-ensurer reset-size5 size5"><span style="font-size:0em;">​</span></span>​</span></span></span><span class="mbin">−</span><span class="mord mathrm">1</span></span></span></span>。無符號整數的所有bit位都用於表示非負數,值域是0到<span class="katex"><span class="katex-mathml"><math><semantics><mrow><msup><mn>2</mn><mi>n</mi></msup><mo>−</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">2^n-1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="strut" style="height:0.664392em;"></span><span class="strut bottom" style="height:0.747722em;vertical-align:-0.08333em;"></span><span class="base textstyle uncramped"><span class="mord"><span class="mord mathrm">2</span><span class="vlist"><span style="top:-0.363em;margin-right:0.05em;"><span class="fontsize-ensurer reset-size5 size5"><span style="font-size:0em;">​</span></span><span class="reset-textstyle scriptstyle uncramped"><span class="mord mathit">n</span></span></span><span class="baseline-fix"><span class="fontsize-ensurer reset-size5 size5"><span style="font-size:0em;">​</span></span>​</span></span></span><span class="mbin">−</span><span class="mord mathrm">1</span></span></span></span>。例如,int8類型整數的值域是從-128到127,而uint8類型整數的值域是從0到255。</p>
|
||||
<p>下面是Go語言中關於算術運算、邏輯運算和比較運算的二元運算符,它們按照先級遞減的順序的排列:</p>
|
||||
<pre><code>* / % << >> & &^
|
||||
+ - | ^
|
||||
== != < <= > >=
|
||||
&&
|
||||
||
|
||||
</code></pre><p>二元運算符有五種優先級. 在同一優先級, 使用左優先結合律, 使用括號可以明確優先順序, 括號也可以用於提陞優先級, 例如 <code>mask & (1 << 28)</code>.</p>
|
||||
<p>對於上表中前兩行的運算符, 例如 + 有一個相應的賦值結合運算符 +=, 可以用於簡化賦值語句.</p>
|
||||
<p>整數的算術運算符 +, -, *, 和 / 可以適用與整數, 浮點數和複數, 但是取模運算符 % 僅用於整數. 不同編程語言間, % 取模運算的行爲併不相同. 在Go語言中, % 取模運算符的符號和被取模數的符號總是一致的, 因此 <code>-5%3</code> 和 <code>-5%-3</code> 結果都是 -2.除法運算符 <code>/</code> 的行爲依賴於操作數是否爲整數, 因此 <code>5.0/4.0</code> 的結果是 1.25, 但是 5/4 的結果是 1, 因此整數除法會向着0方向截斷餘數.</p>
|
||||
<p>如果一個算術運算的結果, 不管是有符號或者是無符號的, 如果需要更多的bit位纔能表示, 就説明是溢齣了. 超齣的高位的bit位部分將被丟棄. 如果原始的數值是有符號類型, 那麽最終結果可能是負的, 如果最左邊的bit爲是1的話, 例如int8的例子:</p>
|
||||
</code></pre><p>二元運算符有五種優先級。在同一個優先級,使用左優先結合規則,但是使用括號可以明確優先順序,使用括號也可以用於提陞優先級,例如<code>mask & (1 << 28)</code>。</p>
|
||||
<p>對於上表中前兩行的運算符,例如+運算符還有一個與賦值相結合的對應運算符+=,可以用於簡化賦值語句。</p>
|
||||
<p>整數的算術運算符+、-、<code>*</code>和<code>/</code>可以適用與於整數、浮點數和複數,但是取模運算符%僅用於整數間的運算。對於不同編程語言,%取模運算的行爲可能併不相同。在Go語言中,%取模運算符的符號和被取模數的符號總是一致的,因此<code>-5%3</code>和<code>-5%-3</code>結果都是-2。除法運算符<code>/</code>的行爲則依賴於操作數是否爲全爲整數,比如<code>5.0/4.0</code>的結果是1.25,但是5/4的結果是1,因爲整數除法會向着0方向截斷餘數。</p>
|
||||
<p>如果一個算術運算的結果,不管是有符號或者是無符號的,如果需要更多的bit位才能正確表示的話,就説明計算結果是溢出了。超出的高位的bit位部分將被丟棄。如果原始的數值是有符號類型,而且最左邊的bit爲是1的話,那麽最終結果可能是負的,例如int8的例子:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> u <span class="hljs-typename">uint8</span> = <span class="hljs-number">255</span>
|
||||
fmt.Println(u, u+<span class="hljs-number">1</span>, u*u) <span class="hljs-comment">// "255 0 1"</span>
|
||||
|
||||
<span class="hljs-keyword">var</span> i <span class="hljs-typename">int8</span> = <span class="hljs-number">127</span>
|
||||
fmt.Println(i, i+<span class="hljs-number">1</span>, i*i) <span class="hljs-comment">// "127 -128 1"</span>
|
||||
</code></pre>
|
||||
<p>兩個相同的整數類型可以使用下面的二元比較運算符進行比較; 比較表達式的結果是布爾類型.</p>
|
||||
<p>兩個相同的整數類型可以使用下面的二元比較運算符進行比較;比較表達式的結果是布爾類型。</p>
|
||||
<pre><code>== equal to
|
||||
!= not equal to
|
||||
< less than
|
||||
<= less than or equal to
|
||||
> greater than
|
||||
>= greater than or equal to
|
||||
</code></pre><p>事實上, 布爾型, 數字類型 和 字符串 等基本類型都是可比較的, 也就是説兩個相同類型的值可以用 == 和 != 進行比較. 此外, 整數, 浮點數和字符串可以根據比較結果排序. 許多其他類型的值是不可比較, 因此也就是不可排序的. 對於我們遇到的每種類型, 我們需要保證規則是類似的.</p>
|
||||
<p>這里是一元的加法和減法運算符:</p>
|
||||
</code></pre><p>事實上,布爾型、數字類型和字符串等基本類型都是可比較的,也就是説兩個相同類型的值可以用==和!=進行比較。此外,整數、浮點數和字符串可以根據比較結果排序。許多其它類型的值可能是不可比較的,因此也就可能是不可排序的。對於我們遇到的每種類型,我們需要保證規則的一致性。</p>
|
||||
<p>這里是一元的加法和減法運算符:</p>
|
||||
<pre><code>+ 一元加法 (無效果)
|
||||
- 負數
|
||||
</code></pre><p>對於整數, +x 是 0+x 的簡寫, -x 是 0-x 的簡寫; 對於浮點數和複數, +x 就是 x, -x 則是 x 的負數.</p>
|
||||
<p>Go語言還提供了以下的bit位操作運算符, 前面4個操作運算符併不區分是有符號還是無符號數:</p>
|
||||
</code></pre><p>對於整數,+x是0+x的簡寫,-x則是0-x的簡寫;對於浮點數和複數,+x就是x,-x則是x 的負數。</p>
|
||||
<p>Go語言還提供了以下的bit位操作運算符,前面4個操作運算符併不區分是有符號還是無符號數:</p>
|
||||
<pre><code>& 位運算 AND
|
||||
| 位運算 OR
|
||||
^ 位運算 XOR
|
||||
&^ 位清空 (AND NOT)
|
||||
<< 左移
|
||||
>> 右移
|
||||
</code></pre><p>位操作運算符 <code>^</code> 作爲二元運算符時是按位異或(XOR), 當用作一元運算符時表示按位取反; 也就是説, 它返迴一個每個bit位都取反的數. 位操作運算符 <code>&^</code> 用於按位置零(AND NOT): 表達式 <code>z = x &^ y</code> 結果z的bit位1, 如果對應y中bit位爲1, 否則對應的bit位等於x相應的bit位的值.</p>
|
||||
<p>下面的代碼演示了如何使用位操作解釋uint8類型值的8個獨立的bit位. 它使用了 Printf 函數的 %b 參數打印二進製格式的數字; 其中 %08b 中08表示打印至少8個數字, 不足的前綴用0填充.</p>
|
||||
</code></pre><p>位操作運算符<code>^</code>作爲二元運算符時是按位異或(XOR),當用作一元運算符時表示按位取反;也就是説,它返迴一個每個bit位都取反的數。位操作運算符<code>&^</code>用於按位置零(AND NOT):表達式<code>z = x &^ y</code>結果z的bit位爲0,如果對應y中bit位爲1的話,否則對應的bit位等於x相應的bit位的值。</p>
|
||||
<p>下面的代碼演示了如何使用位操作解釋uint8類型值的8個獨立的bit位。它使用了Printf函數的%b參數打印二進製格式的數字;其中%08b中08表示打印至少8個字符寬度,不足的前綴部分用0填充。</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> x <span class="hljs-typename">uint8</span> = <span class="hljs-number">1</span><<<span class="hljs-number">1</span> | <span class="hljs-number">1</span><<<span class="hljs-number">5</span>
|
||||
<span class="hljs-keyword">var</span> y <span class="hljs-typename">uint8</span> = <span class="hljs-number">1</span><<<span class="hljs-number">1</span> | <span class="hljs-number">1</span><<<span class="hljs-number">2</span>
|
||||
|
||||
@@ -2084,41 +2088,41 @@ fmt.Printf(<span class="hljs-string">"%08b\n"</span>, x&^y) <span
|
||||
fmt.Printf(<span class="hljs-string">"%08b\n"</span>, x<<<span class="hljs-number">1</span>) <span class="hljs-comment">// "01000100", the set {2, 6}</span>
|
||||
fmt.Printf(<span class="hljs-string">"%08b\n"</span>, x>><span class="hljs-number">1</span>) <span class="hljs-comment">// "00010001", the set {0, 4}</span>
|
||||
</code></pre>
|
||||
<p>(6.5節給齣了一個可以遠大於一個字節的整數集的實現.)</p>
|
||||
<p>在 x<<n 和="" x="">>n 移位運算中, 決定了移位操作bit數部分必鬚是無符號數; 被操作的 x 數可以是有符號或無符號數. 算術上, 一個 x<<n 左移運算等價於乘以="" 2^n,="" 一個="" x="">>n 右移運算等價於除以 2^n.</n></n></p>
|
||||
<p>左移運算用零填充右邊空缺的bit位, 無符號數的右移運算也是用0填充左邊空缺的bit位, 但是有符號數的右移運算會用符號位的值填充左邊空缺的bit位. 因爲這個原因, 最好用無符號運算, 這樣你可以將整數完全當作一個bit位模式處理.</p>
|
||||
<p>盡管Go提供了無符號數和運算, 卽使數值本身不可能齣現負數我們還是傾向於使用有符號的int類型, 就是數組的長度那樣, 雖然使用 uint 似乎是一個更合理的選擇. 事實上, 內置的 len 函數返迴一個有符號的int, 我們可以像下面這個逆序循環那樣處理.</p>
|
||||
<p>(6.5節給出了一個可以遠大於一個字節的整數集的實現。)</p>
|
||||
<p>在<code>x<<n</code>和<code>x>>n</code>移位運算中,決定了移位操作bit數部分必鬚是無符號數;被操作的x數可以是有符號或無符號數。算術上,一個<code>x<<n</code>左移運算等價於乘以<span class="katex"><span class="katex-mathml"><math><semantics><mrow><msup><mn>2</mn><mi>n</mi></msup></mrow><annotation encoding="application/x-tex">2^n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="strut" style="height:0.664392em;"></span><span class="strut bottom" style="height:0.664392em;vertical-align:0em;"></span><span class="base textstyle uncramped"><span class="mord"><span class="mord mathrm">2</span><span class="vlist"><span style="top:-0.363em;margin-right:0.05em;"><span class="fontsize-ensurer reset-size5 size5"><span style="font-size:0em;">​</span></span><span class="reset-textstyle scriptstyle uncramped"><span class="mord mathit">n</span></span></span><span class="baseline-fix"><span class="fontsize-ensurer reset-size5 size5"><span style="font-size:0em;">​</span></span>​</span></span></span></span></span></span>,一個<code>x>>n</code>右移運算等價於除以<span class="katex"><span class="katex-mathml"><math><semantics><mrow><msup><mn>2</mn><mi>n</mi></msup></mrow><annotation encoding="application/x-tex">2^n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="strut" style="height:0.664392em;"></span><span class="strut bottom" style="height:0.664392em;vertical-align:0em;"></span><span class="base textstyle uncramped"><span class="mord"><span class="mord mathrm">2</span><span class="vlist"><span style="top:-0.363em;margin-right:0.05em;"><span class="fontsize-ensurer reset-size5 size5"><span style="font-size:0em;">​</span></span><span class="reset-textstyle scriptstyle uncramped"><span class="mord mathit">n</span></span></span><span class="baseline-fix"><span class="fontsize-ensurer reset-size5 size5"><span style="font-size:0em;">​</span></span>​</span></span></span></span></span></span>。</p>
|
||||
<p>左移運算用零填充右邊空缺的bit位,無符號數的右移運算也是用0填充左邊空缺的bit位,但是有符號數的右移運算會用符號位的值填充左邊空缺的bit位。因爲這個原因,最好用無符號運算,這樣你可以將整數完全當作一個bit位模式處理。</p>
|
||||
<p>盡管Go語言提供了無符號數和運算,卽使數值本身不可能出現負數我們還是傾向於使用有符號的int類型,就像數組的長度那樣,雖然使用uint無符號類型似乎是一個更合理的選擇。事實上,內置的len函數返迴一個有符號的int,我們可以像下面例子那樣處理逆序循環。</p>
|
||||
<pre><code class="lang-Go">medals := []<span class="hljs-typename">string</span>{<span class="hljs-string">"gold"</span>, <span class="hljs-string">"silver"</span>, <span class="hljs-string">"bronze"</span>}
|
||||
<span class="hljs-keyword">for</span> i := <span class="hljs-built_in">len</span>(medals) - <span class="hljs-number">1</span>; i >= <span class="hljs-number">0</span>; i-- {
|
||||
fmt.Println(medals[i]) <span class="hljs-comment">// "bronze", "silver", "gold"</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>另一個選擇將是災難性的. 如果 len 返迴一個無符號數, 那麽 i 也將是無符號的 uint, 然後條件 i >= 0 則永遠爲眞. 在三次迭代之後, 也就是 i == 0 時, i-- 語句將不會産生 -1, 而是變成一個uint的最大值(可能是 2^64 - 1), 然後 medals[i] 表達式將發生運行時 panic 異常(§5.9), 也就是試圖訪問一個切片范圍以外的元素.</p>
|
||||
<p>齣於這個原因, 無符號數往往隻有在位運算或其它特殊的運算常見纔會使用, 就像 bit 集合, 分形二進製文件格式, 或者是哈希和加密操作等. 它們通常併不用於僅僅是表達非負數量的場合.</p>
|
||||
<p>一般來説, 需要一個顯式的轉換將一個值從一種類型轉化位另一種類型, 併且算術和邏輯運算的二元操作中必鬚是相同的類型. 雖然這偶爾會導致很長的表達式, 但是它消除了所有的類型相關的問題, 也使得程序容易理解.</p>
|
||||
<p>從其他類似場景下, 考慮下面這個代碼:</p>
|
||||
<p>另一個選擇對於上面的例子來説將是災難性的。如果len函數返迴一個無符號數,那麽i也將是無符號的uint類型,然後條件<code>i >= 0</code>則永遠爲眞。在三次迭代之後,也就是<code>i == 0</code>時,i--語句將不會産生-1,而是變成一個uint類型的最大值(可能是<span class="katex"><span class="katex-mathml"><math><semantics><mrow><msup><mn>2</mn><mn>6</mn></msup><mn>4</mn><mo>−</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">2^64-1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="strut" style="height:0.8141079999999999em;"></span><span class="strut bottom" style="height:0.897438em;vertical-align:-0.08333em;"></span><span class="base textstyle uncramped"><span class="mord"><span class="mord mathrm">2</span><span class="vlist"><span style="top:-0.363em;margin-right:0.05em;"><span class="fontsize-ensurer reset-size5 size5"><span style="font-size:0em;">​</span></span><span class="reset-textstyle scriptstyle uncramped"><span class="mord mathrm">6</span></span></span><span class="baseline-fix"><span class="fontsize-ensurer reset-size5 size5"><span style="font-size:0em;">​</span></span>​</span></span></span><span class="mord mathrm">4</span><span class="mbin">−</span><span class="mord mathrm">1</span></span></span></span>),然後medals[i]表達式將發生運行時panic異常(§5.9),也就是試圖訪問一個slice范圍以外的元素。</p>
|
||||
<p>出於這個原因,無符號數往往隻有在位運算或其它特殊的運算場景才會使用,就像bit集合、分析二進製文件格式或者是哈希和加密操作等。它們通常併不用於僅僅是表達非負數量的場合。</p>
|
||||
<p>一般來説,需要一個顯式的轉換將一個值從一種類型轉化位另一種類型,併且算術和邏輯運算的二元操作中必鬚是相同的類型。雖然這偶爾會導致需要很長的表達式,但是它消除了所有和類型相關的問題,而且也使得程序容易理解。</p>
|
||||
<p>在很多場景,會遇到類似下面的代碼通用的錯誤:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> apples <span class="hljs-typename">int32</span> = <span class="hljs-number">1</span>
|
||||
<span class="hljs-keyword">var</span> oranges <span class="hljs-typename">int16</span> = <span class="hljs-number">2</span>
|
||||
<span class="hljs-keyword">var</span> compote <span class="hljs-typename">int</span> = apples + oranges <span class="hljs-comment">// compile error</span>
|
||||
</code></pre>
|
||||
<p>當嚐試編譯這三個語句時, 將産生一個錯誤信息:</p>
|
||||
<p>當嚐試編譯這三個語句時,將産生一個錯誤信息:</p>
|
||||
<pre><code>invalid operation: apples + oranges (mismatched types int32 and int16)
|
||||
</code></pre><p>這種類型不匹配的問題可以有幾種不同的方法脩複, 最常見方法是將它們都顯式轉型位一個常見類型:</p>
|
||||
</code></pre><p>這種類型不匹配的問題可以有幾種不同的方法脩複,最常見方法是將它們都顯式轉型爲一個常見類型:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> compote = <span class="hljs-typename">int</span>(apples) + <span class="hljs-typename">int</span>(oranges)
|
||||
</code></pre>
|
||||
<p>如2.5節所述, 對於每種類型T, 類型轉換操作T(x)將x轉換位T類型, 如果轉換允許的話. 許多 整形數之間的相互轉換併不會改變數值; 它們隻是告訴編譯器如何解釋這個值. 但是對於將一個大尺寸的整數類型轉位一個小尺寸的整數類型, 或者是將一個浮點數轉位整數, 可能會改變數值或丟失精度:</p>
|
||||
<p>如2.5節所述,對於每種類型T,如果轉換允許的話,類型轉換操作T(x)將x轉換爲T類型。許多整形數之間的相互轉換併不會改變數值;它們隻是告訴編譯器如何解釋這個值。但是對於將一個大尺寸的整數類型轉爲一個小尺寸的整數類型,或者是將一個浮點數轉爲整數,可能會改變數值或丟失精度:</p>
|
||||
<pre><code class="lang-Go">f := <span class="hljs-number">3.141</span> <span class="hljs-comment">// a float64</span>
|
||||
i := <span class="hljs-typename">int</span>(f)
|
||||
fmt.Println(f, i) <span class="hljs-comment">// "3.141 3"</span>
|
||||
f = <span class="hljs-number">1.99</span>
|
||||
fmt.Println(<span class="hljs-typename">int</span>(f)) <span class="hljs-comment">// "1"</span>
|
||||
</code></pre>
|
||||
<p>浮點數到整數的轉換將丟失任何小數部分, 向數軸零方向截斷. 你應該避免操作目標類型表示范圍的數值類型轉換, 因爲截斷的行爲依賴於具體的實現:</p>
|
||||
<p>浮點數到整數的轉換將丟失任何小數部分,然後向數軸零方向截斷。你應該避免對可能會超出目標類型表示范圍的數值類型轉換,因爲截斷的行爲可能依賴於具體的實現:</p>
|
||||
<pre><code class="lang-Go">f := <span class="hljs-number">1e100</span> <span class="hljs-comment">// a float64</span>
|
||||
i := <span class="hljs-typename">int</span>(f) <span class="hljs-comment">// 結果依賴於具體實現</span>
|
||||
</code></pre>
|
||||
<p>任何大小的整數字面值都可以用以0開始的八進製格式書寫, 例如 0666, 或用以0x或0X開頭的十六進製格式書寫, 例如 0xdeadbeef. 十六進製數字可以用大寫或小寫字母. 如今八進製數據通常用於POSIX操作繫統上的文件訪問權限標誌, 十六進製數字則更強調數字值的bit位模式.</p>
|
||||
<p>當使用 fmt 包打印一個數值時, 我們可以用 %d, %o, 或 %x 控製輸齣的進製格式, 就像下面的例子:</p>
|
||||
<p>任何大小的整數字面值都可以用以0開始的八進製格式書寫,例如0666;或用以0x或0X開頭的十六進製格式書寫,例如0xdeadbeef。十六進製數字可以用大寫或小寫字母。如今八進製數據通常用於POSIX操作繫統上的文件訪問權限標誌,十六進製數字則更強調數字值的bit位模式。</p>
|
||||
<p>當使用fmt包打印一個數值時,我們可以用%d、%o或%x參數控製輸出的進製格式,就像下面的例子:</p>
|
||||
<pre><code class="lang-Go">o := <span class="hljs-number">0666</span>
|
||||
fmt.Printf(<span class="hljs-string">"%d %[1]o %#[1]o\n"</span>, o) <span class="hljs-comment">// "438 666 0666"</span>
|
||||
x := <span class="hljs-typename">int64</span>(<span class="hljs-number">0xdeadbeef</span>)
|
||||
@@ -2126,14 +2130,14 @@ fmt.Printf(<span class="hljs-string">"%d %[1]x %#[1]x %#[1]X\n"</span>
|
||||
<span class="hljs-comment">// Output:</span>
|
||||
<span class="hljs-comment">// 3735928559 deadbeef 0xdeadbeef 0XDEADBEEF</span>
|
||||
</code></pre>
|
||||
<p>請註意 fmt 的兩個使用技巧. 通常 Printf 格式化字符串包含多個 % 參數時將對應相同數量的額外操作數, 但是 % 之後的 <code>[1]</code> 副詞告訴Printf函數再次使用第一個操作數. 第二, % 後的 <code>#</code> 副詞告訴 Printf 在用 %o, %x 或 %X 輸齣時生成 0, 0x 或 0X前綴.</p>
|
||||
<p>字符面值通過一對單引號直接包含對應字符. 最簡單的例子是 ASCII 中類似 'a' 字符面值, 但是我們可以通過轉義的數值來表示任意的Unicode碼點對應的字符, 馬上將會看到例子.</p>
|
||||
<p>字符使用 <code>%c</code> 參數打印, 或者是 <code>%q</code> 參數打印帶單引號的字符:</p>
|
||||
<p>請註意fmt的兩個使用技巧。通常Printf格式化字符串包含多個%參數時將會包含對應相同數量的額外操作數,但是%之後的<code>[1]</code>副詞告訴Printf函數再次使用第一個操作數。第二,%後的<code>#</code>副詞告訴Printf在用%o、%x或%X輸出時生成0、0x或0X前綴。</p>
|
||||
<p>字符面值通過一對單引號直接包含對應字符。最簡單的例子是ASCII中類似'a'寫法的字符面值,但是我們也可以通過轉義的數值來表示任意的Unicode碼點對應的字符,馬上將會看到這樣的例子。</p>
|
||||
<p>字符使用<code>%c</code>參數打印,或者是用<code>%q</code>參數打印帶單引號的字符:</p>
|
||||
<pre><code class="lang-Go">ascii := <span class="hljs-string">'a'</span>
|
||||
unicode := <span class="hljs-string">'國'</span>
|
||||
unicode := <span class="hljs-string">'国'</span>
|
||||
newline := <span class="hljs-string">'\n'</span>
|
||||
fmt.Printf(<span class="hljs-string">"%d %[1]c %[1]q\n"</span>, ascii) <span class="hljs-comment">// "97 a 'a'"</span>
|
||||
fmt.Printf(<span class="hljs-string">"%d %[1]c %[1]q\n"</span>, unicode) <span class="hljs-comment">// "22269 國 '國'"</span>
|
||||
fmt.Printf(<span class="hljs-string">"%d %[1]c %[1]q\n"</span>, unicode) <span class="hljs-comment">// "22269 国 '国'"</span>
|
||||
fmt.Printf(<span class="hljs-string">"%d %[1]q\n"</span>, newline) <span class="hljs-comment">// "10 '\n'"</span>
|
||||
</code></pre>
|
||||
|
||||
@@ -2167,7 +2171,7 @@ fmt.Printf(<span class="hljs-string">"%d %[1]q\n"</span>, newline)
|
||||
|
||||
<script>
|
||||
require(["gitbook"], function(gitbook) {
|
||||
var config = {"highlight":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2}};
|
||||
var config = {"katex":{},"highlight":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2}};
|
||||
gitbook.start(config);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
<link rel="stylesheet" href="../gitbook/style.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../gitbook/plugins/gitbook-plugin-katex/katex.min.css">
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../gitbook/plugins/gitbook-plugin-highlight/website.css">
|
||||
|
||||
|
||||
@@ -44,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="3.2" data-chapter-title="浮點數" data-filepath="ch3/ch3-02.md" data-basepath=".." data-revision="Fri Dec 25 2015 12:32:44 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="3.2" data-chapter-title="浮點數" data-filepath="ch3/ch3-02.md" data-basepath=".." data-revision="Mon Dec 28 2015 16:03:52 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -238,7 +242,7 @@
|
||||
|
||||
<b>1.5.</b>
|
||||
|
||||
穫取URL
|
||||
獲取URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -253,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併發穫取多個URL
|
||||
併發獲取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -802,7 +806,7 @@
|
||||
|
||||
<b>5.10.</b>
|
||||
|
||||
Recover捕穫異常
|
||||
Recover捕獲異常
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1315,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併發的退齣
|
||||
併發的退出
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1834,7 +1838,7 @@
|
||||
|
||||
<b>12.7.</b>
|
||||
|
||||
穫取結構體字段標識
|
||||
獲取結構體字段標識
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2020,25 +2024,25 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="32-浮點數">3.2. 浮點數</h2>
|
||||
<p>Go語言提供了兩種精度的浮點數, float32 和 float64. 它們的算術規范由 IEEE754 國際標準定義, 該浮點數規范被所有現代的CPU支持.</p>
|
||||
<p>這些數值類型的范圍可以從很微小到很鉅大. 浮點數的范圍極限值可以在 matn 包找到. 常量 math.MaxFloat32 表示 float32 能表示的最大數值, 大約是 3.4e38, 對應的 math.MaxFloat64 常量大約是 1.8e308. 它們能表示的最小值近似分别是1.4e-45 和 4.9e-324.</p>
|
||||
<p>一個 float32 類型的浮點數可以提供大約6個十進製數的精度, 而 float64 則可以提供約 15個十進製數精度; 通常應該優先使用 float64 類型, 因爲 float32 類型的纍計計算誤差很容易擴散, 併且 float32 能精度表示的正整數併不是很大:</p>
|
||||
<p>Go語言提供了兩種精度的浮點數,float32和float64。它們的算術規范由IEEE754浮點數国際標準定義,該浮點數規范被所有現代的CPU支持。</p>
|
||||
<p>這些浮點數類型的取值范圍可以從很微小到很鉅大。浮點數的范圍極限值可以在math包找到。常量math.MaxFloat32表示float32能表示的最大數值,大約是 3.4e38;對應的math.MaxFloat64常量大約是1.8e308。它們分别能表示的最小值近似爲1.4e-45和4.9e-324。</p>
|
||||
<p>一個float32類型的浮點數可以提供大約6個十進製數的精度,而float64則可以提供約15個十進製數的精度;通常應該優先使用float64類型,因爲float32類型的纍計計算誤差很容易擴散,併且float32能精確表示的正整數併不是很大(譯註:因爲float32的有效bit位隻有23個,其它的bit位用於指數和符號;當整數大於23bit能表達的范圍時,float32的表示將出現誤差):</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> f <span class="hljs-typename">float32</span> = <span class="hljs-number">16777216</span> <span class="hljs-comment">// 1 << 24</span>
|
||||
fmt.Println(f == f+<span class="hljs-number">1</span>) <span class="hljs-comment">// "true"!</span>
|
||||
</code></pre>
|
||||
<p>浮點數的字面值可以直接寫小數部分, 想這樣:</p>
|
||||
<p>浮點數的字面值可以直接寫小數部分,像這樣:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">const</span> e = <span class="hljs-number">2.71828</span> <span class="hljs-comment">// (approximately)</span>
|
||||
</code></pre>
|
||||
<p>小數點前面或後面的數字都可能被省略(例如 .707 或 1.). 很小或很大的數最好用科學計數法書寫, 通過e或E來指定指數部分:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">const</span> Avogadro = <span class="hljs-number">6.02214129e23</span>
|
||||
<span class="hljs-keyword">const</span> Planck = <span class="hljs-number">6.62606957e-34</span>
|
||||
<p>小數點前面或後面的數字都可能被省略(例如.707或1.)。很小或很大的數最好用科學計數法書寫,通過e或E來指定指數部分:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">const</span> Avogadro = <span class="hljs-number">6.02214129e23</span> <span class="hljs-comment">// 阿伏伽德羅常數</span>
|
||||
<span class="hljs-keyword">const</span> Planck = <span class="hljs-number">6.62606957e-34</span> <span class="hljs-comment">// 普朗剋常數</span>
|
||||
</code></pre>
|
||||
<p>用 Printf 函數的 %g 參數打印浮點數, 將采用緊湊的表示形式打印, 併提供足夠的精度, 但是對應表格的數據, 使用 %e (帶指數) 或 %f 的形式打印可能更合適. 所有的這三個打印形式都可以指定打印的寬度和控製打印精度.</p>
|
||||
<p>用Printf函數的%g參數打印浮點數,將采用更緊湊的表示形式打印,併提供足夠的精度,但是對應表格的數據,使用%e(帶指數)或%f的形式打印可能更合適。所有的這三個打印形式都可以指定打印的寬度和控製打印精度。</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">for</span> x := <span class="hljs-number">0</span>; x < <span class="hljs-number">8</span>; x++ {
|
||||
fmt.Printf(<span class="hljs-string">"x = %d e^x = %8.3f\n"</span>, x, math.Exp(<span class="hljs-typename">float64</span>(x)))
|
||||
}
|
||||
</code></pre>
|
||||
<p>上面代碼打印e的冪, 打印精度是小數點後三個小數精度和8個字符寬度:</p>
|
||||
<p>上面代碼打印e的冪,打印精度是小數點後三個小數精度和8個字符寬度:</p>
|
||||
<pre><code>x = 0 e^x = 1.000
|
||||
x = 1 e^x = 2.718
|
||||
x = 2 e^x = 7.389
|
||||
@@ -2047,15 +2051,15 @@ x = 4 e^x = 54.598
|
||||
x = 5 e^x = 148.413
|
||||
x = 6 e^x = 403.429
|
||||
x = 7 e^x = 1096.633
|
||||
</code></pre><p>math 包中除了提供大量常用的數學函數外, 還提供了IEEE754標準中特殊數值的創建和測試: 正無窮大和負無窮大, 分别用於表示太大溢齣的數字和除零的結果; 還有 NaN 非數, 一般用於表示無效的除法操作結果 0/0 或 Sqrt(-1).</p>
|
||||
</code></pre><p>math包中除了提供大量常用的數學函數外,還提供了IEEE754浮點數標準中定義的特殊值的創建和測試:正無窮大和負無窮大,分别用於表示太大溢出的數字和除零的結果;還有NaN非數,一般用於表示無效的除法操作結果0/0或Sqrt(-1).</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> z <span class="hljs-typename">float64</span>
|
||||
fmt.Println(z, -z, <span class="hljs-number">1</span>/z, -<span class="hljs-number">1</span>/z, z/z) <span class="hljs-comment">// "0 -0 +Inf -Inf NaN"</span>
|
||||
</code></pre>
|
||||
<p>函數 math.IsNaN 用於測試一個數是否是非數 NaN, math.NaN 則返迴非數對應的值. 雖然可以用 math.NaN 來表示一個非法的結果, 但是測試一個結果是否是非數 NaN 則是充滿風險, 因爲 NaN 和任何數都是不相等的:</p>
|
||||
<p>函數math.IsNaN用於測試一個數是否是非數NaN,math.NaN則返迴非數對應的值。雖然可以用math.NaN來表示一個非法的結果,但是測試一個結果是否是非數NaN則是充滿風險的,因爲NaN和任何數都是不相等的(譯註:在浮點數中,NaN、正無窮大和負無窮大都不是唯一的,每個都有非常多種的bit模式表示):</p>
|
||||
<pre><code class="lang-Go">nan := math.NaN()
|
||||
fmt.Println(nan == nan, nan < nan, nan > nan) <span class="hljs-comment">// "false false false"</span>
|
||||
</code></pre>
|
||||
<p>如果一個函數返迴的浮點數結果可能失敗, 最好的做法是用單獨的標誌報告失敗, 像這樣:</p>
|
||||
<p>如果一個函數返迴的浮點數結果可能失敗,最好的做法是用單獨的標誌報告失敗,像這樣:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> compute() (value <span class="hljs-typename">float64</span>, ok <span class="hljs-typename">bool</span>) {
|
||||
<span class="hljs-comment">// ...</span>
|
||||
<span class="hljs-keyword">if</span> failed {
|
||||
@@ -2064,7 +2068,7 @@ fmt.Println(nan == nan, nan < nan, nan > nan) <span class="hljs-comment">/
|
||||
<span class="hljs-keyword">return</span> result, <span class="hljs-constant">true</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>接下來的程序演示了浮點計算圖形. 它是帶有兩個參數的 z = f(x, y) 函數的三維形式, 使用了可縮放矢量圖形(SVG)格式輸齣, 一個用於矢量線繪製的XML標準. 圖3.1顯示了 sin(r)/r 函數的輸齣圖形, 其中 r 是 sqrt(x<em>x+y</em>y).</p>
|
||||
<p>接下來的程序演示了通過浮點計算生成的圖形。它是帶有兩個參數的z = f(x, y)函數的三維形式,使用了可縮放矢量圖形(SVG)格式輸出,SVG是一個用於矢量線繪製的XML標準。圖3.1顯示了sin(r)/r函數的輸出圖形,其中r是sqrt(x<em>x+y</em>y)。</p>
|
||||
<p><img src="../images/ch3-01.png" alt=""></p>
|
||||
<pre><code class="lang-Go">gopl.io/ch3/surface
|
||||
<span class="hljs-comment">// Surface computes an SVG rendering of a 3-D surface function.</span>
|
||||
@@ -2122,20 +2126,20 @@ fmt.Println(nan == nan, nan < nan, nan > nan) <span class="hljs-comment">/
|
||||
<span class="hljs-keyword">return</span> math.Sin(r) / r
|
||||
}
|
||||
</code></pre>
|
||||
<p>要註意的是 corner 返迴了兩個結果, 對應 corner 的坐標參數.</p>
|
||||
<p>要解釋程序是如何工作的需要了解基本的幾何知識, 但是我們可以跳過幾何原理, 因爲程序的重點是演示浮點運算. 程序的本質是三個不同的坐標繫中映射關繫, 如圖3.2所示. 第一個是 100x100 的二維網格, 對應整數整數坐標(i,j), 從遠處的 (0, 0) 位置開始. 我們從遠處像前面繪製, 因此遠處先繪製的多邊形有可能被前面後繪製的多邊形覆蓋.</p>
|
||||
<p>第二個坐標繫是一個三維的網格浮點坐標(x,y,z), 其中x和y是i和j的線性函數, 通過平移轉換位center的中心, 然後用xyrange繫數縮放. 高度z是函數f(x,y)的值.</p>
|
||||
<p>第三個坐標繫是一個二維的畵布, 起點(0,0)在左上角. 畵布中點的坐標用(sx, sy)表示. 我們使用等角投影將三維點</p>
|
||||
<p>要註意的是corner函數返迴了兩個結果,分别對應每個網格頂點的坐標參數。</p>
|
||||
<p>要解釋這個程序是如何工作的需要一些基本的幾何學知識,但是我們可以跳過幾何學原理,因爲程序的重點是演示浮點數運算。程序的本質是三個不同的坐標繫中映射關繫,如圖3.2所示。第一個是100x100的二維網格,對應整數整數坐標(i,j),從遠處的(0, 0)位置開始。我們從遠處向前面繪製,因此遠處先繪製的多邊形有可能被前面後繪製的多邊形覆蓋。</p>
|
||||
<p>第二個坐標繫是一個三維的網格浮點坐標(x,y,z),其中x和y是i和j的線性函數,通過平移轉換位網格單元的中心,然後用xyrange繫數縮放。高度z是函數f(x,y)的值。</p>
|
||||
<p>第三個坐標繫是一個二維的畵布,起點(0,0)在左上角。畵布中點的坐標用(sx, sy)表示。我們使用等角投影將三維點</p>
|
||||
<p><img src="../images/ch3-02.png" alt=""></p>
|
||||
<p>(x,y,z) 投影到二維的畵布中. 畵布中從遠處到右邊的點對應較大的x值和較大的y值. 併且畵布中x和y值越大, 則對應的z值越小. x和y的垂直和水平縮放繫數來自30度角的正絃和餘絃值. z的縮放繫數0.4, 是一個任意選擇的參數.</p>
|
||||
<p>對於二維網格中的每一個單位, main函數計算單元的四個頂點在畵布中對應多邊形ABCD的頂點, 其中B對應(i,j)頂點位置, A, C, 和 D是相鄰的頂點, 然後輸齣SVG的繪製指令.</p>
|
||||
<p><strong>練習3.1:</strong> 如果 f 函數返迴的是無限製的 float64 值, 那麽SVG文件可能輸齣無效的<polygon></polygon>多邊形元素(雖然許多SVG渲染器會妥善處理這類問題). 脩改程序跳過無效的多邊形.</p>
|
||||
<p><strong>練習3.2:</strong> 試驗math包中其他函數的渲染圖形. 你是否能輸齣一個egg box, moguls, 或 a saddle 圖案?</p>
|
||||
<p><strong>練習3.3:</strong>根據高度給每個多邊形上色, 那樣峯值部將是紅色(#ff0000), 谷部將是藍色(#0000ff).</p>
|
||||
<p><strong>3.4:</strong> 參考1.7節Lissajous例子的函數, 構造一個web服務器, 用於計算函數麴面然後返迴SVG數據給客戶端. 服務器必鬚設置 Content-Type 頭部:</p>
|
||||
<p>(x,y,z)投影到二維的畵布中。畵布中從遠處到右邊的點對應較大的x值和較大的y值。併且畵布中x和y值越大,則對應的z值越小。x和y的垂直和水平縮放繫數來自30度角的正絃和餘絃值。z的縮放繫數0.4,是一個任意選擇的參數。</p>
|
||||
<p>對於二維網格中的每一個網格單元,main函數計算單元的四個頂點在畵布中對應多邊形ABCD的頂點,其中B對應(i,j)頂點位置,A、C和D是其它相鄰的頂點,然後輸出SVG的繪製指令。</p>
|
||||
<p><strong>練習 3.1:</strong> 如果f函數返迴的是無限製的float64值,那麽SVG文件可能輸出無效的<polygon></polygon>多邊形元素(雖然許多SVG渲染器會妥善處理這類問題)。脩改程序跳過無效的多邊形。</p>
|
||||
<p><strong>練習 3.2:</strong> 試驗math包中其他函數的渲染圖形。你是否能輸出一個egg box、moguls或a saddle圖案?</p>
|
||||
<p><strong>練習 3.3:</strong> 根據高度給每個多邊形上色,那樣峯值部將是紅色(#ff0000),谷部將是藍色(#0000ff)。</p>
|
||||
<p><strong>練習 3.4:</strong> 參考1.7節Lissajous例子的函數,構造一個web服務器,用於計算函數麴面然後返迴SVG數據給客戶端。服務器必鬚設置Content-Type頭部:</p>
|
||||
<pre><code class="lang-Go">w.Header().Set(<span class="hljs-string">"Content-Type"</span>, <span class="hljs-string">"image/svg+xml"</span>)
|
||||
</code></pre>
|
||||
<p>(這一步在Lissajous例子中不是必鬚的, 因爲服務器使用標準的PNG圖像格式, 可以根據前面的512個字節自動輸齣對應的頭部.) 允許客戶端通過HTTP請求參數設置高度, 寬度, 和顔色等參數.</p>
|
||||
<p>(這一步在Lissajous例子中不是必鬚的,因爲服務器使用標準的PNG圖像格式,可以根據前面的512個字節自動輸出對應的頭部。)允許客戶端通過HTTP請求參數設置高度、寬度和顔色等參數。</p>
|
||||
|
||||
|
||||
</section>
|
||||
@@ -2167,7 +2171,7 @@ fmt.Println(nan == nan, nan < nan, nan > nan) <span class="hljs-comment">/
|
||||
|
||||
<script>
|
||||
require(["gitbook"], function(gitbook) {
|
||||
var config = {"highlight":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2}};
|
||||
var config = {"katex":{},"highlight":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2}};
|
||||
gitbook.start(config);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
<link rel="stylesheet" href="../gitbook/style.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../gitbook/plugins/gitbook-plugin-katex/katex.min.css">
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../gitbook/plugins/gitbook-plugin-highlight/website.css">
|
||||
|
||||
|
||||
@@ -44,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="3.3" data-chapter-title="複數" data-filepath="ch3/ch3-03.md" data-basepath=".." data-revision="Fri Dec 25 2015 12:32:44 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="3.3" data-chapter-title="複數" data-filepath="ch3/ch3-03.md" data-basepath=".." data-revision="Mon Dec 28 2015 16:03:52 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -238,7 +242,7 @@
|
||||
|
||||
<b>1.5.</b>
|
||||
|
||||
穫取URL
|
||||
獲取URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -253,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併發穫取多個URL
|
||||
併發獲取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -802,7 +806,7 @@
|
||||
|
||||
<b>5.10.</b>
|
||||
|
||||
Recover捕穫異常
|
||||
Recover捕獲異常
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1315,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併發的退齣
|
||||
併發的退出
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1834,7 +1838,7 @@
|
||||
|
||||
<b>12.7.</b>
|
||||
|
||||
穫取結構體字段標識
|
||||
獲取結構體字段標識
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2020,25 +2024,25 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="33-複數">3.3. 複數</h2>
|
||||
<p>Go提供了兩種精度的複數類似, complex64 和 complex128, 分别對應 float32 和 float64精度. 內置的 complex 函數用於構建複數, 內建的 real 和 imag 函數返迴複數的實部和虛部:</p>
|
||||
<p>Go語言提供了兩種精度的複數類型:complex64和complex128,分别對應float32和float64兩種浮點數精度。內置的complex函數用於構建複數,內建的real和imag函數分别返迴複數的實部和虛部:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> x <span class="hljs-typename">complex128</span> = <span class="hljs-built_in">complex</span>(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>) <span class="hljs-comment">// 1+2i</span>
|
||||
<span class="hljs-keyword">var</span> y <span class="hljs-typename">complex128</span> = <span class="hljs-built_in">complex</span>(<span class="hljs-number">3</span>, <span class="hljs-number">4</span>) <span class="hljs-comment">// 3+4i</span>
|
||||
fmt.Println(x*y) <span class="hljs-comment">// "(-5+10i)"</span>
|
||||
fmt.Println(<span class="hljs-built_in">real</span>(x*y)) <span class="hljs-comment">// "-5"</span>
|
||||
fmt.Println(<span class="hljs-built_in">imag</span>(x*y)) <span class="hljs-comment">// "10"</span>
|
||||
</code></pre>
|
||||
<p>如果一個浮點數面值或一個十進製整數面值後面跟着一個i, 例如 3.141592i 或 2i, 它將構成一個複數的虛部, 複數的實部是0:</p>
|
||||
<p>如果一個浮點數面值或一個十進製整數面值後面跟着一個i,例如3.141592i或2i,它將構成一個複數的虛部,複數的實部是0:</p>
|
||||
<pre><code class="lang-Go">fmt.Println(<span class="hljs-number">1i</span> * <span class="hljs-number">1i</span>) <span class="hljs-comment">// "(-1+0i)", i^2 = -1</span>
|
||||
</code></pre>
|
||||
<p>在常量算術規則下, 一個複數常量可以加到另一個常量(整數或浮點數, 實部或虛部), 我們可以用自然的方式寫複數, 就像 1+2i, 或與之等價的寫法 2i+1. 上面x和y的聲明語句還可以簡化:</p>
|
||||
<p>在常量算術規則下,一個複數常量可以加到另一個普通數值常量(整數或浮點數、實部或虛部),我們可以用自然的方式書寫複數,就像1+2i或與之等價的寫法2i+1。上面x和y的聲明語句還可以簡化:</p>
|
||||
<pre><code class="lang-Go">x := <span class="hljs-number">1</span> + <span class="hljs-number">2i</span>
|
||||
y := <span class="hljs-number">3</span> + <span class="hljs-number">4i</span>
|
||||
</code></pre>
|
||||
<p>複數也可以用 == 和 != 進行相等比較. 隻有兩個複數的實部和虛部都相等的時候它們纔是相等的.</p>
|
||||
<p>math/cmplx 包提供了複數處理的許多函數, 例如求複數的平方根函數和求冪函數.</p>
|
||||
<p>複數也可以用==和!=進行相等比較。隻有兩個複數的實部和虛部都相等的時候它們才是相等的(譯註:浮點數的相等比較是危險的,需要特别小心處理精度問題)。</p>
|
||||
<p>math/cmplx包提供了複數處理的許多函數,例如求複數的平方根函數和求冪函數。</p>
|
||||
<pre><code class="lang-Go">fmt.Println(cmplx.Sqrt(-<span class="hljs-number">1</span>)) <span class="hljs-comment">// "(0+1i)"</span>
|
||||
</code></pre>
|
||||
<p>下面的程序使用complex128複數算法來生成一個Mandelbrot圖像.</p>
|
||||
<p>下面的程序使用complex128複數算法來生成一個Mandelbrot圖像。</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch3/mandelbrot
|
||||
|
||||
<span class="hljs-comment">// Mandelbrot emits a PNG image of the Mandelbrot fractal.</span>
|
||||
@@ -2086,13 +2090,13 @@ y := <span class="hljs-number">3</span> + <span class="hljs-number">4i</span>
|
||||
<span class="hljs-keyword">return</span> color.Black
|
||||
}
|
||||
</code></pre>
|
||||
<p>遍歷1024x1024圖像每個點的兩個嵌套的循環對應 -2 到 +2 區間的複數平面. 程序反複測試每個點對應複數值平方值加一個增量值對應的點是否超齣半徑爲2的圓. 如果超過了, 通過根據逃逸的迭代次數對應的灰度顔色來代替. 如果不是, 該點屬於Mandelbrot集合, 使用黑色顔色標記. 最終程序將生成的PNG格式分形圖像圖像輸齣到標準輸齣, 如圖3.3所示.</p>
|
||||
<p><strong>練習3.5:</strong> 實現一個綵色的Mandelbrot圖像, 使用 image.NewRGBA 創建圖像, 使用 color.RGBA 或 color.YCbCr 生成顔色.</p>
|
||||
<p><strong>練習3.6:</strong> 超采樣技術可以降低每個像素對計算顔色值和平均值的影響. 簡單的方法是將每個像素分層四個子像素, 實現它.</p>
|
||||
<p><strong>練習3.7:</strong> 另一個生成分形圖像的方式是使用牛頓法來求解一個複數方程, 例如 z^4 − 1 = 0. 每個起點到四個根的迭代次數對應陰影的灰度. 方程根對應的點用顔色表示.</p>
|
||||
<p>用於遍歷1024x1024圖像每個點的兩個嵌套的循環對應-2到+2區間的複數平面。程序反複測試每個點對應複數值平方值加一個增量值對應的點是否超出半徑爲2的圓。如果超過了,通過根據預設置的逃逸迭代次數對應的灰度顔色來代替。如果不是,那麽該點屬於Mandelbrot集合,使用黑色顔色標記。最終程序將生成的PNG格式分形圖像圖像輸出到標準輸出,如圖3.3所示。</p>
|
||||
<p><img src="../images/ch3-03.png" alt=""></p>
|
||||
<p><strong>練習3.8:</strong> 通過提高精度來生成更多級别的分形. 使用四種不同精度類型的數字實現相同的分形: complex64, complex128, big.Float, and big.Rat. (後面兩種類型在 math/big 包聲明. Float是有指定限精度的浮點數; Rat是無效精度的有理數.) 它們間的性能和內存使用對比如何? 當渲染圖可見時縮放的級别是多少?</p>
|
||||
<p><strong>練習3.9:</strong> 編寫一個web服務器, 用於給客戶端生成分形的圖像. 運行客戶端用過HTTP參數參數指定x,y和zoom參數.</p>
|
||||
<p><strong>練習 3.5:</strong> 實現一個綵色的Mandelbrot圖像,使用image.NewRGBA創建圖像,使用color.RGBA或color.YCbCr生成顔色。</p>
|
||||
<p><strong>練習 3.6:</strong> 陞采樣技術可以降低每個像素對計算顔色值和平均值的影響。簡單的方法是將每個像素分層四個子像素,實現它。</p>
|
||||
<p><strong>練習 3.7:</strong> 另一個生成分形圖像的方式是使用牛頓法來求解一個複數方程,例如<span class="katex"><span class="katex-mathml"><math><semantics><mrow><msup><mi>z</mi><mn>4</mn></msup><mo>−</mo><mn>1</mn><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">z^4-1=0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="strut" style="height:0.8141079999999999em;"></span><span class="strut bottom" style="height:0.897438em;vertical-align:-0.08333em;"></span><span class="base textstyle uncramped"><span class="mord"><span class="mord mathit" style="margin-right:0.04398em;">z</span><span class="vlist"><span style="top:-0.363em;margin-right:0.05em;"><span class="fontsize-ensurer reset-size5 size5"><span style="font-size:0em;">​</span></span><span class="reset-textstyle scriptstyle uncramped"><span class="mord mathrm">4</span></span></span><span class="baseline-fix"><span class="fontsize-ensurer reset-size5 size5"><span style="font-size:0em;">​</span></span>​</span></span></span><span class="mbin">−</span><span class="mord mathrm">1</span><span class="mrel">=</span><span class="mord mathrm">0</span></span></span></span>。每個起點到四個根的迭代次數對應陰影的灰度。方程根對應的點用顔色表示。</p>
|
||||
<p><strong>練習 3.8:</strong> 通過提高精度來生成更多級别的分形。使用四種不同精度類型的數字實現相同的分形:complex64、complex128、big.Float和big.Rat。(後面兩種類型在math/big包聲明。Float是有指定限精度的浮點數;Rat是無效精度的有理數。)它們間的性能和內存使用對比如何?當渲染圖可見時縮放的級别是多少?</p>
|
||||
<p><strong>練習 3.9:</strong> 編寫一個web服務器,用於給客戶端生成分形的圖像。運行客戶端用過HTTP參數參數指定x,y和zoom參數。</p>
|
||||
|
||||
|
||||
</section>
|
||||
@@ -2124,7 +2128,7 @@ y := <span class="hljs-number">3</span> + <span class="hljs-number">4i</span>
|
||||
|
||||
<script>
|
||||
require(["gitbook"], function(gitbook) {
|
||||
var config = {"highlight":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2}};
|
||||
var config = {"katex":{},"highlight":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2}};
|
||||
gitbook.start(config);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
<link rel="stylesheet" href="../gitbook/style.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../gitbook/plugins/gitbook-plugin-katex/katex.min.css">
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../gitbook/plugins/gitbook-plugin-highlight/website.css">
|
||||
|
||||
|
||||
@@ -44,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="3.4" data-chapter-title="布爾型" data-filepath="ch3/ch3-04.md" data-basepath=".." data-revision="Fri Dec 25 2015 12:32:44 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="3.4" data-chapter-title="布爾型" data-filepath="ch3/ch3-04.md" data-basepath=".." data-revision="Mon Dec 28 2015 16:03:52 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -238,7 +242,7 @@
|
||||
|
||||
<b>1.5.</b>
|
||||
|
||||
穫取URL
|
||||
獲取URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -253,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併發穫取多個URL
|
||||
併發獲取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -802,7 +806,7 @@
|
||||
|
||||
<b>5.10.</b>
|
||||
|
||||
Recover捕穫異常
|
||||
Recover捕獲異常
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1315,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併發的退齣
|
||||
併發的退出
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1834,7 +1838,7 @@
|
||||
|
||||
<b>12.7.</b>
|
||||
|
||||
穫取結構體字段標識
|
||||
獲取結構體字段標識
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2020,19 +2024,19 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="34-布爾型">3.4. 布爾型</h2>
|
||||
<p>一個布爾類型的值隻有兩種 true 和 false. if 和 for 語句的條件部分都是布爾類型的值, 併且 == 和 < 等比較操作也會産生布爾型的值. 一元操作符 <code>!</code> 對應邏輯非操作, 因此 <code>!true</code> 的值爲 <code>false</code>, 也可以説是 <code>(!true==false)==true</code>, 雖然表達方式不一樣, 不過我們一般會采用簡潔的布爾表達式, 就像用 x 來表示 <code>x==true</code>.</p>
|
||||
<p>布爾值可以和 && (AND) 和 || (OR) 操作符結合, 併且可能會有短路行爲: 如果運算符左邊值已經可以確定整個布爾表達式的值, 那麽運算符右邊的值將不在被評估, 因此下面的表達式總是安全的:</p>
|
||||
<p>一個布爾類型的值隻有兩種:true和false。if和for語句的條件部分都是布爾類型的值,併且==和<等比較操作也會産生布爾型的值。一元操作符<code>!</code>對應邏輯非操作,因此<code>!true</code>的值爲<code>false</code>,更羅嗦的説法是<code>(!true==false)==true</code>,雖然表達方式不一樣,不過我們一般會采用簡潔的布爾表達式,就像用x來表示<code>x==true</code>。</p>
|
||||
<p>布爾值可以和&&(AND)和||(OR)操作符結合,併且可能會有短路行爲:如果運算符左邊值已經可以確定整個布爾表達式的值,那麽運算符右邊的值將不在被求值,因此下面的表達式總是安全的:</p>
|
||||
<pre><code class="lang-Go">s != <span class="hljs-string">""</span> && s[<span class="hljs-number">0</span>] == <span class="hljs-string">'x'</span>
|
||||
</code></pre>
|
||||
<p>其中 s[0] 應用於空字符串會導致 panic 異常.</p>
|
||||
<p>因爲 <code>&&</code> 的優先級比 <code>||</code> 高 (助記: <code>&&</code> 對應邏輯乘法, <code>||</code> 對應邏輯加法, 乘法比加法優先級要高), 下面形式的布爾表達式是不需要加小括弧的:</p>
|
||||
<p>其中s[0]操作如果應用於空字符串將會導致panic異常。</p>
|
||||
<p>因爲<code>&&</code>的優先級比<code>||</code>高(助記:<code>&&</code>對應邏輯乘法,<code>||</code>對應邏輯加法,乘法比加法優先級要高),下面形式的布爾表達式是不需要加小括弧的:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">if</span> <span class="hljs-string">'a'</span> <= c && c <= <span class="hljs-string">'z'</span> ||
|
||||
<span class="hljs-string">'A'</span> <= c && c <= <span class="hljs-string">'Z'</span> ||
|
||||
<span class="hljs-string">'0'</span> <= c && c <= <span class="hljs-string">'9'</span> {
|
||||
<span class="hljs-comment">// ...ASCII letter or digit...</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>布爾值併不會隱式轉換爲數字值0或1, 反之亦然. 必鬚使用一個顯式的if語句輔助轉換:</p>
|
||||
<p>布爾值併不會隱式轉換爲數字值0或1,反之亦然。必鬚使用一個顯式的if語句輔助轉換:</p>
|
||||
<pre><code class="lang-Go">i := <span class="hljs-number">0</span>
|
||||
<span class="hljs-keyword">if</span> b {
|
||||
i = <span class="hljs-number">1</span>
|
||||
@@ -2082,7 +2086,7 @@
|
||||
|
||||
<script>
|
||||
require(["gitbook"], function(gitbook) {
|
||||
var config = {"highlight":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2}};
|
||||
var config = {"katex":{},"highlight":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2}};
|
||||
gitbook.start(config);
|
||||
});
|
||||
</script>
|
||||
|
||||
172
ch3/ch3-05.html
172
ch3/ch3-05.html
@@ -21,6 +21,10 @@
|
||||
<link rel="stylesheet" href="../gitbook/style.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../gitbook/plugins/gitbook-plugin-katex/katex.min.css">
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../gitbook/plugins/gitbook-plugin-highlight/website.css">
|
||||
|
||||
|
||||
@@ -44,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="3.5" data-chapter-title="字符串" data-filepath="ch3/ch3-05.md" data-basepath=".." data-revision="Fri Dec 25 2015 12:32:44 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="3.5" data-chapter-title="字符串" data-filepath="ch3/ch3-05.md" data-basepath=".." data-revision="Mon Dec 28 2015 16:03:52 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -238,7 +242,7 @@
|
||||
|
||||
<b>1.5.</b>
|
||||
|
||||
穫取URL
|
||||
獲取URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -253,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併發穫取多個URL
|
||||
併發獲取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -802,7 +806,7 @@
|
||||
|
||||
<b>5.10.</b>
|
||||
|
||||
Recover捕穫異常
|
||||
Recover捕獲異常
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1315,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併發的退齣
|
||||
併發的退出
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1834,7 +1838,7 @@
|
||||
|
||||
<b>12.7.</b>
|
||||
|
||||
穫取結構體字段標識
|
||||
獲取結構體字段標識
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2020,48 +2024,48 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="35-字符串">3.5. 字符串</h2>
|
||||
<p>一個字符串是一個不可改變的字節序列. 字符串可以包含任意的數據, 包括字節值0, 但是通常包含人類可讀的文本. 文本字符串通常被解釋爲采用UTF8編碼的Unicode碼點(rune)序列, 我們稍後會詳細討論這個問題.</p>
|
||||
<p>內置的 len 函數可以返迴一個字符串的字節數目(不是rune字符數目), 索引操作 s[i] 返迴第i個字節的字節值, i 必鬚滿足 0 ≤ i< len(s) 條件約束.</p>
|
||||
<p>一個字符串是一個不可改變的字節序列。字符串可以包含任意的數據,包括byte值0,但是通常是用來包含人類可讀的文本。文本字符串通常被解釋爲采用UTF8編碼的Unicode碼點(rune)序列,我們稍後會詳細討論這個問題。</p>
|
||||
<p>內置的len函數可以返迴一個字符串中的字節數目(不是rune字符數目),索引操作s[i]返迴第i個字節的字節值,i必鬚滿足0 ≤ i< len(s)條件約束。</p>
|
||||
<pre><code class="lang-Go">s := <span class="hljs-string">"hello, world"</span>
|
||||
fmt.Println(<span class="hljs-built_in">len</span>(s)) <span class="hljs-comment">// "12"</span>
|
||||
fmt.Println(s[<span class="hljs-number">0</span>], s[<span class="hljs-number">7</span>]) <span class="hljs-comment">// "104 119" ('h' and 'w')</span>
|
||||
</code></pre>
|
||||
<p>如果視圖訪問超齣字符串范圍的字節將會導致panic異常:</p>
|
||||
<p>如果試圖訪問超出字符串索引范圍的字節將會導致panic異常:</p>
|
||||
<pre><code class="lang-Go">c := s[<span class="hljs-built_in">len</span>(s)] <span class="hljs-comment">// panic: index out of range</span>
|
||||
</code></pre>
|
||||
<p>第i個字節併不一定是字符串的第i個字符, 因此對於非ASCII字符的UTF8編碼會要兩個或多個字節. 我們簡單説下字符的工作方式.</p>
|
||||
<p>子字符串操作s[i:j]基於原始的s字符串的第i個字節開始到第j個字節(併不包含j本身)生成一個新字符串. 生成的子字符串將包含 j-i 個字節.</p>
|
||||
<p>第i個字節併不一定是字符串的第i個字符,因爲對於非ASCII字符的UTF8編碼會要兩個或多個字節。我們先簡單説下字符的工作方式。</p>
|
||||
<p>子字符串操作s[i:j]基於原始的s字符串的第i個字節開始到第j個字節(併不包含j本身)生成一個新字符串。生成的新字符串將包含j-i個字節。</p>
|
||||
<pre><code class="lang-Go">fmt.Println(s[<span class="hljs-number">0</span>:<span class="hljs-number">5</span>]) <span class="hljs-comment">// "hello"</span>
|
||||
</code></pre>
|
||||
<p>同樣, 如果索引超齣字符串范圍或者j小於i的話將導致panic異常.</p>
|
||||
<p>不管i還是j都可能被忽略, 當它們被忽略時將采用0作爲開始位置, 采用 len(s) 作爲接受的位置.</p>
|
||||
<p>同樣,如果索引超出字符串范圍或者j小於i的話將導致panic異常。</p>
|
||||
<p>不管i還是j都可能被忽略,當它們被忽略時將采用0作爲開始位置,采用len(s)作爲結束的位置。</p>
|
||||
<pre><code class="lang-Go">fmt.Println(s[:<span class="hljs-number">5</span>]) <span class="hljs-comment">// "hello"</span>
|
||||
fmt.Println(s[<span class="hljs-number">7</span>:]) <span class="hljs-comment">// "world"</span>
|
||||
fmt.Println(s[:]) <span class="hljs-comment">// "hello, world"</span>
|
||||
</code></pre>
|
||||
<p>其中 + 操作符將兩個字符串鏈接構造一個新字符串:</p>
|
||||
<p>其中+操作符將兩個字符串鏈接構造一個新字符串:</p>
|
||||
<pre><code class="lang-Go">fmt.Println(<span class="hljs-string">"goodbye"</span> + s[<span class="hljs-number">5</span>:]) <span class="hljs-comment">// "goodbye, world"</span>
|
||||
</code></pre>
|
||||
<p>字符串可以用 == 和 < 進行比較; 比較通過逐個字節比較完成的, 因此比較的結果是字符串自然編碼的順序.</p>
|
||||
<p>字符串的值是不可變的: 一個字符串包含的字節序列永遠不會被改變, 當然我們也可以給一個字符串變量分配一個新字符串值. 可以像下面這樣將一個字符串追加到另一個字符串</p>
|
||||
<p>字符串可以用==和<進行比較;比較通過逐個字節比較完成的,因此比較的結果是字符串自然編碼的順序。</p>
|
||||
<p>字符串的值是不可變的:一個字符串包含的字節序列永遠不會被改變,當然我們也可以給一個字符串變量分配一個新字符串值。可以像下面這樣將一個字符串追加到另一個字符串:</p>
|
||||
<pre><code class="lang-Go">s := <span class="hljs-string">"left foot"</span>
|
||||
t := s
|
||||
s += <span class="hljs-string">", right foot"</span>
|
||||
</code></pre>
|
||||
<p>這併不會導致原始的字符串值被改變, 但是 s 將因爲 += 語句持有一個新的字符串值, 但是 t 依然是包含原先的字符串值.</p>
|
||||
<p>這併不會導致原始的字符串值被改變,但是變量s將因爲+=語句持有一個新的字符串值,但是t依然是包含原先的字符串值。</p>
|
||||
<pre><code class="lang-Go">fmt.Println(s) <span class="hljs-comment">// "left foot, right foot"</span>
|
||||
fmt.Println(t) <span class="hljs-comment">// "left foot"</span>
|
||||
</code></pre>
|
||||
<p>因爲字符串是不可脩改的, 因此嚐試脩改字符串內部數據的操作是被禁止的:</p>
|
||||
<p>因爲字符串是不可脩改的,因此嚐試脩改字符串內部數據的操作也是被禁止的:</p>
|
||||
<pre><code class="lang-Go">s[<span class="hljs-number">0</span>] = <span class="hljs-string">'L'</span> <span class="hljs-comment">// compile error: cannot assign to s[0]</span>
|
||||
</code></pre>
|
||||
<p>不變性意味如果兩個字符串共享相同的底層數據是安全的, 這使得複製任何長度的字符串代價是低廉的. 同樣, 一個字符串 s 和對應的子字符串 s[7:] 也可以安全地共享相同的內存, 因此字符串切片操作代價也是低廉的. 在這兩種情況下都沒有必要分配新的內存. 圖3.4 演示了一個字符串和兩個字串共享相同的底層數據.</p>
|
||||
<p>不變性意味如果兩個字符串共享相同的底層數據的話也是安全的,這使得複製任何長度的字符串代價是低廉的。同樣,一個字符串s和對應的子字符串切片s[7:]的操作也可以安全地共享相同的內存,因此字符串切片操作代價也是低廉的。在這兩種情況下都沒有必要分配新的內存。 圖3.4演示了一個字符串和兩個字串共享相同的底層數據。</p>
|
||||
<h3 id="351-字符串面值">3.5.1. 字符串面值</h3>
|
||||
<p>字符串值也可以用字符串面值方式編寫, 隻要將一繫列字節序列包含在雙引號卽可:</p>
|
||||
<p>字符串值也可以用字符串面值方式編寫,隻要將一繫列字節序列包含在雙引號卽可:</p>
|
||||
<pre><code>"Hello, 世界"
|
||||
</code></pre><p><img src="../images/ch3-04.png" alt=""></p>
|
||||
<p>因爲Go語言源文件總是用UTF8編碼, 併且Go的文本字符串也以UTF8編碼的方式處理, 我們可以將Unicode碼點也寫到字符串面值中.</p>
|
||||
<p>在一個雙引號包含的字符串面值中, 可以用以反斜槓\開頭的轉義序列插入任意的數據. 下面換行, 迴車和 製表符等常見的ASCII控製代碼的轉義方式:</p>
|
||||
<p>因爲Go語言源文件總是用UTF8編碼,併且Go語言的文本字符串也以UTF8編碼的方式處理,因此我們可以將Unicode碼點也寫到字符串面值中。</p>
|
||||
<p>在一個雙引號包含的字符串面值中,可以用以反斜槓<code>\</code>開頭的轉義序列插入任意的數據。下面的換行、迴車和製表符等是常見的ASCII控製代碼的轉義方式:</p>
|
||||
<pre><code>\a 響鈴
|
||||
\b 退格
|
||||
\f 換頁
|
||||
@@ -2072,9 +2076,9 @@ fmt.Println(t) <span class="hljs-comment">// "left foot"</span>
|
||||
\' 單引號 (隻用在 '\'' 形式的rune符號面值中)
|
||||
\" 雙引號 (隻用在 "..." 形式的字符串面值中)
|
||||
\\ 反斜槓
|
||||
</code></pre><p>可以通過十六進製或八進製轉義在字符串面值包含任意的字節. 一個十六進製的轉義是 \xhh, 其中兩個h表示十六進製數字(大寫或小寫都可以). 一個八進製轉義是 \ooo, 包含三個八進製的o數字(0到7), 但是不能超過\377. 每一個單一的字節表達一個特定的值. 稍後我們將看到如何將一個Unicode碼點寫到字符串面值中.</p>
|
||||
<p>一個原生的字符串面值形式是 <code>...</code>, 使用反引號 ``` 代替雙引號. 在原生的字符串面值中, 沒有轉義操作; 全部的內容都是字面的意思, 包含退格和換行, 因此一個程序中的原生字符串面值可能跨越多行. 唯一的特殊處理是是刪除迴車以保證在所有平颱上的值都是一樣的, 包括那些把迴車也放入文本文件的繫統.</p>
|
||||
<p>原生字符串面值用於編寫正則表達式會很方便, 因爲正則表達式往往會包含很多反斜槓. 原生字符串面值同時廣泛應用於HTML模闆, JSON面值, 命令行提示信息, 以及那些需要擴展到多行的場景.</p>
|
||||
</code></pre><p>可以通過十六進製或八進製轉義在字符串面值包含任意的字節。一個十六進製的轉義形式是\xhh,其中兩個h表示十六進製數字(大寫或小寫都可以)。一個八進製轉義形式是\ooo,包含三個八進製的o數字(0到7),但是不能超過<code>\377</code>(譯註:對應一個字節的范圍,十進製爲255)。每一個單一的字節表達一個特定的值。稍後我們將看到如何將一個Unicode碼點寫到字符串面值中。</p>
|
||||
<p>一個原生的字符串面值形式是<code>...</code>,使用反引號<code>代替雙引號。在原生的字符串面值中,沒有轉義操作;全部的內容都是字面的意思,包含退格和換行,因此一個程序中的原生字符串面值可能跨越多行(譯註:在原生字符串面值內部是無法直接寫</code>字符的,可以用八進製或十六進製轉義或+"```"鏈接字符串常量完成)。唯一的特殊處理是會刪除迴車以保證在所有平台上的值都是一樣的,包括那些把迴車也放入文本文件的繫統(譯註:Windows繫統會把迴車和換行一起放入文本文件中)。</p>
|
||||
<p>原生字符串面值用於編寫正則表達式會很方便,因爲正則表達式往往會包含很多反斜槓。原生字符串面值同時被廣泛應用於HTML模闆、JSON面值、命令行提示信息以及那些需要擴展到多行的場景。</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">const</span> GoUsage = <span class="hljs-string">`Go is a tool for managing Go source code.
|
||||
|
||||
Usage:
|
||||
@@ -2082,38 +2086,38 @@ Usage:
|
||||
...`</span>
|
||||
</code></pre>
|
||||
<h3 id="352-unicode">3.5.2. Unicode</h3>
|
||||
<p>在很久以前, 世界比較簡單的, 起碼計算機就隻有一個ASCII字符集: 美國信息交換標準代碼. ASCII, 更準確地説是美國的ASCII, 使用 7bit 來表示 128 個字符: 包含英文字母的大小寫, 數字, 各種標點符號和設置控製符. 對於早期的計算機程序, 這些足夠了, 但是這也導致了世界上很多其他地區的用戶無法直接使用自己的書寫繫統. 隨着互聯網的發展, 混合多種語言的數據變了很常見. 如何有效處理這些包含了各種語言的豐富多樣的數據呢?</p>
|
||||
<p>答案就是使用Unicode(unicode.org), 它收集了這個世界上所有的書寫繫統, 包括重音符號和其他變音符號, 製表符和迴車符, 還有很多神祕符號, 每個符號都分配一個Unicode碼點, Unicode碼點對應Go語言中的rune類型.</p>
|
||||
<p>第八版本的Unicode標準收集了超過120,000個字符, 涵蓋超過100種語言. 這些在計算機程序和數據中是如何體現的那? 通用的表示一個Unicode碼點的數據類型是int32, 也就是Go語言中rune對應的類型; 它的同義詞rune符文正是這個意思.</p>
|
||||
<p>我們可以將一個符文序列表示爲一個int32序列. 這種編碼方式叫UTF-32或UCS-4, 每個Unicode碼點都使用同樣的大小32bit來表示. 這種方式比較簡單統一, 它會浪費很多存儲空間, 因爲大數據計算機可讀的文本是ASCII字符, 本來每個ASCII字符隻需要8bit或1字節就能表示. 卽使是常用的字符也遠少於65,536個, 也就是説用16bit編碼方式就能表達常用字符. 但是, 還有更好的編碼方法嗎?</p>
|
||||
<p>在很久以前,世界還是比較簡單的,起碼計算機世界就隻有一個ASCII字符集:美国信息交換標準代碼。ASCII,更準確地説是美国的ASCII,使用7bit來表示128個字符:包含英文字母的大小寫、數字、各種標點符號和設置控製符。對於早期的計算機程序來説,這些就足夠了,但是這也導致了世界上很多其他地區的用戶無法直接使用自己的符號繫統。隨着互聯網的發展,混合多種語言的數據變得很常見(譯註:比如本身的英文原文或中文翻譯都包含了ASCII、中文、日文等多種語言字符)。如何有效處理這些包含了各種語言的豐富多樣的文本數據呢?</p>
|
||||
<p>答案就是使用Unicode( <a href="http://unicode.org" target="_blank">http://unicode.org</a> ),它收集了這個世界上所有的符號繫統,包括重音符號和其它變音符號,製表符和迴車符,還有很多神祕的符號,每個符號都分配一個唯一的Unicode碼點,Unicode碼點對應Go語言中的rune整數類型(譯註:rune是int32等價類型)。</p>
|
||||
<p>在第八版本的Unicode標準收集了超過120,000個字符,涵蓋超過100多種語言。這些在計算機程序和數據中是如何體現的呢?通用的表示一個Unicode碼點的數據類型是int32,也就是Go語言中rune對應的類型;它的同義詞rune符文正是這個意思。</p>
|
||||
<p>我們可以將一個符文序列表示爲一個int32序列。這種編碼方式叫UTF-32或UCS-4,每個Unicode碼點都使用同樣的大小32bit來表示。這種方式比較簡單統一,但是它會浪費很多存儲空間,因爲大數據計算機可讀的文本是ASCII字符,本來每個ASCII字符隻需要8bit或1字節就能表示。而且卽使是常用的字符也遠少於65,536個,也就是説用16bit編碼方式就能表達常用字符。但是,還有其它更好的編碼方法嗎?</p>
|
||||
<h3 id="353-utf8">3.5.3. UTF-8</h3>
|
||||
<p>UTF8是一個將Unicode碼點編碼爲字節序列的變長編碼. UTF8編碼由Go語言之父 Ken Thompson 和 Rob Pike 共同發明, 現在已經是Unicode的標準. UTF8使用1到4個字節來表示每個Unicode碼點符號, ASCII部分字符隻使用1個字節, 常用字符部分使用2或3個字節. 每個符號編碼後第一個字節的高端bit位用於表示總共有多少個字節. 如果第一個字節的高端bit爲0, 則表示對應7bit的ASCII字符, 每個字符一個字節, 和傳統的ASCII編碼兼容. 如果第一個字節的高端bit是110, 則説明需要2個字節; 後續的每個高端bit都以10開頭. 更大的Unicode碼點也是采用類似的策略處理.</p>
|
||||
<p>UTF8是一個將Unicode碼點編碼爲字節序列的變長編碼。UTF8編碼由Go語言之父Ken Thompson和Rob Pike共同發明的,現在已經是Unicode的標準。UTF8編碼使用1到4個字節來表示每個Unicode碼點,ASCII部分字符隻使用1個字節,常用字符部分使用2或3個字節表示。每個符號編碼後第一個字節的高端bit位用於表示總共有多少編碼個字節。如果第一個字節的高端bit爲0,則表示對應7bit的ASCII字符,ASCII字符每個字符依然是一個字節,和傳統的ASCII編碼兼容。如果第一個字節的高端bit是110,則説明需要2個字節;後續的每個高端bit都以10開頭。更大的Unicode碼點也是采用類似的策略處理。</p>
|
||||
<pre><code>0xxxxxxx runes 0-127 (ASCII)
|
||||
110xxxxx 10xxxxxx 128-2047 (values <128 unused)
|
||||
1110xxxx 10xxxxxx 10xxxxxx 2048-65535 (values <2048 unused)
|
||||
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 65536-0x10ffff (other values unused)
|
||||
</code></pre><p>變長的編碼無法直接通過索引來訪問第n個字符, 但是UTF8穫得了很多額外的優點. 首先UTF8編碼比較緊湊, 兼容ASCII, 併且可以自動同步: 它可以通過向前迴朔最多2個字節就能確定當前字符編碼的開始字節的位置. 它也是一個前綴編碼, 所以當從左向右解碼時不會有任何歧義也併不需要向前査看. 沒有任何字符的編碼是其它字符編碼的子串, 或是其它編碼序列的字串, 因此蒐索一個字符時隻要蒐索它的字節編碼序列卽可, 不用擔心前後的上下文會對蒐索結果産生榦擾. 同時UTF8編碼的順序和Unicode碼點的順序一致, 因此可以直接排序UTF8編碼序列. 同業也沒有嵌入的NUL(0)字節, 可以很好地兼容那些使用NUL作爲字符串結尾的編程語言.</p>
|
||||
<p>Go的源文件采用UTF8編碼, 併且Go處理UTF8編碼的文本也很齣色. unicode 包提供了諸多處理 rune 字符相關功能的函數函數(區分字母和數組, 或者是字母的大寫和小寫轉換等), unicode/utf8 包了提供了rune 字符序列的UTF8編碼和解碼的功能.</p>
|
||||
<p>有很多Unicode字符很難直接從鍵盤輸入, 併且很多字符有着相似的結構; 有一些甚至是不可見的字符. Go字符串面值中的Unicode轉義字符讓我們可以通過Unicode碼點輸入特殊的字符. 有兩種形式, \uhhhh 對應16bit的碼點值, \Uhhhhhhhh 對應32bit的碼點值, 其中h是一個十六進製數字; 一般很少需要使用32bit的形式. 每一個對應碼點的UTF8編碼. 例如: 下面的字母串面值都表示相同的值:</p>
|
||||
</code></pre><p>變長的編碼無法直接通過索引來訪問第n個字符,但是UTF8編碼獲得了很多額外的優點。首先UTF8編碼比較緊湊,完全兼容ASCII碼,併且可以自動同步:它可以通過向前迴朔最多2個字節就能確定當前字符編碼的開始字節的位置。它也是一個前綴編碼,所以當從左向右解碼時不會有任何歧義也併不需要向前査看(譯註:像GBK之類的編碼,如果不知道起點位置則可能會出現歧義)。沒有任何字符的編碼是其它字符編碼的子串,或是其它編碼序列的字串,因此蒐索一個字符時隻要蒐索它的字節編碼序列卽可,不用擔心前後的上下文會對蒐索結果産生榦擾。同時UTF8編碼的順序和Unicode碼點的順序一致,因此可以直接排序UTF8編碼序列。同時因爲沒有嵌入的NUL(0)字節,可以很好地兼容那些使用NUL作爲字符串結尾的編程語言。</p>
|
||||
<p>Go語言的源文件采用UTF8編碼,併且Go語言處理UTF8編碼的文本也很出色。unicode包提供了諸多處理rune字符相關功能的函數(比如區分字母和數組,或者是字母的大寫和小寫轉換等),unicode/utf8包則提供了用於rune字符序列的UTF8編碼和解碼的功能。</p>
|
||||
<p>有很多Unicode字符很難直接從鍵盤輸入,併且還有很多字符有着相似的結構;有一些甚至是不可見的字符(譯註:中文和日文就有很多相似但不同的字)。Go語言字符串面值中的Unicode轉義字符讓我們可以通過Unicode碼點輸入特殊的字符。有兩種形式:\uhhhh對應16bit的碼點值,\Uhhhhhhhh對應32bit的碼點值,其中h是一個十六進製數字;一般很少需要使用32bit的形式。每一個對應碼點的UTF8編碼。例如:下面的字母串面值都表示相同的值:</p>
|
||||
<pre><code>"世界"
|
||||
"\xe4\xb8\x96\xe7\x95\x8c"
|
||||
"\u4e16\u754c"
|
||||
"\U00004e16\U0000754c"
|
||||
</code></pre><p>上面三個轉義序列爲第一個字符串提供替代寫法, 但是它們的值都是相同的.</p>
|
||||
<p>Unicode轉義也可以使用在rune字符中. 下面三個字符是等價的:</p>
|
||||
</code></pre><p>上面三個轉義序列都爲第一個字符串提供替代寫法,但是它們的值都是相同的。</p>
|
||||
<p>Unicode轉義也可以使用在rune字符中。下面三個字符是等價的:</p>
|
||||
<pre><code>'世' '\u4e16' '\U00004e16'
|
||||
</code></pre><p>對於小於256碼點值可以寫在一個十六進製轉義字節中, 例如 '\x41' 對應 'A' 字符, 但是對於更大的碼點則必鬚使用 \u 或 \U 轉義形式. 因此, '\xe4\xb8\x96' 併不是一個合法的rune字符, 雖然這三個字節對應一個有效的UTF8編碼的碼點.</p>
|
||||
<p>得意於UTF8優良的設計, 諸多字符串操作都不需要解碼. 我們可以不用解碼直接測試一個字符串是否是另一個字符串的前綴:</p>
|
||||
</code></pre><p>對於小於256碼點值可以寫在一個十六進製轉義字節中,例如'\x41'對應字符'A',但是對於更大的碼點則必鬚使用\u或\U轉義形式。因此,'\xe4\xb8\x96'併不是一個合法的rune字符,雖然這三個字節對應一個有效的UTF8編碼的碼點。</p>
|
||||
<p>得益於UTF8編碼優良的設計,諸多字符串操作都不需要解碼操作。我們可以不用解碼直接測試一個字符串是否是另一個字符串的前綴:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> HasPrefix(s, prefix <span class="hljs-typename">string</span>) <span class="hljs-typename">bool</span> {
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-built_in">len</span>(s) >= <span class="hljs-built_in">len</span>(prefix) && s[:<span class="hljs-built_in">len</span>(prefix)] == prefix
|
||||
}
|
||||
</code></pre>
|
||||
<p>或者是後綴測試:</p>
|
||||
<p>或者是後綴測試:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> HasSuffix(s, suffix <span class="hljs-typename">string</span>) <span class="hljs-typename">bool</span> {
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-built_in">len</span>(s) >= <span class="hljs-built_in">len</span>(suffix) && s[<span class="hljs-built_in">len</span>(s)-<span class="hljs-built_in">len</span>(suffix):] == suffix
|
||||
}
|
||||
</code></pre>
|
||||
<p>或者是包含子串測試:</p>
|
||||
<p>或者是包含子串測試:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> Contains(s, substr <span class="hljs-typename">string</span>) <span class="hljs-typename">bool</span> {
|
||||
<span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i < <span class="hljs-built_in">len</span>(s); i++ {
|
||||
<span class="hljs-keyword">if</span> HasPrefix(s[i:], substr) {
|
||||
@@ -2123,72 +2127,72 @@ Usage:
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-constant">false</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>對於UTF8編碼後文本的處理和原始的字節處理邏輯一樣. 但是對應很多其它編碼則併不是這樣的. (上面的函數都來自 strings 字符串處理包, 雖然它們的實現包含了一個用哈希技術優化的 Contains 實現.)</p>
|
||||
<p>另以方面, 如果我們眞的關心每個Unicode字符, 我們可以使用其它機製. 考慮前面的第一個例子中的字符串, 它包混合了中西兩種字符. 圖3.5展示了它的內存表示形式. 字符串包含13個字節, 以UTF8形式編碼, 但是隻對應9個Unicode字符:</p>
|
||||
<p>對於UTF8編碼後文本的處理和原始的字節處理邏輯是一樣的。但是對應很多其它編碼則併不是這樣的。(上面的函數都來自strings字符串處理包,眞實的代碼包含了一個用哈希技術優化的Contains 實現。)</p>
|
||||
<p>另一方面,如果我們眞的關心每個Unicode字符,我們可以使用其它處理方式。考慮前面的第一個例子中的字符串,它包混合了中西兩種字符。圖3.5展示了它的內存表示形式。字符串包含13個字節,以UTF8形式編碼,但是隻對應9個Unicode字符:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">import</span> <span class="hljs-string">"unicode/utf8"</span>
|
||||
|
||||
s := <span class="hljs-string">"Hello, 世界"</span>
|
||||
fmt.Println(<span class="hljs-built_in">len</span>(s)) <span class="hljs-comment">// "13"</span>
|
||||
fmt.Println(utf8.RuneCountInString(s)) <span class="hljs-comment">// "9"</span>
|
||||
</code></pre>
|
||||
<p>爲了處理這些眞實的字符, 我們需要一個UTF8解碼器. unicode/utf8 包提供了實現, 我們可以這樣使用:</p>
|
||||
<p>爲了處理這些眞實的字符,我們需要一個UTF8解碼器。unicode/utf8包提供了該功能,我們可以這樣使用:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i < <span class="hljs-built_in">len</span>(s); {
|
||||
r, size := utf8.DecodeRuneInString(s[i:])
|
||||
fmt.Printf(<span class="hljs-string">"%d\t%c\n"</span>, i, r)
|
||||
i += size
|
||||
}
|
||||
</code></pre>
|
||||
<p>每一次調用 DecodeRuneInString 函數都返迴一個 r 和 長度, r 對應字符本身, 長度對應r采用UTF8編碼後的字節數目. 長度可以用於更新第i個字符在字符串中的字節索引位置. 但是這種方式是笨拙的, 我們需要更簡潔的語法. 幸運的是, Go的range循環在處理字符串的時候, 會自動隱式解碼UTF8字符串. 下面的循環運行如圖3.5所示; 需要註意的是對於非ASCII, 索引更新的步長超過1個字節.</p>
|
||||
<p>每一次調用DecodeRuneInString函數都返迴一個r和長度,r對應字符本身,長度對應r采用UTF8編碼後的編碼字節數目。長度可以用於更新第i個字符在字符串中的字節索引位置。但是這種編碼方式是笨拙的,我們需要更簡潔的語法。幸運的是,Go語言的range循環在處理字符串的時候,會自動隱式解碼UTF8字符串。下面的循環運行如圖3.5所示;需要註意的是對於非ASCII,索引更新的步長將超過1個字節。</p>
|
||||
<p><img src="../images/ch3-05.png" alt=""></p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">for</span> i, r := <span class="hljs-keyword">range</span> <span class="hljs-string">"Hello, 世界"</span> {
|
||||
fmt.Printf(<span class="hljs-string">"%d\t%q\t%d\n"</span>, i, r, r)
|
||||
}
|
||||
</code></pre>
|
||||
<p>我們可以使用一個簡單的循環來統計字符串中字符的數目, 像這樣:</p>
|
||||
<p>我們可以使用一個簡單的循環來統計字符串中字符的數目,像這樣:</p>
|
||||
<pre><code class="lang-Go">n := <span class="hljs-number">0</span>
|
||||
<span class="hljs-keyword">for</span> _, _ = <span class="hljs-keyword">range</span> s {
|
||||
n++
|
||||
}
|
||||
</code></pre>
|
||||
<p>想其它形式的循環那樣, 我們可以忽略不需要的變量:</p>
|
||||
<p>像其它形式的循環那樣,我們也可以忽略不需要的變量:</p>
|
||||
<pre><code class="lang-Go">n := <span class="hljs-number">0</span>
|
||||
<span class="hljs-keyword">for</span> <span class="hljs-keyword">range</span> s {
|
||||
n++
|
||||
}
|
||||
</code></pre>
|
||||
<p>或者我們可以直接調用 utf8.RuneCountInString(s) 函數.</p>
|
||||
<p>正如我們前面提到了, 文本字符串采用UTF8編碼隻是一種慣例,但是對於循環的眞正字符串併不是一個慣例, 這是正確的. 如果用於循環的字符串隻是一個普通的二進製數據, 或者是含有錯誤編碼的UTF8數據, 將會發送什麽?</p>
|
||||
<p>每一個UTF8字符解碼, 不管是顯示地調用 utf8.DecodeRuneInString 解碼或在 range 循環中隱式地解碼, 如果遇到一個錯誤的輸入字節, 將生成一個特别的Unicode字符 '\uFFFD', 在印刷中這個符號通常是一個黑色六角或鑽石形狀, 里面包含一個白色的問號(?). 當程序遇到這樣的一個字符, 通常是一個信號, 説明輸入併不是一個完美沒有錯誤的的UTF8編碼字符串.</p>
|
||||
<p>UTF8作爲交換格式是非常方便的, 但是在程序內部采用rune類型可能更方便, 因爲rune大小一致, 支持數組索引和方便切割.</p>
|
||||
<p>string 接受到 []rune 的轉換, 可以將一個UTF8編碼的字符串解碼爲Unicode字符序列:</p>
|
||||
<p>或者我們可以直接調用utf8.RuneCountInString(s)函數。</p>
|
||||
<p>正如我們前面提到的,文本字符串采用UTF8編碼隻是一種慣例,但是對於循環的眞正字符串併不是一個慣例,這是正確的。如果用於循環的字符串隻是一個普通的二進製數據,或者是含有錯誤編碼的UTF8數據,將會發送什麽呢?</p>
|
||||
<p>每一個UTF8字符解碼,不管是顯式地調用utf8.DecodeRuneInString解碼或是在range循環中隱式地解碼,如果遇到一個錯誤的UTF8編碼輸入,將生成一個特别的Unicode字符'\uFFFD',在印刷中這個符號通常是一個黑色六角或鑽石形狀,里面包含一個白色的問號(?)。當程序遇到這樣的一個字符,通常是一個危險信號,説明輸入併不是一個完美沒有錯誤的UTF8字符串。</p>
|
||||
<p>UTF8字符串作爲交換格式是非常方便的,但是在程序內部采用rune序列可能更方便,因爲rune大小一致,支持數組索引和方便切割。</p>
|
||||
<p>string接受到[]rune的類型轉換,可以將一個UTF8編碼的字符串解碼爲Unicode字符序列:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-comment">// "program" in Japanese katakana</span>
|
||||
s := <span class="hljs-string">"プログラム"</span>
|
||||
fmt.Printf(<span class="hljs-string">"% x\n"</span>, s) <span class="hljs-comment">// "e3 83 97 e3 83 ad e3 82 b0 e3 83 a9 e3 83 a0"</span>
|
||||
r := []<span class="hljs-typename">rune</span>(s)
|
||||
fmt.Printf(<span class="hljs-string">"%x\n"</span>, r) <span class="hljs-comment">// "[30d7 30ed 30b0 30e9 30e0]"</span>
|
||||
</code></pre>
|
||||
<p>(在第一個Printf中的 <code>% x</code> 參數用於在每個十六進製數字前插入一個空格.)</p>
|
||||
<p>如果是將一個 []rune 類型的Unicode字符切片或數組轉爲string, 則對它們進行UTF8編碼:</p>
|
||||
<p>(在第一個Printf中的<code>% x</code>參數用於在每個十六進製數字前插入一個空格。)</p>
|
||||
<p>如果是將一個[]rune類型的Unicode字符slice或數組轉爲string,則對它們進行UTF8編碼:</p>
|
||||
<pre><code class="lang-Go">fmt.Println(<span class="hljs-typename">string</span>(r)) <span class="hljs-comment">// "プログラム"</span>
|
||||
</code></pre>
|
||||
<p>將一個整數轉型爲字符串意思是生成整數作爲Unicode碼點的UTF8編碼的字符串: </p>
|
||||
<p>將一個整數轉型爲字符串意思是生成以隻包含對應Unicode碼點字符的UTF8字符串:</p>
|
||||
<pre><code class="lang-Go">fmt.Println(<span class="hljs-typename">string</span>(<span class="hljs-number">65</span>)) <span class="hljs-comment">// "A", not "65"</span>
|
||||
fmt.Println(<span class="hljs-typename">string</span>(<span class="hljs-number">0x4eac</span>)) <span class="hljs-comment">// "京"</span>
|
||||
</code></pre>
|
||||
<p>如果對應碼點的字符是無效的, 則用'\uFFFD'無效字符作爲替換:</p>
|
||||
<p>如果對應碼點的字符是無效的,則用'\uFFFD'無效字符作爲替換:</p>
|
||||
<pre><code class="lang-Go">fmt.Println(<span class="hljs-typename">string</span>(<span class="hljs-number">1234567</span>)) <span class="hljs-comment">// "(?)"</span>
|
||||
</code></pre>
|
||||
<h3 id="354-字符串和byte切片">3.5.4. 字符串和Byte切片</h3>
|
||||
<p>標準庫中有四個包對字符串處理尤爲重要: bytes, strings, strconv, 和 unicode. strings 包提供了許多如字符串的査詢, 替換, 比較, 截斷, 拆分, 和合併等功能.</p>
|
||||
<p>bytes 包也提供了很多類似功能的函數, 但是針對和字符串有着相同結構的 []byte 類型. 因爲字符串是隻讀的, 因此逐步構建字符串會導致很多分配和複製. 在這種情況下, 使用 bytes.Buffer 類型會更有效, 稍後我們將展示.</p>
|
||||
<p>strconv 包提供了 布爾型, 整型數, 浮點數和對應字符串間的相互轉換, 還提供了雙引號的字符串面值形式的轉換.</p>
|
||||
<p>unicode 包提供了類似 IsDigit, IsLetter, IsUpper, 和 IsLower 等功能, 它們用於給字符分類. 每個函數有一個單一的rune類型的參數, 然後返迴一個布爾值. 像 ToUpper 和 ToLower 之類的轉換函數將用於rune字符的大小寫轉換. 所有的這些函數都是遵循Unicode標準定義的字母,數字等分類規范. strings 包也有類似的函數, 它們是 ToUpper 和 ToLower, 將原始字符串的每個字符都做相應的轉換, 然後返迴新的字符串.</p>
|
||||
<p>下面的 basename 函數的靈感由Unix shell的同名工具而來. 在我們實現的版本中, basename(s) 將看起來像是繫統路徑的前綴刪除, 同時將看似文件類型的後綴名刪除:</p>
|
||||
<p>標準庫中有四個包對字符串處理尤爲重要:bytes、strings、strconv和unicode包。strings包提供了許多如字符串的査詢、替換、比較、截斷、拆分和合併等功能。</p>
|
||||
<p>bytes包也提供了很多類似功能的函數,但是針對和字符串有着相同結構的[]byte類型。因爲字符串是隻讀的,因此逐步構建字符串會導致很多分配和複製。在這種情況下,使用bytes.Buffer類型將會更有效,稍後我們將展示。</p>
|
||||
<p>strconv包提供了布爾型、整型數、浮點數和對應字符串的相互轉換,還提供了雙引號轉義相關的轉換。</p>
|
||||
<p>unicode包提供了IsDigit、IsLetter、IsUpper和IsLower等類似功能,它們用於給字符分類。每個函數有一個單一的rune類型的參數,然後返迴一個布爾值。而像ToUpper和ToLower之類的轉換函數將用於rune字符的大小寫轉換。所有的這些函數都是遵循Unicode標準定義的字母、數字等分類規范。strings包也有類似的函數,它們是ToUpper和ToLower,將原始字符串的每個字符都做相應的轉換,然後返迴新的字符串。</p>
|
||||
<p>下面例子的basename函數靈感於Unix shell的同名工具。在我們實現的版本中,basename(s)將看起來像是繫統路徑的前綴刪除,同時將看似文件類型的後綴名部分刪除:</p>
|
||||
<pre><code class="lang-Go">fmt.Println(basename(<span class="hljs-string">"a/b/c.go"</span>)) <span class="hljs-comment">// "c"</span>
|
||||
fmt.Println(basename(<span class="hljs-string">"c.d.go"</span>)) <span class="hljs-comment">// "c.d"</span>
|
||||
fmt.Println(basename(<span class="hljs-string">"abc"</span>)) <span class="hljs-comment">// "abc"</span>
|
||||
</code></pre>
|
||||
<p>第一個版本併沒有使用任何庫, 全部手工實現:</p>
|
||||
<p>第一個版本併沒有使用任何庫,全部手工硬編碼實現:</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch3/basename1
|
||||
<span class="hljs-comment">// basename removes directory components and a .suffix.</span>
|
||||
<span class="hljs-comment">// e.g., a => a, a.go => a, a/b/c.go => c, a/b.c.go => b.c</span>
|
||||
@@ -2210,7 +2214,7 @@ fmt.Println(basename(<span class="hljs-string">"abc"</span>)) <sp
|
||||
<span class="hljs-keyword">return</span> s
|
||||
}
|
||||
</code></pre>
|
||||
<p>一個簡化的版本使用了 strings.LastIndex 庫函數:</p>
|
||||
<p>簡化個版本使用了strings.LastIndex庫函數:</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch3/basename2
|
||||
|
||||
<span class="hljs-keyword">func</span> basename(s <span class="hljs-typename">string</span>) <span class="hljs-typename">string</span> {
|
||||
@@ -2222,8 +2226,8 @@ fmt.Println(basename(<span class="hljs-string">"abc"</span>)) <sp
|
||||
<span class="hljs-keyword">return</span> s
|
||||
}
|
||||
</code></pre>
|
||||
<p>path 和 path/filepath 包提供了關於文件名更一般的函數操作. 使用斜槓分隔路徑可以在任何操作繫統上工作. 斜槓本身不應該用於文件名, 但是在其他一些領域可能是有效的, 例如URL路徑組件. 相比之下, path/filepath 包使用操作繫統本身的路徑規則, 例如 POSIX 繫統使用 /foo/bar, Microsoft Windows 使用 c:\foo\bar 等.</p>
|
||||
<p>讓我們繼續另一個字符串的例子. 任務是將一個表示整值的字符串, 每隔三個字符插入一個逗號, 例如 "12345" 處理後成爲 "12,345". 這個版本隻適用於整數類型; 支持浮點數類型的支持留做練習.</p>
|
||||
<p>path和path/filepath包提供了關於文件路徑名更一般的函數操作。使用斜槓分隔路徑可以在任何操作繫統上工作。斜槓本身不應該用於文件名,但是在其他一些領域可能會用於文件名,例如URL路徑組件。相比之下,path/filepath包則使用操作繫統本身的路徑規則,例如POSIX繫統使用/foo/bar,而Microsoft Windows使用c:\foo\bar等。</p>
|
||||
<p>讓我們繼續另一個字符串的例子。函數的功能是將一個表示整值的字符串,每隔三個字符插入一個逗號分隔符,例如“12345”處理後成爲“12,345”。這個版本隻適用於整數類型;支持浮點數類型的支持留作練習。</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch3/comma
|
||||
|
||||
<span class="hljs-comment">// comma inserts commas in a non-negative decimal integer string.</span>
|
||||
@@ -2235,15 +2239,15 @@ fmt.Println(basename(<span class="hljs-string">"abc"</span>)) <sp
|
||||
<span class="hljs-keyword">return</span> comma(s[:n-<span class="hljs-number">3</span>]) + <span class="hljs-string">","</span> + s[n-<span class="hljs-number">3</span>:]
|
||||
}
|
||||
</code></pre>
|
||||
<p>輸入 comma 的參數是一個字符串. 如果輸入字符串的長度小於或等於3的話, 則不需要插入逗號. 否則, comma 將在最後三個字符前切割爲兩個兩個子串, 然後用前面的子串遞歸調用自身.</p>
|
||||
<p>一個字符串包含的字節數組, 一旦創建, 是不可變的. 相比之下, 一個字節切片的原始則可以自由地脩改.</p>
|
||||
<p>字符串和字節切片可以相互轉換:</p>
|
||||
<p>輸入comma函數的參數是一個字符串。如果輸入字符串的長度小於或等於3的話,則不需要插入逗分隔符。否則,comma函數將在最後三個字符前位置將字符串切割爲兩個兩個子串併插入逗號分隔符,然後通過遞歸調用自身來出前面的子串。</p>
|
||||
<p>一個字符串是包含的隻讀字節數組,一旦創建,是不可變的。相比之下,一個字節slice的元素則可以自由地脩改。</p>
|
||||
<p>字符串和字節slice之間可以相互轉換:</p>
|
||||
<pre><code class="lang-Go">s := <span class="hljs-string">"abc"</span>
|
||||
b := []<span class="hljs-typename">byte</span>(s)
|
||||
s2 := <span class="hljs-typename">string</span>(b)
|
||||
</code></pre>
|
||||
<p>從概念上講, []byte(s) 轉換是分配了一個新的字節數組保存了字符串數據的拷貝, 然後引用這個字節數組. 編譯器的優化可以避免在一些場景下分配和複製字符串數據, 但總的來説需要確保在b被脩改的情況下, 原始的s字符串也不會改變. 將一個字節切片轉到字符串的 string(b) 操作則是構造一個拷貝, 以確保s2字符串是隻讀的.</p>
|
||||
<p>爲了避免轉換中不必要的內存分配, bytes包和strings同時提供了許多類似的實用函數. 下面是strings包中的六個函數:</p>
|
||||
<p>從概念上講,一個[]byte(s)轉換是分配了一個新的字節數組用於保存字符串數據的拷貝,然後引用這個底層的字節數組。編譯器的優化可以避免在一些場景下分配和複製字符串數據,但總的來説需要確保在變量b被脩改的情況下,原始的s字符串也不會改變。將一個字節slice轉到字符串的string(b)操作則是構造一個字符串拷貝,以確保s2字符串是隻讀的。</p>
|
||||
<p>爲了避免轉換中不必要的內存分配,bytes包和strings同時提供了許多實用函數。下面是strings包中的六個函數:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> Contains(s, substr <span class="hljs-typename">string</span>) <span class="hljs-typename">bool</span>
|
||||
<span class="hljs-keyword">func</span> Count(s, sep <span class="hljs-typename">string</span>) <span class="hljs-typename">int</span>
|
||||
<span class="hljs-keyword">func</span> Fields(s <span class="hljs-typename">string</span>) []<span class="hljs-typename">string</span>
|
||||
@@ -2251,7 +2255,7 @@ s2 := <span class="hljs-typename">string</span>(b)
|
||||
<span class="hljs-keyword">func</span> Index(s, sep <span class="hljs-typename">string</span>) <span class="hljs-typename">int</span>
|
||||
<span class="hljs-keyword">func</span> Join(a []<span class="hljs-typename">string</span>, sep <span class="hljs-typename">string</span>) <span class="hljs-typename">string</span>
|
||||
</code></pre>
|
||||
<p>bytes 包中對應的六個函數:</p>
|
||||
<p>bytes包中也對應的六個函數:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> Contains(b, subslice []<span class="hljs-typename">byte</span>) <span class="hljs-typename">bool</span>
|
||||
<span class="hljs-keyword">func</span> Count(s, sep []<span class="hljs-typename">byte</span>) <span class="hljs-typename">int</span>
|
||||
<span class="hljs-keyword">func</span> Fields(s []<span class="hljs-typename">byte</span>) [][]<span class="hljs-typename">byte</span>
|
||||
@@ -2259,8 +2263,8 @@ s2 := <span class="hljs-typename">string</span>(b)
|
||||
<span class="hljs-keyword">func</span> Index(s, sep []<span class="hljs-typename">byte</span>) <span class="hljs-typename">int</span>
|
||||
<span class="hljs-keyword">func</span> Join(s [][]<span class="hljs-typename">byte</span>, sep []<span class="hljs-typename">byte</span>) []<span class="hljs-typename">byte</span>
|
||||
</code></pre>
|
||||
<p>唯一的區别是字符串類型參數被替換成了字節切片類型的參數.</p>
|
||||
<p>bytes 包還提供了 Buffer 類型用於字節切片的緩存. 一個 Buffer 開始是空的, 但是隨着 string, byte, 和 []byte 等類型數據的寫入可以動態增長, 一個 bytes.Buffer 變量併不需要處理化, 因此零值也是有效的:</p>
|
||||
<p>它們之間唯一的區别是字符串類型參數被替換成了字節slice類型的參數。</p>
|
||||
<p>bytes包還提供了Buffer類型用於字節slice的緩存。一個Buffer開始是空的,但是隨着string、byte或[]byte等類型數據的寫入可以動態增長,一個bytes.Buffer變量併不需要處理化,因爲零值也是有效的:</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch3/printints
|
||||
|
||||
<span class="hljs-comment">// intsToString is like fmt.Sprintf(values) but adds commas.</span>
|
||||
@@ -2281,30 +2285,30 @@ s2 := <span class="hljs-typename">string</span>(b)
|
||||
fmt.Println(intsToString([]<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">// "[1, 2, 3]"</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>當向 bytes.Buffer 添加任意字符的UTF8編碼, 最好使用 bytes.Buffer 的 WriteRune 方法, 但是 WriteByte 方法對於寫入類似 '[' 和 ']' 等 ASCII 字符則更有效.</p>
|
||||
<p>bytes.Buffer 類型有着諸多實用的功能, 我們在第七章討論接口時層涉及到, 我們將看看如何將它用作一個I/O 的輸入和輸齣對象, 例如 Fprintf 的 io.Writer 輸齣, 或作爲輸入源 io.Reader.</p>
|
||||
<p><strong>練習3.10:</strong> 編寫一個非遞歸版本的comma函數, 使用 bytes.Buffer 代替字符串鏈接操作.</p>
|
||||
<p><strong>練習3.11:</strong> 完善 comma 函數, 以支持浮點數處理和一個可選的正負號處理.</p>
|
||||
<p><strong>練習3.12:</strong> 編寫一個函數, 判斷兩個字符串是否是是相互打亂的, 也就是説它們有着相同的字符, 但是對應不同的順序.</p>
|
||||
<p>當向bytes.Buffer添加任意字符的UTF8編碼時,最好使用bytes.Buffer的WriteRune方法,但是WriteByte方法對於寫入類似'['和']'等ASCII字符則會更加有效。</p>
|
||||
<p>bytes.Buffer類型有着很多實用的功能,我們在第七章討論接口時將會涉及到,我們將看看如何將它用作一個I/O的輸入和輸出對象,例如當做Fprintf的io.Writer輸出對象,或者當作io.Reader類型的輸入源對象。</p>
|
||||
<p><strong>練習 3.10:</strong> 編寫一個非遞歸版本的comma函數,使用bytes.Buffer代替字符串鏈接操作。</p>
|
||||
<p><strong>練習 3.11:</strong> 完善comma函數,以支持浮點數處理和一個可選的正負號的處理。</p>
|
||||
<p><strong>練習 3.12:</strong> 編寫一個函數,判斷兩個字符串是否是是相互打亂的,也就是説它們有着相同的字符,但是對應不同的順序。</p>
|
||||
<h3 id="355-字符串和數字的轉換">3.5.5. 字符串和數字的轉換</h3>
|
||||
<p>除了字符串, 字符, 字節 之間的轉換, 字符串和數值之間的轉換也比較常見. 由 strconv 包提供這類轉換功能.</p>
|
||||
<p>將一個整數轉爲字符串, 一種方法是用 fmt.Sprintf; 另一個方法是用 strconv.Itoa(“整數到ASCII”):</p>
|
||||
<p>除了字符串、字符、字節之間的轉換,字符串和數值之間的轉換也比較常見。由strconv包提供這類轉換功能。</p>
|
||||
<p>將一個整數轉爲字符串,一種方法是用fmt.Sprintf返迴一個格式化的字符串;另一個方法是用strconv.Itoa(“整數到ASCII”):</p>
|
||||
<pre><code class="lang-Go">x := <span class="hljs-number">123</span>
|
||||
y := fmt.Sprintf(<span class="hljs-string">"%d"</span>, x)
|
||||
fmt.Println(y, strconv.Itoa(x)) <span class="hljs-comment">// "123 123"</span>
|
||||
</code></pre>
|
||||
<p>FormatInt和FormatUint可以用不同的進製來格式化數字:</p>
|
||||
<p>FormatInt和FormatUint函數可以用不同的進製來格式化數字:</p>
|
||||
<pre><code class="lang-Go">fmt.Println(strconv.FormatInt(<span class="hljs-typename">int64</span>(x), <span class="hljs-number">2</span>)) <span class="hljs-comment">// "1111011"</span>
|
||||
</code></pre>
|
||||
<p>fmt.Printf 函數的 %b, %d, %u, 和 %x 等參數提供功能往往比strconv 包的 Format 函數方便很多, 特别是在需要包含附加信息的時候:</p>
|
||||
<p>fmt.Printf函數的%b、%d、%u和%x等參數提供功能往往比strconv包的Format函數方便很多,特别是在需要包含附加額外信息的時候:</p>
|
||||
<pre><code class="lang-Go">s := fmt.Sprintf(<span class="hljs-string">"x=%b"</span>, x) <span class="hljs-comment">// "x=1111011"</span>
|
||||
</code></pre>
|
||||
<p>如果要將一個字符串解析爲整數, 可以使用 strconv 包的 Atoi 或 ParseInt 函數, 還有用於解析無符號整數的 ParseUint 函數:</p>
|
||||
<p>如果要將一個字符串解析爲整數,可以使用strconv包的Atoi或ParseInt函數,還有用於解析無符號整數的ParseUint函數:</p>
|
||||
<pre><code class="lang-Go">x, err := strconv.Atoi(<span class="hljs-string">"123"</span>) <span class="hljs-comment">// x is an int</span>
|
||||
y, err := strconv.ParseInt(<span class="hljs-string">"123"</span>, <span class="hljs-number">10</span>, <span class="hljs-number">64</span>) <span class="hljs-comment">// base 10, up to 64 bits</span>
|
||||
</code></pre>
|
||||
<p>ParseInt 函數的第三個參數是用於指定整型數的大小; 例如16表示int16, 0則表示int. 在任何情況下, 返迴的結果 y 總是 int64 類型, 你可以通過強製類型轉換將它轉爲更小的整數類型.</p>
|
||||
<p>有時候也會使用 fmt.Scanf 來解析輸入的字符串和數字, 特别是當字符串和數字混合在一行的時候, 它可以靈活處理不完整或不規則的輸入.</p>
|
||||
<p>ParseInt函數的第三個參數是用於指定整型數的大小;例如16表示int16,0則表示int。在任何情況下,返迴的結果y總是int64類型,你可以通過強製類型轉換將它轉爲更小的整數類型。</p>
|
||||
<p>有時候也會使用fmt.Scanf來解析輸入的字符串和數字,特别是當字符串和數字混合在一行的時候,它可以靈活處理不完整或不規則的輸入。</p>
|
||||
|
||||
|
||||
</section>
|
||||
@@ -2336,7 +2340,7 @@ y, err := strconv.ParseInt(<span class="hljs-string">"123"</span>, <sp
|
||||
|
||||
<script>
|
||||
require(["gitbook"], function(gitbook) {
|
||||
var config = {"highlight":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2}};
|
||||
var config = {"katex":{},"highlight":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2}};
|
||||
gitbook.start(config);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
<link rel="stylesheet" href="../gitbook/style.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../gitbook/plugins/gitbook-plugin-katex/katex.min.css">
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../gitbook/plugins/gitbook-plugin-highlight/website.css">
|
||||
|
||||
|
||||
@@ -44,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="3.6" data-chapter-title="常量" data-filepath="ch3/ch3-06.md" data-basepath=".." data-revision="Fri Dec 25 2015 12:32:44 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="3.6" data-chapter-title="常量" data-filepath="ch3/ch3-06.md" data-basepath=".." data-revision="Mon Dec 28 2015 16:03:52 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -238,7 +242,7 @@
|
||||
|
||||
<b>1.5.</b>
|
||||
|
||||
穫取URL
|
||||
獲取URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -253,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併發穫取多個URL
|
||||
併發獲取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -802,7 +806,7 @@
|
||||
|
||||
<b>5.10.</b>
|
||||
|
||||
Recover捕穫異常
|
||||
Recover捕獲異常
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1315,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併發的退齣
|
||||
併發的退出
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1834,7 +1838,7 @@
|
||||
|
||||
<b>12.7.</b>
|
||||
|
||||
穫取結構體字段標識
|
||||
獲取結構體字段標識
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2020,19 +2024,19 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="36-常量">3.6. 常量</h2>
|
||||
<p>常量表達式的值在編譯期計算, 而不是在運行期. 每種常量的潛在類型都是基礎類型: boolean, string, 或數字.</p>
|
||||
<p>一個常量的聲明語句定義了常量的名字, 和變量的聲明語法類似, 常量的值不可脩改, 這樣可以防止在運行期被意外或惡意的脩改. 例如, 常量比變量更適合用於表達像 π 之類的數學常數, 因爲它們的值不會變化:</p>
|
||||
<p>常量表達式的值在編譯期計算,而不是在運行期。每種常量的潛在類型都是基礎類型:boolean、string或數字。</p>
|
||||
<p>一個常量的聲明語句定義了常量的名字,和變量的聲明語法類似,常量的值不可脩改,這樣可以防止在運行期被意外或惡意的脩改。例如,常量比變量更適合用於表達像π之類的數學常數,因爲它們的值不會發生變化:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">const</span> pi = <span class="hljs-number">3.14159</span> <span class="hljs-comment">// approximately; math.Pi is a better approximation</span>
|
||||
</code></pre>
|
||||
<p>和變量聲明一樣, 可以批量聲明多個常量; 這比較適合聲明一組相關的常量:</p>
|
||||
<p>和變量聲明一樣,可以批量聲明多個常量;這比較適合聲明一組相關的常量:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">const</span> (
|
||||
e = <span class="hljs-number">2.71828182845904523536028747135266249775724709369995957496696763</span>
|
||||
pi = <span class="hljs-number">3.14159265358979323846264338327950288419716939937510582097494459</span>
|
||||
)
|
||||
</code></pre>
|
||||
<p>許多常量的運算可以在編譯期完成, 這樣可以減少運行時的工作, 也方便其他編譯優化. 當操作數是常量時, 一些運行時的錯誤可以在編譯時發現, 例如整數除零, 字符串索引越界, 任何導致無效浮點數的操作等.</p>
|
||||
<p>常量間的所有算術運算, 邏輯運算和比較運算的結果也是常量, 對常量的類型轉換操作或以下函數調用都是返迴常量結果: len, cap, real, imag, complex, 和 unsafe.Sizeof(§13.1).</p>
|
||||
<p>因爲它們的值是在編譯期就確定的, 因此常量可以是構成類型的一部分, 例如用於指定數組類型的長度:</p>
|
||||
<p>所有常量的運算都可以在編譯期完成,這樣可以減少運行時的工作,也方便其他編譯優化。當操作數是常量時,一些運行時的錯誤也可以在編譯時被發現,例如整數除零、字符串索引越界、任何導致無效浮點數的操作等。</p>
|
||||
<p>常量間的所有算術運算、邏輯運算和比較運算的結果也是常量,對常量的類型轉換操作或以下函數調用都是返迴常量結果:len、cap、real、imag、complex和unsafe.Sizeof(§13.1)。</p>
|
||||
<p>因爲它們的值是在編譯期就確定的,因此常量可以是構成類型的一部分,例如用於指定數組類型的長度:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">const</span> IPv4Len = <span class="hljs-number">4</span>
|
||||
|
||||
<span class="hljs-comment">// parseIPv4 parses an IPv4 address (d.d.d.d).</span>
|
||||
@@ -2041,14 +2045,14 @@
|
||||
<span class="hljs-comment">// ...</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>一個常量的聲明也可以包含一個類型和一個值, 但是如果沒有顯式指明類型, 那麽將從右邊的表達式推斷類型. 在下面的代碼中, time.Duration 是一個命名類型, 底層類型是 int64, time.Minute 是對應類型的常量. 下面聲明的兩個常量都是 time.Duration 類型, 可以通過 %T 參數打印類型信息:</p>
|
||||
<p>一個常量的聲明也可以包含一個類型和一個值,但是如果沒有顯式指明類型,那麽將從右邊的表達式推斷類型。在下面的代碼中,time.Duration是一個命名類型,底層類型是int64,time.Minute是對應類型的常量。下面聲明的兩個常量都是time.Duration類型,可以通過%T參數打印類型信息:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">const</span> noDelay time.Duration = <span class="hljs-number">0</span>
|
||||
<span class="hljs-keyword">const</span> timeout = <span class="hljs-number">5</span> * time.Minute
|
||||
fmt.Printf(<span class="hljs-string">"%T %[1]v\n"</span>, noDelay) <span class="hljs-comment">// "time.Duration 0"</span>
|
||||
fmt.Printf(<span class="hljs-string">"%T %[1]v\n"</span>, timeout) <span class="hljs-comment">// "time.Duration 5m0s</span>
|
||||
fmt.Printf(<span class="hljs-string">"%T %[1]v\n"</span>, time.Minute) <span class="hljs-comment">// "time.Duration 1m0s"</span>
|
||||
</code></pre>
|
||||
<p>如果是批量聲明的常量, 除了第一個外其他常量的右邊的表發生可以省略, 如果省略則表示使用前面的表達式, 對應的常量類型也一樣. 例如:</p>
|
||||
<p>如果是批量聲明的常量,除了第一個外其它的常量右邊的初始化表達式都可以省略,如果省略初始化表達式則表示使用前面常量的初始化表達式寫法,對應的常量類型也一樣的。例如:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">const</span> (
|
||||
a = <span class="hljs-number">1</span>
|
||||
b
|
||||
@@ -2058,10 +2062,10 @@ fmt.Printf(<span class="hljs-string">"%T %[1]v\n"</span>, time.Minute)
|
||||
|
||||
fmt.Println(a, b, c, d) <span class="hljs-comment">// "1 1 2 2"</span>
|
||||
</code></pre>
|
||||
<p>如果隻是簡單地複製右邊的常量表達式, 併沒有太實用的價值. 但是它可以帶來其他的特性, 那就是 iota 常量生成器.</p>
|
||||
<p>如果隻是簡單地複製右邊的常量表達式,其實併沒有太實用的價值。但是它可以帶來其它的特性,那就是iota常量生成器語法。</p>
|
||||
<h3 id="361-iota-常量生成器">3.6.1. iota 常量生成器</h3>
|
||||
<p>常量聲明可以使用 iota 常量生成器, 用於生成一組相似的常量值, 但是不用每行都寫一遍. 在一個 const 聲明語句中, 在開始一行 iota 將會被置爲0, 然後在每一個有常量聲明的行加一.</p>
|
||||
<p>下面是來自 time 包的例子, 它首先定義了Weekday命名類型, 然後爲一週的每天定義一個常量, 從週日0開始. 這種類型一般被稱爲枚舉類型.</p>
|
||||
<p>常量聲明可以使用iota常量生成器初始化,它用於生成一組以相似規則初始化的常量,但是不用每行都寫一遍初始化表達式。在一個const聲明語句中,在第一個聲明的常量所在的行,iota將會被置爲0,然後在每一個有常量聲明的行加一。</p>
|
||||
<p>下面是來自time包的例子,它首先定義了一個Weekday命名類型,然後爲一週的每天定義了一個常量,從週日0開始。在其它編程語言中,這種類型一般被稱爲枚舉類型。</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">type</span> Weekday <span class="hljs-typename">int</span>
|
||||
|
||||
<span class="hljs-keyword">const</span> (
|
||||
@@ -2074,8 +2078,8 @@ fmt.Println(a, b, c, d) <span class="hljs-comment">// "1 1 2 2"</span>
|
||||
Saturday
|
||||
)
|
||||
</code></pre>
|
||||
<p>週一將對應0, 週一爲1, 如此等等.</p>
|
||||
<p>我們也可以在複雜的常量表達式中使用 iota, 下面是來自 net 包的例子, 用於給一個無符號整數的最低5bit的每個bit給定一個名字:</p>
|
||||
<p>週一將對應0,週一爲1,如此等等。</p>
|
||||
<p>我們也可以在複雜的常量表達式中使用iota,下面是來自net包的例子,用於給一個無符號整數的最低5bit的每個bit指定一個名字:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">type</span> Flags <span class="hljs-typename">uint</span>
|
||||
|
||||
<span class="hljs-keyword">const</span> (
|
||||
@@ -2086,7 +2090,7 @@ fmt.Println(a, b, c, d) <span class="hljs-comment">// "1 1 2 2"</span>
|
||||
FlagMulticast <span class="hljs-comment">// supports multicast access capability</span>
|
||||
)
|
||||
</code></pre>
|
||||
<p>隨着 iota 的遞增, 每個常量對應表達式 1 << iota, 是連續的2的冪, 分别對應一個bit位置. 使用這些常量可以測試, 設置, 或清除對應的bit位的值:</p>
|
||||
<p>隨着iota的遞增,每個常量對應表達式1 << iota,是連續的2的冪,分别對應一個bit位置。使用這些常量可以用於測試、設置或清除對應的bit位的值:</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch3/netflag
|
||||
|
||||
<span class="hljs-keyword">func</span> IsUp(v Flags) <span class="hljs-typename">bool</span> { <span class="hljs-keyword">return</span> v&FlagUp == FlagUp }
|
||||
@@ -2104,7 +2108,7 @@ unc main() {
|
||||
fmt.Printf(<span class="hljs-string">"%b %t\n"</span>, v, IsCast(v)) <span class="hljs-comment">// "10010 true"</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>下面是一個更複雜的例子, 每個常量都是1024的冪:</p>
|
||||
<p>下面是一個更複雜的例子,每個常量都是1024的冪:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">const</span> (
|
||||
_ = <span class="hljs-number">1</span> << (<span class="hljs-number">10</span> * <span class="hljs-constant">iota</span>)
|
||||
KiB <span class="hljs-comment">// 1024</span>
|
||||
@@ -2117,33 +2121,33 @@ unc main() {
|
||||
YiB <span class="hljs-comment">// 1208925819614629174706176</span>
|
||||
)
|
||||
</code></pre>
|
||||
<p>iota 機製也有其局限性. 例如, 它併不能用於産生1000的冪(KB,MB,等等), 因爲併沒有計算冪的運算符.</p>
|
||||
<p><strong>練習3.13:</strong> 編寫KB,MB的常量聲明, 然後擴展到YB.</p>
|
||||
<p>不過iota常量生成規則也有其局限性。例如,它併不能用於産生1000的冪(KB、MB等),因爲Go語言併沒有計算冪的運算符。</p>
|
||||
<p><strong>練習 3.13:</strong> 編寫KB、MB的常量聲明,然後擴展到YB。</p>
|
||||
<h3 id="362-無類型常量">3.6.2. 無類型常量</h3>
|
||||
<p>Go語言的常量有點不尋常. 雖然一個常量可以有任意有一個確定的基礎類型, 例如 int 或 float64, 或者是類似 time.Duration 這樣命名的基礎類型, 但是許多常量併沒有一個明確的基礎類型. 編譯期爲這些沒有明確的基礎類型的數字常量提供比基礎類型或機器更高精度的算術運算; 你可以認爲至少有256bit的運算精度. 這里有六種未明確類型的常量類型, 分别是 無類型的布爾型, 無類型的整數, 無類型的字符, 無類型的浮點數, 無類型的複數, 無類型的字符串.</p>
|
||||
<p>通過延遲明確具體類型, 無類型的常量不僅可以提供更高的精度, 而且可以直接用於更多的表達式而不需要類型轉換. 例如 例子中的 ZiB 和 YiB 的值已經超齣任何Go中整數類型能表達的范圍, 但是它們依然是合法的常量, 而且可以像下面表達式這樣使用:</p>
|
||||
<p>Go語言的常量有個不同尋常之處。雖然一個常量可以有任意有一個確定的基礎類型,例如int或float64,或者是類似time.Duration這樣命名的基礎類型,但是許多常量併沒有一個明確的基礎類型。編譯器爲這些沒有明確的基礎類型的數字常量提供比基礎類型更高精度的算術運算;你可以認爲至少有256bit的運算精度。這里有六種未明確類型的常量類型,分别是無類型的布爾型、無類型的整數、無類型的字符、無類型的浮點數、無類型的複數、無類型的字符串。</p>
|
||||
<p>通過延遲明確常量的具體類型,無類型的常量不僅可以提供更高的運算精度,而且可以直接用於更多的表達式而不需要顯式的類型轉換。例如,例子中的ZiB和YiB的值已經超出任何Go語言中整數類型能表達的范圍,但是它們依然是合法的常量,而且可以像下面常量表達式依然有效(譯註:YiB/ZiB是在編譯期計算出來的,併且結果常量是1024,是Go語言int變量能有效表示的):</p>
|
||||
<pre><code class="lang-Go">fmt.Println(YiB/ZiB) <span class="hljs-comment">// "1024"</span>
|
||||
</code></pre>
|
||||
<p>另一個例子, math.Pi 無類型的浮點數常量, 可以直接用於任意需要浮點數或複數的地方:</p>
|
||||
<p>另一個例子,math.Pi無類型的浮點數常量,可以直接用於任意需要浮點數或複數的地方:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> x <span class="hljs-typename">float32</span> = math.Pi
|
||||
<span class="hljs-keyword">var</span> y <span class="hljs-typename">float64</span> = math.Pi
|
||||
<span class="hljs-keyword">var</span> z <span class="hljs-typename">complex128</span> = math.Pi
|
||||
</code></pre>
|
||||
<p>如果 math.Pi 被確定爲特定類型, 比如 float64, 那麽結果精度可能會不一樣, 同時對於需要float32或complex128類型值的地方會需要一個明確的類型轉換:</p>
|
||||
<p>如果math.Pi被確定爲特定類型,比如float64,那麽結果精度可能會不一樣,同時對於需要float32或complex128類型值的地方則會強製需要一個明確的類型轉換:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">const</span> Pi64 <span class="hljs-typename">float64</span> = math.Pi
|
||||
|
||||
<span class="hljs-keyword">var</span> x <span class="hljs-typename">float32</span> = <span class="hljs-typename">float32</span>(Pi64)
|
||||
<span class="hljs-keyword">var</span> y <span class="hljs-typename">float64</span> = Pi64
|
||||
<span class="hljs-keyword">var</span> z <span class="hljs-typename">complex128</span> = <span class="hljs-typename">complex128</span>(Pi64)
|
||||
</code></pre>
|
||||
<p>對於常量面值, 不同的寫法對應不同的類型. 例如 0, 0.0, 0i, 和 '\u0000' 雖然有着相同的常量值, 但是它們分别對應無類型的整數,無類型的浮點數,無類型的複數,和無類型的字符等不同的常量類型. 同樣, true 和 false 也是無類型的布爾類型, 字符串面值常量是無類型的字符串.</p>
|
||||
<p>前面説過除法運算符 / 根據操作數的類型生成對應類型的結果. 因此, 不同寫法的常量除法表達式可能對應不同的結果:</p>
|
||||
<p>對於常量面值,不同的寫法可能會對應不同的類型。例如0、0.0、0i和'\u0000'雖然有着相同的常量值,但是它們分别對應無類型的整數、無類型的浮點數、無類型的複數和無類型的字符等不同的常量類型。同樣,true和false也是無類型的布爾類型,字符串面值常量是無類型的字符串類型。</p>
|
||||
<p>前面説過除法運算符/會根據操作數的類型生成對應類型的結果。因此,不同寫法的常量除法表達式可能對應不同的結果:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> f <span class="hljs-typename">float64</span> = <span class="hljs-number">212</span>
|
||||
fmt.Println((f - <span class="hljs-number">32</span>) * <span class="hljs-number">5</span> / <span class="hljs-number">9</span>) <span class="hljs-comment">// "100"; (f - 32) * 5 is a float64</span>
|
||||
fmt.Println(<span class="hljs-number">5</span> / <span class="hljs-number">9</span> * (f - <span class="hljs-number">32</span>)) <span class="hljs-comment">// "0"; 5/9 is an untyped integer, 0</span>
|
||||
fmt.Println(<span class="hljs-number">5.0</span> / <span class="hljs-number">9.0</span> * (f - <span class="hljs-number">32</span>)) <span class="hljs-comment">// "100"; 5.0/9.0 is an untyped float</span>
|
||||
</code></pre>
|
||||
<p>隻有常量可以是無類型的. 當一個無類型的常量被賦值給一個變量, 就像上面的第一行語句, 或者是像其餘三個語句中右邊表達式中含有明確類型的值, 無類型的常量將會被隱式轉換爲對應的類型, 如果可能的話.</p>
|
||||
<p>隻有常量可以是無類型的。當一個無類型的常量被賦值給一個變量的時候,就像上面的第一行語句,或者是像其餘三個語句中右邊表達式中含有明確類型的值,無類型的常量將會被隱式轉換爲對應的類型,如果轉換合法的話。</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> f <span class="hljs-typename">float64</span> = <span class="hljs-number">3</span> + <span class="hljs-number">0i</span> <span class="hljs-comment">// untyped complex -> float64</span>
|
||||
f = <span class="hljs-number">2</span> <span class="hljs-comment">// untyped integer -> float64</span>
|
||||
f = <span class="hljs-number">1e123</span> <span class="hljs-comment">// untyped floating-point -> float64</span>
|
||||
@@ -2155,7 +2159,7 @@ f = <span class="hljs-typename">float64</span>(<span class="hljs-number">2</span
|
||||
f = <span class="hljs-typename">float64</span>(<span class="hljs-number">1e123</span>)
|
||||
f = <span class="hljs-typename">float64</span>(<span class="hljs-string">'a'</span>)
|
||||
</code></pre>
|
||||
<p>無論是隱式或顯式, 將一種類型轉換爲另一種類型要求目標可以表示原始值. 對於浮點數和複數, 可能會有舍入處理:</p>
|
||||
<p>無論是隱式或顯式轉換,將一種類型轉換爲另一種類型都要求目標可以表示原始值。對於浮點數和複數,可能會有舍入處理:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">const</span> (
|
||||
deadbeef = <span class="hljs-number">0xdeadbeef</span> <span class="hljs-comment">// untyped int with value 3735928559</span>
|
||||
a = <span class="hljs-typename">uint32</span>(deadbeef) <span class="hljs-comment">// uint32 with value 3735928559</span>
|
||||
@@ -2166,24 +2170,24 @@ f = <span class="hljs-typename">float64</span>(<span class="hljs-string">'a
|
||||
f = <span class="hljs-typename">uint</span>(-<span class="hljs-number">1</span>) <span class="hljs-comment">// compile error: constant underflows uint</span>
|
||||
)
|
||||
</code></pre>
|
||||
<p>對於一個沒有顯式類型的變量聲明(包括短變量聲明語法), 無類型的常量會被隱式轉爲默認的變量類型, 就像下面的例子:</p>
|
||||
<p>對於一個沒有顯式類型的變量聲明語法(包括短變量聲明語法),無類型的常量會被隱式轉爲默認的變量類型,就像下面的例子:</p>
|
||||
<pre><code class="lang-Go">i := <span class="hljs-number">0</span> <span class="hljs-comment">// untyped integer; implicit int(0)</span>
|
||||
r := <span class="hljs-string">'\000'</span> <span class="hljs-comment">// untyped rune; implicit rune('\000')</span>
|
||||
f := <span class="hljs-number">0.0</span> <span class="hljs-comment">// untyped floating-point; implicit float64(0.0)</span>
|
||||
c := <span class="hljs-number">0i</span> <span class="hljs-comment">// untyped complex; implicit complex128(0i)</span>
|
||||
</code></pre>
|
||||
<p>註意默認類型是規則的: 無類型的整數常量默認轉換爲int, 對應不確定的尺寸, 但是浮點數好複數常量則默認轉換爲float64和complex128. Go語言本身併沒有不確定的尺寸的浮點數和複數類型, 因爲如何不知道浮點數類型的話很難寫齣正確的數值算法.</p>
|
||||
<p>如果要給變量一個不同的類型, 我們必鬚顯式地將無類型的常量轉化爲所需的類型, 或給聲明的變量指定類型, 像下面例子這樣:</p>
|
||||
<p>註意默認類型是規則的:無類型的整數常量默認轉換爲int,對應不確定的內存大小,但是浮點數和複數常量則默認轉換爲float64和complex128。Go語言本身併沒有不確定內存大小的浮點數和複數類型,而且如果不知道浮點數類型的話將很難寫出正確的數值算法。</p>
|
||||
<p>如果要給變量一個不同的類型,我們必鬚顯式地將無類型的常量轉化爲所需的類型,或給聲明的變量指定明確的類型,像下面例子這樣:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> i = <span class="hljs-typename">int8</span>(<span class="hljs-number">0</span>)
|
||||
<span class="hljs-keyword">var</span> i <span class="hljs-typename">int8</span> = <span class="hljs-number">0</span>
|
||||
</code></pre>
|
||||
<p>當嚐試將這些無類型的常量轉爲一個接口值時(見第7章), 這些默認類型將顯得尤爲重要, 因爲要靠它們明確接口對應的動態類型.</p>
|
||||
<p>當嚐試將這些無類型的常量轉爲一個接口值時(見第7章),這些默認類型將顯得尤爲重要,因爲要靠它們明確接口對應的動態類型。</p>
|
||||
<pre><code class="lang-Go">fmt.Printf(<span class="hljs-string">"%T\n"</span>, <span class="hljs-number">0</span>) <span class="hljs-comment">// "int"</span>
|
||||
fmt.Printf(<span class="hljs-string">"%T\n"</span>, <span class="hljs-number">0.0</span>) <span class="hljs-comment">// "float64"</span>
|
||||
fmt.Printf(<span class="hljs-string">"%T\n"</span>, <span class="hljs-number">0i</span>) <span class="hljs-comment">// "complex128"</span>
|
||||
fmt.Printf(<span class="hljs-string">"%T\n"</span>, <span class="hljs-string">'\000'</span>) <span class="hljs-comment">// "int32" (rune)</span>
|
||||
</code></pre>
|
||||
<p>現在我們已經講述了Go語言中全部的基礎數據類型. 下一步將演示如何用基礎數據類型組合成數組或結構體等複雜數據類型, 然後構建用於解決實際編程問題的數據結構, 這將是第四章的討論主題.</p>
|
||||
<p>現在我們已經講述了Go語言中全部的基礎數據類型。下一步將演示如何用基礎數據類型組合成數組或結構體等複雜數據類型,然後構建用於解決實際編程問題的數據結構,這將是第四章的討論主題。</p>
|
||||
|
||||
|
||||
</section>
|
||||
@@ -2215,7 +2219,7 @@ fmt.Printf(<span class="hljs-string">"%T\n"</span>, <span class="hljs-
|
||||
|
||||
<script>
|
||||
require(["gitbook"], function(gitbook) {
|
||||
var config = {"highlight":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2}};
|
||||
var config = {"katex":{},"highlight":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2}};
|
||||
gitbook.start(config);
|
||||
});
|
||||
</script>
|
||||
|
||||
22
ch3/ch3.html
22
ch3/ch3.html
@@ -21,6 +21,10 @@
|
||||
<link rel="stylesheet" href="../gitbook/style.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../gitbook/plugins/gitbook-plugin-katex/katex.min.css">
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../gitbook/plugins/gitbook-plugin-highlight/website.css">
|
||||
|
||||
|
||||
@@ -44,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="3" data-chapter-title="基礎數據類型" data-filepath="ch3/ch3.md" data-basepath=".." data-revision="Fri Dec 25 2015 12:32:44 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="3" data-chapter-title="基礎數據類型" data-filepath="ch3/ch3.md" data-basepath=".." data-revision="Mon Dec 28 2015 16:03:52 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -238,7 +242,7 @@
|
||||
|
||||
<b>1.5.</b>
|
||||
|
||||
穫取URL
|
||||
獲取URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -253,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併發穫取多個URL
|
||||
併發獲取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -802,7 +806,7 @@
|
||||
|
||||
<b>5.10.</b>
|
||||
|
||||
Recover捕穫異常
|
||||
Recover捕獲異常
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1315,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併發的退齣
|
||||
併發的退出
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1834,7 +1838,7 @@
|
||||
|
||||
<b>12.7.</b>
|
||||
|
||||
穫取結構體字段標識
|
||||
獲取結構體字段標識
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2020,8 +2024,8 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h1 id="第3章-基礎數據類型">第3章 基礎數據類型</h1>
|
||||
<p>雖然從底層而言,所有的數據都是比特,但計算機操作的是固定位數的數,如整數、浮點數、比特組、內存地址。將這些數,進一步組織在一起,可表達更多的對象,如數據包、像素點、詩歌,甚至任何對象.Go提供了豐富的數據組織形式,這依賴於Go內置的數據類型。這些內置的數據類型,兼顧了硬件的特性和表達複雜數據結構的便捷性。</p>
|
||||
<p>Go將數據類型分爲四類:基礎類型、複合類型、引用類型和接口類型。本章介紹基礎類型,包括:數字,字符串和布爾型。複合數據類型——數組(§4.1)和結構體(§4.2)——通過組合簡單類型,表達更加複雜的數據結構。引用類型包括指針(§2.3.2)、切片(§4.2))字典(§4.3)、函數(§5)、通道(§8).雖然種類很多,但它們都是對程序中一個變量或狀態的間接引用。這意味着對任一引用的脩改都會影響所有該引用的拷貝。我們將在第7章介紹接口類型。</p>
|
||||
<p>雖然從底層而言,所有的數據都是由比特組成,但計算機一般操作的是固定大小的數,如整數、浮點數、比特數組、內存地址等。進一步將這些數組織在一起,就可表達更多的對象,例如數據包、像素點、詩歌,甚至其他任何對象。Go語言提供了豐富的數據組織形式,這依賴於Go語言內置的數據類型。這些內置的數據類型,兼顧了硬件的特性和表達複雜數據結構的便捷性。</p>
|
||||
<p>Go語言將數據類型分爲四類:基礎類型、複合類型、引用類型和接口類型。本章介紹基礎類型,包括:數字、字符串和布爾型。複合數據類型——數組(§4.1)和結構體(§4.2)——是通過組合簡單類型,來表達更加複雜的數據結構。引用類型包括指針(§2.3.2)、切片(§4.2))字典(§4.3)、函數(§5)、通道(§8),雖然數據種類很多,但它們都是對程序中一個變量或狀態的間接引用。這意味着對任一引用類型數據的脩改都會影響所有該引用的拷貝。我們將在第7章介紹接口類型。</p>
|
||||
|
||||
|
||||
</section>
|
||||
@@ -2053,7 +2057,7 @@
|
||||
|
||||
<script>
|
||||
require(["gitbook"], function(gitbook) {
|
||||
var config = {"highlight":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2}};
|
||||
var config = {"katex":{},"highlight":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2}};
|
||||
gitbook.start(config);
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user