make zh2tw

This commit is contained in:
chai2010 2015-12-21 12:52:25 +08:00
parent 14665b4d9c
commit d024df28e9
7 changed files with 131 additions and 131 deletions

View File

@ -1,20 +1,20 @@
## 3.1. 整型 ## 3.1. 整型
Go语言的数值类型包括几种不同大小的整形数, 浮点数, 和复数. 每种数值类型都决定了对应的大小范围和是否有正负符号. 让我们先从整形数类型开始介绍. Go語言的數值類型包括幾種不同大小的整形數, 浮點數, 和複數. 每種數值類型都決定了對應的大小范圍和是否有正負符號. 讓我們先從整形數類型開始介紹.
Go同时提供了有符号和无符号的整数运算. 这里有四种int8, int16, int32 和 int64截然不同大小的有符号整形数类型, 分别对应 8, 16, 32, 64 bit 大小的有符号整形数, 与此对应的是 uint8, uint16, uint32, 和 uint64 四种无符号整形数类型. Go同時提供了有符號和無符號的整數運算. 這里有四種int8, int16, int32 和 int64截然不同大小的有符號整形數類型, 分别對應 8, 16, 32, 64 bit 大小的有符號整形數, 與此對應的是 uint8, uint16, uint32, 和 uint64 四種無符號整形數類型.
这里还有两种对应特定平台最天然或最有效率的大小有符号和无符号整数int和uint; 其中int是应用最广泛的数值类型. 这两种类型都有同样的大小, 32 或 64 bit, 但是我们不能对此做任何的假设; 因为不同的编译器在相同的硬件平台上可能产生不同的大小. 這里還有兩種對應特定平颱最天然或最有效率的大小有符號和無符號整數int和uint; 其中int是應用最廣泛的數值類型. 這兩種類型都有同樣的大小, 32 或 64 bit, 但是我們不能對此做任何的假設; 因爲不同的編譯器在相同的硬件平颱上可能産生不同的大小.
字符rune类型是和int32等价的类型, 通常用于表示一个Unicode码点. 这两个名称可以互换使用. 同样byte也是uint8类型的等价类型, byte类型用于强调数值是一个原始的数据而不是一个小的整数. 字符rune類型是和int32等價的類型, 通常用於表示一個Unicode碼點. 這兩個名稱可以互換使用. 同樣byte也是uint8類型的等價類型, byte類型用於強調數值是一個原始的數據而不是一個小的整數.
最好, 还有一个无符号的整数类型 uintptr, 没有指定具体的bit大小但是足以容纳指针. uintptr 类型只有在底层编程是才需要, 特别是Go语言和C函数库或操作系统相交互的地方. 我们将在第十三章的 unsafe 包相关部分看到类似的例子. 最好, 還有一個無符號的整數類型 uintptr, 沒有指定具體的bit大小但是足以容納指針. uintptr 類型隻有在底層編程是纔需要, 特别是Go語言和C函數庫或操作繫統相交互的地方. 我們將在第十三章的 unsafe 包相關部分看到類似的例子.
不管它们的大小, int, uint, 和 uintptr 是不同类型大小的兄弟类型. 其中 int 和 int32 也是不同的类型, 即使int的大小也是32bit, 在需要将int当作int32类型的地方需要一个显式的类型转换, 反之亦然. 不管它們的大小, int, uint, 和 uintptr 是不同類型大小的兄弟類型. 其中 int 和 int32 也是不同的類型, 卽使int的大小也是32bit, 在需要將int當作int32類型的地方需要一個顯式的類型轉換, 反之亦然.
有符号数采用2的补码形式表示, 也就是最高位用作符号位, 一个nbit的有符号数的值域是 `-2^(n-1)``(2^(n-1)) - 1`. 无符号整数的所有bit位都用于表示非负数, 值域是 0 到 `(2^n) - 1`. 例如, int8 的值域是 -128 到 127, 而 uint8 的值域是 0 到 255. 有符號數采用2的補碼形式表示, 也就是最高位用作符號位, 一個nbit的有符號數的值域是 `-2^(n-1)``(2^(n-1)) - 1`. 無符號整數的所有bit位都用於表示非負數, 值域是 0 到 `(2^n) - 1`. 例如, int8 的值域是 -128 到 127, 而 uint8 的值域是 0 到 255.
下面是Go中关于算术, 逻辑和比较的二元运算符按照先级递减的顺序的列表: 下面是Go中關於算術, 邏輯和比較的二元運算符按照先級遞減的順序的列表:
``` ```
* / % << >> & &^ * / % << >> & &^
@ -24,14 +24,14 @@ Go同时提供了有符号和无符号的整数运算. 这里有四种int8, int1
|| ||
``` ```
二元运算符有五种优先级. 在同一优先级, 使用左优先结合律, 使用括号可以明确优先顺序, 括号也可以用于提升优先级, 例如 `mask & (1 << 28)`. 二元運算符有五種優先級. 在同一優先級, 使用左優先結合律, 使用括號可以明確優先順序, 括號也可以用於提陞優先級, 例如 `mask & (1 << 28)`.
对于上表中前两行的运算符, 例如 + 有一个相应的赋值结合运算符 +=, 可以用于简化赋值语句. 對於上表中前兩行的運算符, 例如 + 有一個相應的賦值結合運算符 +=, 可以用於簡化賦值語句.
数的算术运算符 +, -, *, 和 / 可以适用与整数, 浮点数和复数, 但是取模运算符 % 仅用于整数. 不同编程语言间, % 取模运算的行为并不相同. 在Go语言中, % 取模运算符的符号和被取模数的符号总是一致的, 因此 `-5%3``-5%-3` 结果都是 -2.除法运算符 `/` 的行为依赖于操作数是否为整数, 因此 `5.0/4.0` 的结果是 1.25, 但是 5/4 的结果是 1, 因此整数除法会向着0方向截断余数. 數的算術運算符 +, -, *, 和 / 可以適用與整數, 浮點數和複數, 但是取模運算符 % 僅用於整數. 不同編程語言間, % 取模運算的行爲併不相同. 在Go語言中, % 取模運算符的符號和被取模數的符號總是一致的, 因此 `-5%3``-5%-3` 結果都是 -2.除法運算符 `/` 的行爲依賴於操作數是否爲整數, 因此 `5.0/4.0` 的結果是 1.25, 但是 5/4 的結果是 1, 因此整數除法會向着0方向截斷餘數.
如果一个算术运算的结果, 不管是有符号或者是无符号的, 如果需要更多的bit位才能表示, 就说明是溢出了. 超出的高位的bit位部分将被丢弃. 如果原始的数值是有符号类型, 那么最终结果可能是负的, 如果最左边的bit为是1的话, 例如int8的例子: 如果一個算術運算的結果, 不管是有符號或者是無符號的, 如果需要更多的bit位纔能表示, 就説明是溢齣了. 超齣的高位的bit位部分將被丟棄. 如果原始的數值是有符號類型, 那麽最終結果可能是負的, 如果最左邊的bit爲是1的話, 例如int8的例子:
```Go ```Go
var u uint8 = 255 var u uint8 = 255
@ -41,7 +41,7 @@ var i int8 = 127
fmt.Println(i, i+1, i*i) // "127 -128 1" fmt.Println(i, i+1, i*i) // "127 -128 1"
``` ```
两个相同的整数类型可以使用下面的二元比较运算符进行比较; 比较表达式的结果是布尔类型. 兩個相同的整數類型可以使用下面的二元比較運算符進行比較; 比較表達式的結果是布爾類型.
``` ```
== equal to == equal to
@ -52,33 +52,33 @@ fmt.Println(i, i+1, i*i) // "127 -128 1"
>= greater than or equal to >= greater than or equal to
``` ```
实上, 布尔型, 数字类型 和 字符串 等基本类型都是可比较的, 也就是说两个相同类型的值可以用 == 和 != 进行比较. 此外, 整数, 浮点数和字符串可以根据比较结果排序. 许多其他类型的值是不可比较, 因此也就是不可排序的. 对于我们遇到的每种类型, 我们需要保证规则是类似的. 實上, 布爾型, 數字類型 和 字符串 等基本類型都是可比較的, 也就是説兩個相同類型的值可以用 == 和 != 進行比較. 此外, 整數, 浮點數和字符串可以根據比較結果排序. 許多其他類型的值是不可比較, 因此也就是不可排序的. 對於我們遇到的每種類型, 我們需要保證規則是類似的.
这里是一元的加法和减法运算符: 這里是一元的加法和減法運算符:
``` ```
+ 一元加法 (效果) + 一元加法 (效果)
- 负数 - 負數
``` ```
对于整数, +x 是 0+x 的简写, -x 是 0-x 的简写; 对于浮点数和复数, +x 就是 x, -x 则是 x 的负数. 對於整數, +x 是 0+x 的簡寫, -x 是 0-x 的簡寫; 對於浮點數和複數, +x 就是 x, -x 則是 x 的負數.
Go语言还提供了以下的bit位操作运算符, 前面4个操作运算符并不区分是有符号还是无符号数: Go語言還提供了以下的bit位操作運算符, 前面4個操作運算符併不區分是有符號還是無符號數:
``` ```
&算 AND &算 AND
| 位算 OR | 位算 OR
^ 位算 XOR ^ 位算 XOR
&^ 位清空 (AND NOT) &^ 位清空 (AND NOT)
<< 左移 << 左移
>> 右移 >> 右移
``` ```
位操作运算符 `^` 作为二元运算符时是按位异或(XOR), 当用作一元运算符时表示按位取反; 也就是说, 它返回一个每个bit位都取反的数. 位操作运算符 `&^` 用于按位置零(AND NOT): 表达式 `z = x &^ y` 结果z的bit位1, 如果对应y中bit位为1, 否则对应的bit位等于x相应的bit位的值. 位操作運算符 `^` 作爲二元運算符時是按位異或(XOR), 當用作一元運算符時表示按位取反; 也就是説, 它返迴一個每個bit位都取反的數. 位操作運算符 `&^` 用於按位置零(AND NOT): 表達式 `z = x &^ y` 結果z的bit位1, 如果對應y中bit位爲1, 否則對應的bit位等於x相應的bit位的值.
下面的代码演示了如何使用位操作解释uint8类型值的8个独立的bit位. 它使用了 Printf 函数的 %b 参数打印二进制格式的数字; 其中 %08b 中08表示打印至少8个数字, 不足的前缀用0填充. 下面的代碼演示了如何使用位操作解釋uint8類型值的8個獨立的bit位. 它使用了 Printf 函數的 %b 參數打印二進製格式的數字; 其中 %08b 中08表示打印至少8個數字, 不足的前綴用0填充.
```Go ```Go
var x uint8 = 1<<1 | 1<<5 var x uint8 = 1<<1 | 1<<5
@ -102,13 +102,13 @@ fmt.Printf("%08b\n", x<<1) // "01000100", the set {2, 6}
fmt.Printf("%08b\n", x>>1) // "00010001", the set {0, 4} fmt.Printf("%08b\n", x>>1) // "00010001", the set {0, 4}
``` ```
(6.5节给出了一个可以远大于一个字节的整数集的实现.) (6.5節給齣了一個可以遠大於一個字節的整數集的實現.)
在 x<<n x>>n 移位运算中, 决定了移位操作bit数部分必须是无符号数; 被操作的 x 数可以是有符号或无符号数. 算术上, 一个 x<<n 左移运算等价于乘以 2^n, 一个 x>>n 右移运算等价于除以 2^n. 在 x<<n x>>n 移位運算中, 決定了移位操作bit數部分必鬚是無符號數; 被操作的 x 數可以是有符號或無符號數. 算術上, 一個 x<<n 左移運算等價於乘以 2^n, 一個 x>>n 右移運算等價於除以 2^n.
左移运算用零填充右边空缺的bit位, 无符号数的右移运算也是用0填充左边空缺的bit位, 但是有符号数的右移运算会用符号位的值填充左边空缺的bit位. 因为这个原因, 最好用无符号运算, 这样你可以将整数完全当作一个bit位模式处理. 左移運算用零填充右邊空缺的bit位, 無符號數的右移運算也是用0填充左邊空缺的bit位, 但是有符號數的右移運算會用符號位的值填充左邊空缺的bit位. 因爲這個原因, 最好用無符號運算, 這樣你可以將整數完全當作一個bit位模式處理.
尽管Go提供了无符号数和运算, 即使数值本身不可能出现负数我们还是倾向于使用有符号的int类型, 就是数组的长度那样, 虽然使用 uint 似乎是一个更合理的选择. 事实上, 内置的 len 函数返回一个有符号的int, 我们可以像下面这个逆序循环那样处理. 盡管Go提供了無符號數和運算, 卽使數值本身不可能齣現負數我們還是傾向於使用有符號的int類型, 就是數組的長度那樣, 雖然使用 uint 似乎是一個更合理的選擇. 事實上, 內置的 len 函數返迴一個有符號的int, 我們可以像下面這個逆序循環那樣處理.
```Go ```Go
medals := []string{"gold", "silver", "bronze"} medals := []string{"gold", "silver", "bronze"}
@ -117,13 +117,13 @@ for i := len(medals) - 1; i >= 0; i-- {
} }
``` ```
另一个选择将是灾难性的. 如果 len 返回一个无符号数, 那么 i 也将是无符号的 uint, 然后条件 i >= 0 则永远为真. 在三次迭代之后, 也就是 i == 0 时, i-- 语句将不会产生 -1, 而是变成一个uint的最大值(可能是 2^64 - 1), 然后 medals[i] 表达式将发生运行时 panic 异常(§5.9), 也就是试图访问一个切片范围以外的元素. 另一個選擇將是災難性的. 如果 len 返迴一個無符號數, 那麽 i 也將是無符號的 uint, 然後條件 i >= 0 則永遠爲眞. 在三次迭代之後, 也就是 i == 0 時, i-- 語句將不會産生 -1, 而是變成一個uint的最大值(可能是 2^64 - 1), 然後 medals[i] 表達式將發生運行時 panic 異常(§5.9), 也就是試圖訪問一個切片范圍以外的元素.
出于这个原因, 无符号数往往只有在位运算或其它特殊的运算常见才会使用, 就像 bit 集合, 分形二进制文件格式, 或者是哈希和加密操作等. 它们通常并不用于仅仅是表达非负数量的场合. 齣於這個原因, 無符號數往往隻有在位運算或其它特殊的運算常見纔會使用, 就像 bit 集合, 分形二進製文件格式, 或者是哈希和加密操作等. 它們通常併不用於僅僅是表達非負數量的場合.
一般来说, 需要一个显式的转换将一个值从一种类型转化位另一种类型, 并且算术和逻辑运算的二元操作中必须是相同的类型. 虽然这偶尔会导致很长的表达式, 但是它消除了所有的类型相关的问题, 也使得程序容易理解. 一般來説, 需要一個顯式的轉換將一個值從一種類型轉化位另一種類型, 併且算術和邏輯運算的二元操作中必鬚是相同的類型. 雖然這偶爾會導致很長的表達式, 但是它消除了所有的類型相關的問題, 也使得程序容易理解.
从其他类似场景下, 考虑下面这个代码: 從其他類似場景下, 考慮下面這個代碼:
```Go ```Go
var apples int32 = 1 var apples int32 = 1
@ -131,19 +131,19 @@ var oranges int16 = 2
var compote int = apples + oranges // compile error var compote int = apples + oranges // compile error
``` ```
当尝试编译这三个语句时, 将产生一个错误信息: 當嚐試編譯這三個語句時, 將産生一個錯誤信息:
``` ```
invalid operation: apples + oranges (mismatched types int32 and int16) invalid operation: apples + oranges (mismatched types int32 and int16)
``` ```
这种类型不匹配的问题可以有几种不同的方法修复, 最常见方法是将它们都显式转型位一个常见类型: 這種類型不匹配的問題可以有幾種不同的方法脩複, 最常見方法是將它們都顯式轉型位一個常見類型:
```Go ```Go
var compote = int(apples) + int(oranges) var compote = int(apples) + int(oranges)
``` ```
如2.5节所述, 对于每种类型T, 类型转换操作T(x)将x转换位T类型, 如果转换允许的话. 许多 整形数之间的相互转换并不会改变数值; 它们只是告诉编译器如何解释这个值. 但是对于将一个大尺寸的整数类型转位一个小尺寸的整数类型, 或者是将一个浮点数转位整数, 可能会改变数值或丢失精度: 如2.5節所述, 對於每種類型T, 類型轉換操作T(x)將x轉換位T類型, 如果轉換允許的話. 許多 整形數之間的相互轉換併不會改變數值; 它們隻是告訴編譯器如何解釋這個值. 但是對於將一個大尺寸的整數類型轉位一個小尺寸的整數類型, 或者是將一個浮點數轉位整數, 可能會改變數值或丟失精度:
```Go ```Go
f := 3.141 // a float64 f := 3.141 // a float64
@ -153,16 +153,16 @@ f = 1.99
fmt.Println(int(f)) // "1" fmt.Println(int(f)) // "1"
``` ```
点数到整数的转换将丢失任何小数部分, 向数轴零方向截断. 你应该避免操作目标类型表示范围的数值类型转换, 因为截断的行为依赖于具体的实现: 點數到整數的轉換將丟失任何小數部分, 向數軸零方向截斷. 你應該避免操作目標類型表示范圍的數值類型轉換, 因爲截斷的行爲依賴於具體的實現:
```Go ```Go
f := 1e100 // a float64 f := 1e100 // a float64
i := int(f) // 结果依赖于具体实现 i := int(f) // 結果依賴於具體實現
``` ```
任何大小的整数字面值都可以用以0开始的八进制格式书写, 例如 0666, 或用以0x或0X开头的十六进制格式书写, 例如 0xdeadbeef. 十六进制数字可以用大写或小写字母. 如今八进制数据通常用于POSIX操作系统上的文件访问权限标志, 十六进制数字则更强调数字值的bit位模式. 任何大小的整數字面值都可以用以0開始的八進製格式書寫, 例如 0666, 或用以0x或0X開頭的十六進製格式書寫, 例如 0xdeadbeef. 十六進製數字可以用大寫或小寫字母. 如今八進製數據通常用於POSIX操作繫統上的文件訪問權限標誌, 十六進製數字則更強調數字值的bit位模式.
当使用 fmt 包打印一个数值时, 我们可以用 %d, %o, 或 %x 控制输出的进制格式, 就像下面的例子: 當使用 fmt 包打印一個數值時, 我們可以用 %d, %o, 或 %x 控製輸齣的進製格式, 就像下面的例子:
```Go ```Go
o := 0666 o := 0666
@ -173,18 +173,18 @@ fmt.Printf("%d %[1]x %#[1]x %#[1]X\n", x)
// 3735928559 deadbeef 0xdeadbeef 0XDEADBEEF // 3735928559 deadbeef 0xdeadbeef 0XDEADBEEF
``` ```
请注意 fmt 的两个使用技巧. 通常 Printf 格式化字符串包含多个 % 参数时将对应相同数量的额外操作数, 但是 % 之后的 `[1]` 副词告诉Printf函数再次使用第一个操作数. 第二, % 后的 `#` 副词告诉 Printf 在用 %o, %x 或 %X 输出时生成 0, 0x 或 0X前缀. 請註意 fmt 的兩個使用技巧. 通常 Printf 格式化字符串包含多個 % 參數時將對應相同數量的額外操作數, 但是 % 之後的 `[1]` 副詞告訴Printf函數再次使用第一個操作數. 第二, % 後的 `#` 副詞告訴 Printf 在用 %o, %x 或 %X 輸齣時生成 0, 0x 或 0X前綴.
字符面值通过一对单引号直接包含对应字符. 最简单的例子是 ASCII 中类似 'a' 字符面值, 但是我们可以通过转义的数值来表示任意的Unicode码点对应的字符, 马上将会看到例子. 字符面值通過一對單引號直接包含對應字符. 最簡單的例子是 ASCII 中類似 'a' 字符面值, 但是我們可以通過轉義的數值來表示任意的Unicode碼點對應的字符, 馬上將會看到例子.
字符使用 `%c` 参数打印, 或者是 `%q` 参数打印带单引号的字符: 字符使用 `%c` 參數打印, 或者是 `%q` 參數打印帶單引號的字符:
```Go ```Go
ascii := 'a' ascii := 'a'
unicode := '' unicode := ''
newline := '\n' newline := '\n'
fmt.Printf("%d %[1]c %[1]q\n", ascii) // "97 a 'a'" fmt.Printf("%d %[1]c %[1]q\n", ascii) // "97 a 'a'"
fmt.Printf("%d %[1]c %[1]q\n", unicode) // "22269 国 '国'" fmt.Printf("%d %[1]c %[1]q\n", unicode) // "22269 國 '國'"
fmt.Printf("%d %[1]q\n", newline) // "10 '\n'" fmt.Printf("%d %[1]q\n", newline) // "10 '\n'"
``` ```

View File

@ -1,23 +1,23 @@
## 3.2. 浮點數 ## 3.2. 浮點數
Go语言提供了两种精度的浮点数, float32 和 float64. 它们的算术规范由 IEEE754 国际标准定义, 该浮点数规范被所有现代的CPU支持. Go語言提供了兩種精度的浮點數, float32 和 float64. 它們的算術規范由 IEEE754 國際標準定義, 該浮點數規范被所有現代的CPU支持.
这些数值类型的范围可以从很微小到很巨大. 浮点数的范围极限值可以在 matn 包找到. 常量 math.MaxFloat32 表示 float32 能表示的最大数值, 大约是 3.4e38, 对应的 math.MaxFloat64 常量大约是 1.8e308. 它们能表示的最小值近似分别是1.4e-45 和 4.9e-324. 這些數值類型的范圍可以從很微小到很鉅大. 浮點數的范圍極限值可以在 matn 包找到. 常量 math.MaxFloat32 表示 float32 能表示的最大數值, 大約是 3.4e38, 對應的 math.MaxFloat64 常量大約是 1.8e308. 它們能表示的最小值近似分别是1.4e-45 和 4.9e-324.
个 float32 类型的浮点数可以提供大约6个十进制数的精度, 而 float64 则可以提供约 15个十进制数精度; 通常应该优先使用 float64 类型, 因为 float32 类型的累计计算误差很容易扩散, 并且 float32 能精度表示的正整数并不是很大: 個 float32 類型的浮點數可以提供大約6個十進製數的精度, 而 float64 則可以提供約 15個十進製數精度; 通常應該優先使用 float64 類型, 因爲 float32 類型的纍計計算誤差很容易擴散, 併且 float32 能精度表示的正整數併不是很大:
```Go ```Go
var f float32 = 16777216 // 1 << 24 var f float32 = 16777216 // 1 << 24
fmt.Println(f == f+1) // "true"! fmt.Println(f == f+1) // "true"!
``` ```
点数的字面值可以直接写小数部分, 想这样: 點數的字面值可以直接寫小數部分, 想這樣:
```Go ```Go
const e = 2.71828 // (approximately) const e = 2.71828 // (approximately)
``` ```
数点前面或后面的数字都可能被省略(例如 .707 或 1.). 很小或很大的数最好用科学计数法书写, 通过e或E来指定指数部分: 數點前面或後面的數字都可能被省略(例如 .707 或 1.). 很小或很大的數最好用科學計數法書寫, 通過e或E來指定指數部分:
```Go ```Go
const Avogadro = 6.02214129e23 const Avogadro = 6.02214129e23
@ -25,7 +25,7 @@ const Planck = 6.62606957e-34
``` ```
用 Printf 函数的 %g 参数打印浮点数, 将采用紧凑的表示形式打印, 并提供足够的精度, 但是对应表格的数据, 使用 %e (带指数) 或 %f 的形式打印可能更合适. 所有的这三个打印形式都可以指定打印的宽度和控制打印精度. 用 Printf 函數的 %g 參數打印浮點數, 將采用緊湊的表示形式打印, 併提供足夠的精度, 但是對應表格的數據, 使用 %e (帶指數) 或 %f 的形式打印可能更合適. 所有的這三個打印形式都可以指定打印的寬度和控製打印精度.
```Go ```Go
for x := 0; x < 8; x++ { for x := 0; x < 8; x++ {
@ -33,7 +33,7 @@ for x := 0; x < 8; x++ {
} }
``` ```
上面代码打印e的幂, 打印精度是小数点后三个小数精度和8个字符宽度: 上面代碼打印e的冪, 打印精度是小數點後三個小數精度和8個字符寬度:
``` ```
x = 0 e^x = 1.000 x = 0 e^x = 1.000
@ -46,21 +46,21 @@ x = 6 e^x = 403.429
x = 7 e^x = 1096.633 x = 7 e^x = 1096.633
``` ```
math 包中除了提供大量常用的数学函数外, 还提供了IEEE754标准中特殊数值的创建和测试: 正无穷大和负无穷大, 分别用于表示太大溢出的数字和除零的结果; 还有 NaN 非数, 一般用于表示无效的除法操作结果 0/0 或 Sqrt(-1). math 包中除了提供大量常用的數學函數外, 還提供了IEEE754標準中特殊數值的創建和測試: 正無窮大和負無窮大, 分别用於表示太大溢齣的數字和除零的結果; 還有 NaN 非數, 一般用於表示無效的除法操作結果 0/0 或 Sqrt(-1).
```Go ```Go
var z float64 var z float64
fmt.Println(z, -z, 1/z, -1/z, z/z) // "0 -0 +Inf -Inf NaN" fmt.Println(z, -z, 1/z, -1/z, z/z) // "0 -0 +Inf -Inf NaN"
``` ```
数 math.IsNaN 用于测试一个数是否是非数 NaN, math.NaN 则返回非数对应的值. 虽然可以用 math.NaN 来表示一个非法的结果, 但是测试一个结果是否是非数 NaN 则是充满风险, 因为 NaN 和任何数都是不相等的: 數 math.IsNaN 用於測試一個數是否是非數 NaN, math.NaN 則返迴非數對應的值. 雖然可以用 math.NaN 來表示一個非法的結果, 但是測試一個結果是否是非數 NaN 則是充滿風險, 因爲 NaN 和任何數都是不相等的:
```Go ```Go
nan := math.NaN() nan := math.NaN()
fmt.Println(nan == nan, nan < nan, nan > nan) // "false false false" fmt.Println(nan == nan, nan < nan, nan > nan) // "false false false"
``` ```
如果一个函数返回的浮点数结果可能失败, 最好的做法是用单独的标志报告失败, 像这样: 如果一個函數返迴的浮點數結果可能失敗, 最好的做法是用單獨的標誌報告失敗, 像這樣:
```Go ```Go
func compute() (value float64, ok bool) { func compute() (value float64, ok bool) {
@ -72,7 +72,7 @@ func compute() (value float64, ok bool) {
} }
``` ```
接下来的程序演示了浮点计算图形. 它是带有两个参数的 z = f(x, y) 函数的三维形式, 使用了可缩放矢量图形(SVG)格式输出, 一个用于矢量线绘制的XML标准. 图3.1显示了 sin(r)/r 函数的输出图形, 其中 r 是 sqrt(x*x+y*y). 接下來的程序演示了浮點計算圖形. 它是帶有兩個參數的 z = f(x, y) 函數的三維形式, 使用了可縮放矢量圖形(SVG)格式輸齣, 一個用於矢量線繪製的XML標準. 圖3.1顯示了 sin(r)/r 函數的輸齣圖形, 其中 r 是 sqrt(x*x+y*y).
![](../images/ch3-01.png) ![](../images/ch3-01.png)
@ -135,32 +135,32 @@ func f(x, y float64) float64 {
} }
``` ```
注意的是 corner 返回了两个结果, 对应 corner 的坐标参数. 註意的是 corner 返迴了兩個結果, 對應 corner 的坐標參數.
要解释程序是如何工作的需要了解基本的几何知识, 但是我们可以跳过几何原理, 因为程序的重点是演示浮点运算. 程序的本质是三个不同的坐标系中映射关系, 如图3.2所示. 第一个是 100x100 的二维网格, 对应整数整数坐标(i,j), 从远处的 (0, 0) 位置开始. 我们从远处像前面绘制, 因此远处先绘制的多边形有可能被前面后绘制的多边形覆盖. 要解釋程序是如何工作的需要了解基本的幾何知識, 但是我們可以跳過幾何原理, 因爲程序的重點是演示浮點運算. 程序的本質是三個不同的坐標繫中映射關繫, 如圖3.2所示. 第一個是 100x100 的二維網格, 對應整數整數坐標(i,j), 從遠處的 (0, 0) 位置開始. 我們從遠處像前面繪製, 因此遠處先繪製的多邊形有可能被前面後繪製的多邊形覆蓋.
第二个坐标系是一个三维的网格浮点坐标(x,y,z), 其中x和y是i和j的线性函数, 通过平移转换位center的中心, 然后用xyrange系数缩放. 高度z是函数f(x,y)的值. 第二個坐標繫是一個三維的網格浮點坐標(x,y,z), 其中x和y是i和j的線性函數, 通過平移轉換位center的中心, 然後用xyrange繫數縮放. 高度z是函數f(x,y)的值.
第三个坐标系是一个二维的画布, 起点(0,0)在左上角. 画布中点的坐标用(sx, sy)表示. 我们使用等角投影将三维点 第三個坐標繫是一個二維的畵布, 起點(0,0)在左上角. 畵布中點的坐標用(sx, sy)表示. 我們使用等角投影將三維點
![](../images/ch3-02.png) ![](../images/ch3-02.png)
(x,y,z) 投影到二维的画布中. 画布中从远处到右边的点对应较大的x值和较大的y值. 并且画布中x和y值越大, 则对应的z值越小. x和y的垂直和水平缩放系数来自30度角的正弦和余弦值. z的缩放系数0.4, 是一个任意选择的参数. (x,y,z) 投影到二維的畵布中. 畵布中從遠處到右邊的點對應較大的x值和較大的y值. 併且畵布中x和y值越大, 則對應的z值越小. x和y的垂直和水平縮放繫數來自30度角的正絃和餘絃值. z的縮放繫數0.4, 是一個任意選擇的參數.
对于二维网格中的每一个单位, main函数计算单元的四个顶点在画布中对应多边形ABCD的顶点, 其中B对应(i,j)顶点位置, A, C, 和 D是相邻的顶点, 然后输出SVG的绘制指令. 對於二維網格中的每一個單位, main函數計算單元的四個頂點在畵布中對應多邊形ABCD的頂點, 其中B對應(i,j)頂點位置, A, C, 和 D是相鄰的頂點, 然後輸齣SVG的繪製指令.
**练习3.1:** 如果 f 函数返回的是无限制的 float64 值, 那么SVG文件可能输出无效的<polygon>多边形元素(虽然许多SVG渲染器会妥善处理这类问题). 修改程序跳过无效的多边形. **練習3.1:** 如果 f 函數返迴的是無限製的 float64 值, 那麽SVG文件可能輸齣無效的<polygon>多邊形元素(雖然許多SVG渲染器會妥善處理這類問題). 脩改程序跳過無效的多邊形.
**练习3.2:** 试验math包中其他函数的渲染图形. 你是否能输出一个egg box, moguls, 或 a saddle 图案? **練習3.2:** 試驗math包中其他函數的渲染圖形. 你是否能輸齣一個egg box, moguls, 或 a saddle 圖案?
**练习3.3:**根据高度给每个多边形上色, 那样峰值部将是红色(#ff0000), 谷部将是蓝色(#0000ff). **練習3.3:**根據高度給每個多邊形上色, 那樣峯值部將是紅色(#ff0000), 谷部將是藍色(#0000ff).
**3.4:** 参考1.7节Lissajous例子的函数, 构造一个web服务器, 用于计算函数曲面然后返回SVG数据给客户端. 服务器必须设置 Content-Type 头部: **3.4:** 參考1.7節Lissajous例子的函數, 構造一個web服務器, 用於計算函數麴面然後返迴SVG數據給客戶端. 服務器必鬚設置 Content-Type 頭部:
```Go ```Go
w.Header().Set("Content-Type", "image/svg+xml") w.Header().Set("Content-Type", "image/svg+xml")
``` ```
(这一步在Lissajous例子中不是必须的, 因为服务器使用标准的PNG图像格式, 可以根据前面的512个字节自动输出对应的头部.) 允许客户端通过HTTP请求参数设置高度, 宽度, 和颜色等参数. (這一步在Lissajous例子中不是必鬚的, 因爲服務器使用標準的PNG圖像格式, 可以根據前面的512個字節自動輸齣對應的頭部.) 允許客戶端通過HTTP請求參數設置高度, 寬度, 和顔色等參數.

View File

@ -1,7 +1,7 @@
## 3.3. 複數 ## 3.3. 複數
Go提供了两种精度的复数类似, complex64 和 complex128, 分别对应 float32 和 float64精度. 内置的 complex 函数用于构建复数, 内建的 real 和 imag 函数返回复数的实部和虚部: Go提供了兩種精度的複數類似, complex64 和 complex128, 分别對應 float32 和 float64精度. 內置的 complex 函數用於構建複數, 內建的 real 和 imag 函數返迴複數的實部和虛部:
```Go ```Go
var x complex128 = complex(1, 2) // 1+2i var x complex128 = complex(1, 2) // 1+2i
@ -11,28 +11,28 @@ fmt.Println(real(x*y)) // "-5"
fmt.Println(imag(x*y)) // "10" fmt.Println(imag(x*y)) // "10"
``` ```
如果一个浮点数面值或一个十进制整数面值后面跟着一个i, 例如 3.141592i 或 2i, 它将构成一个复数的虚部, 复数的实部是0: 如果一個浮點數面值或一個十進製整數面值後面跟着一個i, 例如 3.141592i 或 2i, 它將構成一個複數的虛部, 複數的實部是0:
```Go ```Go
fmt.Println(1i * 1i) // "(-1+0i)", i^2 = -1 fmt.Println(1i * 1i) // "(-1+0i)", i^2 = -1
``` ```
在常量算术规则下, 一个复数常量可以加到另一个常量(整数或浮点数, 实部或虚部), 我们可以用自然的方式写复数, 就像 1+2i, 或与之等价的写法 2i+1. 上面x和y的声明语句还可以简化: 在常量算術規則下, 一個複數常量可以加到另一個常量(整數或浮點數, 實部或虛部), 我們可以用自然的方式寫複數, 就像 1+2i, 或與之等價的寫法 2i+1. 上面x和y的聲明語句還可以簡化:
```Go ```Go
x := 1 + 2i x := 1 + 2i
y := 3 + 4i y := 3 + 4i
``` ```
复数也可以用 == 和 != 进行相等比较. 只有两个复数的实部和虚部都相等的时候它们才是相等的. 複數也可以用 == 和 != 進行相等比較. 隻有兩個複數的實部和虛部都相等的時候它們纔是相等的.
math/cmplx 包提供了复数处理的许多函数, 例如求复数的平方根函数和求幂函数. math/cmplx 包提供了複數處理的許多函數, 例如求複數的平方根函數和求冪函數.
```Go ```Go
fmt.Println(cmplx.Sqrt(-1)) // "(0+1i)" fmt.Println(cmplx.Sqrt(-1)) // "(0+1i)"
``` ```
下面的程序使用complex128复数算法来生成一个Mandelbrot图像. 下面的程序使用complex128複數算法來生成一個Mandelbrot圖像.
```Go ```Go
gopl.io/ch3/mandelbrot gopl.io/ch3/mandelbrot
@ -83,19 +83,19 @@ func mandelbrot(z complex128) color.Color {
} }
``` ```
历1024x1024图像每个点的两个嵌套的循环对应 -2 到 +2 区间的复数平面. 程序反复测试每个点对应复数值平方值加一个增量值对应的点是否超出半径为2的圆. 如果超过了, 通过根据逃逸的迭代次数对应的灰度颜色来代替. 如果不是, 该点属于Mandelbrot集合, 使用黑色颜色标记. 最终程序将生成的PNG格式分形图像图像输出到标准输出, 如图3.3所示. 歷1024x1024圖像每個點的兩個嵌套的循環對應 -2 到 +2 區間的複數平面. 程序反複測試每個點對應複數值平方值加一個增量值對應的點是否超齣半徑爲2的圓. 如果超過了, 通過根據逃逸的迭代次數對應的灰度顔色來代替. 如果不是, 該點屬於Mandelbrot集合, 使用黑色顔色標記. 最終程序將生成的PNG格式分形圖像圖像輸齣到標準輸齣, 如圖3.3所示.
**练习3.5:** 实现一个彩色的Mandelbrot图像, 使用 image.NewRGBA 创建图像, 使用 color.RGBA 或 color.YCbCr 生成颜色. **練習3.5:** 實現一個綵色的Mandelbrot圖像, 使用 image.NewRGBA 創建圖像, 使用 color.RGBA 或 color.YCbCr 生成顔色.
**练习3.6:** 超采样技术可以降低每个像素对计算颜色值和平均值的影响. 简单的方法是将每个像素分层四个子像素, 实现它. **練習3.6:** 超采樣技術可以降低每個像素對計算顔色值和平均值的影響. 簡單的方法是將每個像素分層四個子像素, 實現它.
**练习3.7:** 另一个生成分形图像的方式是使用牛顿法来求解一个复数方程, 例如 z^4 1 = 0. 每个起点到四个根的迭代次数对应阴影的灰度. 方程根对应的点用颜色表示. **練習3.7:** 另一個生成分形圖像的方式是使用牛頓法來求解一個複數方程, 例如 z^4 1 = 0. 每個起點到四個根的迭代次數對應陰影的灰度. 方程根對應的點用顔色表示.
![](../images/ch3-03.png) ![](../images/ch3-03.png)
**练习3.8:** 通过提高精度来生成更多级别的分形. 使用四种不同精度类型的数字实现相同的分形: complex64, complex128, big.Float, and big.Rat. (后面两种类型在 math/big 包声明. Float是有指定限精度的浮点数; Rat是无效精度的有理数.) 它们间的性能和内存使用对比如何? 当渲染图可见时缩放的级别是多少? **練習3.8:** 通過提高精度來生成更多級别的分形. 使用四種不同精度類型的數字實現相同的分形: complex64, complex128, big.Float, and big.Rat. (後面兩種類型在 math/big 包聲明. Float是有指定限精度的浮點數; Rat是無效精度的有理數.) 它們間的性能和內存使用對比如何? 當渲染圖可見時縮放的級别是多少?
**练习3.9:** 编写一个web服务器, 用于给客户端生成分形的图像. 运行客户端用过HTTP参数参数指定x,y和zoom参数. **練習3.9:** 編寫一個web服務器, 用於給客戶端生成分形的圖像. 運行客戶端用過HTTP參數參數指定x,y和zoom參數.

View File

@ -1,15 +1,15 @@
### 3.5.4. 字符串和Byte切片 ### 3.5.4. 字符串和Byte切片
标准库中有四个包对字符串处理尤为重要: bytes, strings, strconv, 和 unicode. strings 包提供了许多如字符串的查询, 替换, 比较, 截断, 拆分, 和合并等功能. 標準庫中有四個包對字符串處理尤爲重要: bytes, strings, strconv, 和 unicode. strings 包提供了許多如字符串的査詢, 替換, 比較, 截斷, 拆分, 和合併等功能.
bytes 包也提供了很多类似功能的函数, 但是针对和字符串有着相同结构的 []byte 类型. 因为字符串是只读的, 因此逐步构建字符串会导致很多分配和复制. 在这种情况下, 使用 bytes.Buffer 类型会更有效, 稍后我们将展示. bytes 包也提供了很多類似功能的函數, 但是針對和字符串有着相同結構的 []byte 類型. 因爲字符串是隻讀的, 因此逐步構建字符串會導致很多分配和複製. 在這種情況下, 使用 bytes.Buffer 類型會更有效, 稍後我們將展示.
strconv 包提供了 布尔型, 整型数, 浮点数和对应字符串间的相互转换, 还提供了双引号的字符串面值形式的转换. strconv 包提供了 布爾型, 整型數, 浮點數和對應字符串間的相互轉換, 還提供了雙引號的字符串面值形式的轉換.
unicode 包提供了类似 IsDigit, IsLetter, IsUpper, 和 IsLower 等功能, 它们用于给字符分类. 每个函数有一个单一的rune类型的参数, 然后返回一个布尔值. 像 ToUpper 和 ToLower 之类的转换函数将用于rune字符的大小写转换. 所有的这些函数都是遵循Unicode标准定义的字母,数字等分类规范. strings 包也有类似的函数, 它们是 ToUpper 和 ToLower, 将原始字符串的每个字符都做相应的转换, 然后返回新的字符串. unicode 包提供了類似 IsDigit, IsLetter, IsUpper, 和 IsLower 等功能, 它們用於給字符分類. 每個函數有一個單一的rune類型的參數, 然後返迴一個布爾值. 像 ToUpper 和 ToLower 之類的轉換函數將用於rune字符的大小寫轉換. 所有的這些函數都是遵循Unicode標準定義的字母,數字等分類規范. strings 包也有類似的函數, 它們是 ToUpper 和 ToLower, 將原始字符串的每個字符都做相應的轉換, 然後返迴新的字符串.
下面的 basename 函数的灵感由Unix shell的同名工具而来. 在我们实现的版本中, basename(s) 将看起来像是系统路径的前缀删除, 同时将看似文件类型的后缀名删除: 下面的 basename 函數的靈感由Unix shell的同名工具而來. 在我們實現的版本中, basename(s) 將看起來像是繫統路徑的前綴刪除, 同時將看似文件類型的後綴名刪除:
```Go ```Go
fmt.Println(basename("a/b/c.go")) // "c" fmt.Println(basename("a/b/c.go")) // "c"
@ -17,7 +17,7 @@ fmt.Println(basename("c.d.go")) // "c.d"
fmt.Println(basename("abc")) // "abc" fmt.Println(basename("abc")) // "abc"
``` ```
第一个版本并没有使用任何库, 全部手工实现: 第一個版本併沒有使用任何庫, 全部手工實現:
```Go ```Go
gopl.io/ch3/basename1 gopl.io/ch3/basename1
@ -42,7 +42,7 @@ func basename(s string) string {
} }
``` ```
个简化的版本使用了 strings.LastIndex 库函数: 個簡化的版本使用了 strings.LastIndex 庫函數:
```Go ```Go
gopl.io/ch3/basename2 gopl.io/ch3/basename2
@ -57,10 +57,10 @@ func basename(s string) string {
} }
``` ```
path 和 path/filepath 包提供了关于文件名更一般的函数操作. 使用斜杠分隔路径可以在任何操作系统上工作. 斜杠本身不应该用于文件名, 但是在其他一些领域可能是有效的, 例如URL路径组件. 相比之下, path/filepath 包使用操作系统本身的路径规则, 例如 POSIX 系统使用 /foo/bar, Microsoft Windows 使用 c:\foo\bar 等. path 和 path/filepath 包提供了關於文件名更一般的函數操作. 使用斜槓分隔路徑可以在任何操作繫統上工作. 斜槓本身不應該用於文件名, 但是在其他一些領域可能是有效的, 例如URL路徑組件. 相比之下, path/filepath 包使用操作繫統本身的路徑規則, 例如 POSIX 繫統使用 /foo/bar, Microsoft Windows 使用 c:\foo\bar 等.
让我们继续另一个字符串的例子. 任务是将一个表示整值的字符串, 每隔三个字符插入一个逗号, 例如 "12345" 处理后成为 "12,345". 这个版本只适用于整数类型; 支持浮点数类型的支持留做练习. 讓我們繼續另一個字符串的例子. 任務是將一個表示整值的字符串, 每隔三個字符插入一個逗號, 例如 "12345" 處理後成爲 "12,345". 這個版本隻適用於整數類型; 支持浮點數類型的支持留做練習.
```Go ```Go
gopl.io/ch3/comma gopl.io/ch3/comma
@ -75,11 +75,11 @@ func comma(s string) string {
} }
``` ```
输入 comma 的参数是一个字符串. 如果输入字符串的长度小于或等于3的话, 则不需要插入逗号. 否则, comma 将在最后三个字符前切割为两个两个子串, 然后用前面的子串递归调用自身. 輸入 comma 的參數是一個字符串. 如果輸入字符串的長度小於或等於3的話, 則不需要插入逗號. 否則, comma 將在最後三個字符前切割爲兩個兩個子串, 然後用前面的子串遞歸調用自身.
个字符串包含的字节数组, 一旦创建, 是不可变的. 相比之下, 一个字节切片的原始则可以自由地修改. 個字符串包含的字節數組, 一旦創建, 是不可變的. 相比之下, 一個字節切片的原始則可以自由地脩改.
字符串和字节切片可以相互转换: 字符串和字節切片可以相互轉換:
```Go ```Go
s := "abc" s := "abc"
@ -87,9 +87,9 @@ b := []byte(s)
s2 := string(b) s2 := string(b)
``` ```
从概念上讲, []byte(s) 转换是分配了一个新的字节数组保存了字符串数据的拷贝, 然后引用这个字节数组. 编译器的优化可以避免在一些场景下分配和复制字符串数据, 但总的来说需要确保在b被修改的情况下, 原始的s字符串也不会改变. 将一个字节切片转到字符串的 string(b) 操作则是构造一个拷贝, 以确保s2字符串是只读的. 從概念上講, []byte(s) 轉換是分配了一個新的字節數組保存了字符串數據的拷貝, 然後引用這個字節數組. 編譯器的優化可以避免在一些場景下分配和複製字符串數據, 但總的來説需要確保在b被脩改的情況下, 原始的s字符串也不會改變. 將一個字節切片轉到字符串的 string(b) 操作則是構造一個拷貝, 以確保s2字符串是隻讀的.
为了避免转换中不必要的内存分配, bytes包和strings同时提供了许多类似的实用函数. 下面是strings包中的六个函数: 爲了避免轉換中不必要的內存分配, bytes包和strings同時提供了許多類似的實用函數. 下面是strings包中的六個函數:
```Go ```Go
func Contains(s, substr string) bool func Contains(s, substr string) bool
@ -100,7 +100,7 @@ func Index(s, sep string) int
func Join(a []string, sep string) string func Join(a []string, sep string) string
``` ```
bytes 包中对应的六个函数: bytes 包中對應的六個函數:
```Go ```Go
func Contains(b, subslice []byte) bool func Contains(b, subslice []byte) bool
@ -111,9 +111,9 @@ func Index(s, sep []byte) int
func Join(s [][]byte, sep []byte) []byte func Join(s [][]byte, sep []byte) []byte
``` ```
唯一的区别是字符串类型参数被替换成了字节切片类型的参数. 唯一的區别是字符串類型參數被替換成了字節切片類型的參數.
bytes 包还提供了 Buffer 类型用于字节切片的缓存. 一个 Buffer 开始是空的, 但是随着 string, byte, 和 []byte 等类型数据的写入可以动态增长, 一个 bytes.Buffer 变量并不需要处理化, 因此零值也是有效的: bytes 包還提供了 Buffer 類型用於字節切片的緩存. 一個 Buffer 開始是空的, 但是隨着 string, byte, 和 []byte 等類型數據的寫入可以動態增長, 一個 bytes.Buffer 變量併不需要處理化, 因此零值也是有效的:
```Go ```Go
gopl.io/ch3/printints gopl.io/ch3/printints
@ -137,16 +137,16 @@ func main() {
} }
``` ```
当向 bytes.Buffer 添加任意字符的UTF8编码, 最好使用 bytes.Buffer 的 WriteRune 方法, 但是 WriteByte 方法对于写入类似 '[' 和 ']' 等 ASCII 字符则更有效. 當向 bytes.Buffer 添加任意字符的UTF8編碼, 最好使用 bytes.Buffer 的 WriteRune 方法, 但是 WriteByte 方法對於寫入類似 '[' 和 ']' 等 ASCII 字符則更有效.
bytes.Buffer 类型有着诸多实用的功能, 我们在第七章讨论接口时层涉及到, 我们将看看如何将它用作一个I/O 的输入和输出对象, 例如 Fprintf 的 io.Writer 输出, 或作为输入源 io.Reader. bytes.Buffer 類型有着諸多實用的功能, 我們在第七章討論接口時層涉及到, 我們將看看如何將它用作一個I/O 的輸入和輸齣對象, 例如 Fprintf 的 io.Writer 輸齣, 或作爲輸入源 io.Reader.
**练习3.10:** 编写一个非递归版本的comma函数, 使用 bytes.Buffer 代替字符串链接操作. **練習3.10:** 編寫一個非遞歸版本的comma函數, 使用 bytes.Buffer 代替字符串鏈接操作.
**练习3.11:** 完善 comma 函数, 以支持浮点数处理和一个可选的正负号处理. **練習3.11:** 完善 comma 函數, 以支持浮點數處理和一個可選的正負號處理.
**练习3.12:** 编写一个函数, 判断两个字符串是否是是相互打乱的, 也就是说它们有着相同的字符, 但是对应不同的顺序. **練習3.12:** 編寫一個函數, 判斷兩個字符串是否是是相互打亂的, 也就是説它們有着相同的字符, 但是對應不同的順序.

View File

@ -1,9 +1,9 @@
### 3.6.1. iota 常量生成器 ### 3.6.1. iota 常量生成器
常量声明可以使用 iota 常量生成器, 用于生成一组相似的常量值, 但是不用每行都写一遍. 在一个 const 声明语句中, 在开始一行 iota 将会被置为0, 然后在每一个有常量声明的行加一. 常量聲明可以使用 iota 常量生成器, 用於生成一組相似的常量值, 但是不用每行都寫一遍. 在一個 const 聲明語句中, 在開始一行 iota 將會被置爲0, 然後在每一個有常量聲明的行加一.
下面是来自 time 包的例子, 它首先定义了Weekday命名类型, 然后为一周的每天定义一个常量, 从周日0开始. 这种类型一般被称为枚举类型. 下面是來自 time 包的例子, 它首先定義了Weekday命名類型, 然後爲一週的每天定義一個常量, 從週日0開始. 這種類型一般被稱爲枚舉類型.
```Go ```Go
type Weekday int type Weekday int
@ -19,9 +19,9 @@ const (
) )
``` ```
周一将对应0, 周一为1, 如此等等. 週一將對應0, 週一爲1, 如此等等.
们也可以在复杂的常量表达式中使用 iota, 下面是来自 net 包的例子, 用于给一个无符号整数的最低5bit的每个bit给定一个名字: 們也可以在複雜的常量表達式中使用 iota, 下面是來自 net 包的例子, 用於給一個無符號整數的最低5bit的每個bit給定一個名字:
```Go ```Go
type Flags uint type Flags uint
@ -35,7 +35,7 @@ const (
) )
``` ```
随着 iota 的递增, 每个常量对应表达式 1 << iota, 是连续的2的幂, 分别对应一个bit位置. 使用这些常量可以测试, 设置, 或清除对应的bit位的值: 隨着 iota 的遞增, 每個常量對應表達式 1 << iota, 是連續的2的冪, 分别對應一個bit位置. 使用這些常量可以測試, 設置, 或清除對應的bit位的值:
```Go ```Go
gopl.io/ch3/netflag gopl.io/ch3/netflag
@ -56,7 +56,7 @@ unc main() {
} }
``` ```
下面是一个更复杂的例子, 每个常量都是1024的幂: 下面是一個更複雜的例子, 每個常量都是1024的冪:
```Go ```Go
const ( const (
@ -72,8 +72,8 @@ const (
) )
``` ```
iota 机制也有其局限性. 例如, 它并不能用于产生1000的幂(KB,MB,等等), 因为并没有计算幂的运算符. iota 機製也有其局限性. 例如, 它併不能用於産生1000的冪(KB,MB,等等), 因爲併沒有計算冪的運算符.
**练习3.13:** 编写KB,MB的常量声明, 然后扩展到YB. **練習3.13:** 編寫KB,MB的常量聲明, 然後擴展到YB.

View File

@ -1,15 +1,15 @@
### 3.6.2. 無類型常量 ### 3.6.2. 無類型常量
Go语言的常量有点不寻常. 虽然一个常量可以有任意有一个确定的基础类型, 例如 int 或 float64, 或者是类似 time.Duration 这样命名的基础类型, 但是许多常量并没有一个明确的基础类型. 编译期为这些没有明确的基础类型的数字常量提供比基础类型或机器更高精度的算术运算; 你可以认为至少有256bit的运算精度. 这里有六种未明确类型的常量类型, 分别是 无类型的布尔型, 无类型的整数, 无类型的字符, 无类型的浮点数, 无类型的复数, 无类型的字符串. Go語言的常量有點不尋常. 雖然一個常量可以有任意有一個確定的基礎類型, 例如 int 或 float64, 或者是類似 time.Duration 這樣命名的基礎類型, 但是許多常量併沒有一個明確的基礎類型. 編譯期爲這些沒有明確的基礎類型的數字常量提供比基礎類型或機器更高精度的算術運算; 你可以認爲至少有256bit的運算精度. 這里有六種未明確類型的常量類型, 分别是 無類型的布爾型, 無類型的整數, 無類型的字符, 無類型的浮點數, 無類型的複數, 無類型的字符串.
过延迟明确具体类型, 无类型的常量不仅可以提供更高的精度, 而且可以直接用于更多的表达式而不需要类型转换. 例如 例子中的 ZiB 和 YiB 的值已经超出任何Go中整数类型能表达的范围, 但是它们依然是合法的常量, 而且可以像下面表达式这样使用: 過延遲明確具體類型, 無類型的常量不僅可以提供更高的精度, 而且可以直接用於更多的表達式而不需要類型轉換. 例如 例子中的 ZiB 和 YiB 的值已經超齣任何Go中整數類型能表達的范圍, 但是它們依然是合法的常量, 而且可以像下面表達式這樣使用:
```Go ```Go
fmt.Println(YiB/ZiB) // "1024" fmt.Println(YiB/ZiB) // "1024"
``` ```
另一个例子, math.Pi 无类型的浮点数常量, 可以直接用于任意需要浮点数或复数的地方: 另一個例子, math.Pi 無類型的浮點數常量, 可以直接用於任意需要浮點數或複數的地方:
```Go ```Go
var x float32 = math.Pi var x float32 = math.Pi
@ -17,7 +17,7 @@ var y float64 = math.Pi
var z complex128 = math.Pi var z complex128 = math.Pi
``` ```
如果 math.Pi 被确定为特定类型, 比如 float64, 那么结果精度可能会不一样, 同时对于需要float32或complex128类型值的地方会需要一个明确的类型转换: 如果 math.Pi 被確定爲特定類型, 比如 float64, 那麽結果精度可能會不一樣, 同時對於需要float32或complex128類型值的地方會需要一個明確的類型轉換:
```Go ```Go
const Pi64 float64 = math.Pi const Pi64 float64 = math.Pi
@ -27,9 +27,9 @@ var y float64 = Pi64
var z complex128 = complex128(Pi64) var z complex128 = complex128(Pi64)
``` ```
对于常量面值, 不同的写法对应不同的类型. 例如 0, 0.0, 0i, 和 '\u0000' 虽然有着相同的常量值, 但是它们分别对应无类型的整数,无类型的浮点数,无类型的复数,和无类型的字符等不同的常量类型. 同样, true 和 false 也是无类型的布尔类型, 字符串面值常量是无类型的字符串. 對於常量面值, 不同的寫法對應不同的類型. 例如 0, 0.0, 0i, 和 '\u0000' 雖然有着相同的常量值, 但是它們分别對應無類型的整數,無類型的浮點數,無類型的複數,和無類型的字符等不同的常量類型. 同樣, true 和 false 也是無類型的布爾類型, 字符串面值常量是無類型的字符串.
前面说过除法运算符 / 根据操作数的类型生成对应类型的结果. 因此, 不同写法的常量除法表达式可能对应不同的结果: 前面説過除法運算符 / 根據操作數的類型生成對應類型的結果. 因此, 不同寫法的常量除法表達式可能對應不同的結果:
```Go ```Go
var f float64 = 212 var f float64 = 212
@ -38,7 +38,7 @@ fmt.Println(5 / 9 * (f - 32)) // "0"; 5/9 is an untyped integer, 0
fmt.Println(5.0 / 9.0 * (f - 32)) // "100"; 5.0/9.0 is an untyped float fmt.Println(5.0 / 9.0 * (f - 32)) // "100"; 5.0/9.0 is an untyped float
``` ```
只有常量可以是无类型的. 当一个无类型的常量被赋值给一个变量, 就像上面的第一行语句, 或者是像其余三个语句中右边表达式中含有明确类型的值, 无类型的常量将会被隐式转换为对应的类型, 如果可能的话. 隻有常量可以是無類型的. 當一個無類型的常量被賦值給一個變量, 就像上面的第一行語句, 或者是像其餘三個語句中右邊表達式中含有明確類型的值, 無類型的常量將會被隱式轉換爲對應的類型, 如果可能的話.
```Go ```Go
var f float64 = 3 + 0i // untyped complex -> float64 var f float64 = 3 + 0i // untyped complex -> float64
@ -47,7 +47,7 @@ f = 1e123 // untyped floating-point -> float64
f = 'a' // untyped rune -> float64 f = 'a' // untyped rune -> float64
``` ```
上面的语句相当于: 上面的語句相當於:
```Go ```Go
var f float64 = float64(3 + 0i) var f float64 = float64(3 + 0i)
@ -56,7 +56,7 @@ f = float64(1e123)
f = float64('a') f = float64('a')
``` ```
无论是隐式或显式, 将一种类型转换为另一种类型要求目标可以表示原始值. 对于浮点数和复数, 可能会有舍入处理: 無論是隱式或顯式, 將一種類型轉換爲另一種類型要求目標可以表示原始值. 對於浮點數和複數, 可能會有舍入處理:
```Go ```Go
const ( const (
@ -70,7 +70,7 @@ const (
) )
``` ```
对于一个没有显式类型的变量声明(包括短变量声明语法), 无类型的常量会被隐式转为默认的变量类型, 就像下面的例子: 對於一個沒有顯式類型的變量聲明(包括短變量聲明語法), 無類型的常量會被隱式轉爲默認的變量類型, 就像下面的例子:
```Go ```Go
i := 0 // untyped integer; implicit int(0) i := 0 // untyped integer; implicit int(0)
@ -79,16 +79,16 @@ f := 0.0 // untyped floating-point; implicit float64(0.0)
c := 0i // untyped complex; implicit complex128(0i) c := 0i // untyped complex; implicit complex128(0i)
``` ```
注意默认类型是规则的: 无类型的整数常量默认转换为int, 对应不确定的尺寸, 但是浮点数好复数常量则默认转换为float64和complex128. Go语言本身并没有不确定的尺寸的浮点数和复数类型, 因为如何不知道浮点数类型的话很难写出正确的数值算法. 註意默認類型是規則的: 無類型的整數常量默認轉換爲int, 對應不確定的尺寸, 但是浮點數好複數常量則默認轉換爲float64和complex128. Go語言本身併沒有不確定的尺寸的浮點數和複數類型, 因爲如何不知道浮點數類型的話很難寫齣正確的數值算法.
如果要给变量一个不同的类型, 我们必须显式地将无类型的常量转化为所需的类型, 或给声明的变量指定类型, 像下面例子这样: 如果要給變量一個不同的類型, 我們必鬚顯式地將無類型的常量轉化爲所需的類型, 或給聲明的變量指定類型, 像下面例子這樣:
```Go ```Go
var i = int8(0) var i = int8(0)
var i int8 = 0 var i int8 = 0
``` ```
当尝试将这些无类型的常量转为一个接口值时(见第7章), 这些默认类型将显得尤为重要, 因为要靠它们明确接口对应的动态类型. 當嚐試將這些無類型的常量轉爲一個接口值時(見第7章), 這些默認類型將顯得尤爲重要, 因爲要靠它們明確接口對應的動態類型.
```Go ```Go
fmt.Printf("%T\n", 0) // "int" fmt.Printf("%T\n", 0) // "int"
@ -97,7 +97,7 @@ fmt.Printf("%T\n", 0i) // "complex128"
fmt.Printf("%T\n", '\000') // "int32" (rune) fmt.Printf("%T\n", '\000') // "int32" (rune)
``` ```
现在我们已经讲述了Go语言中全部的基础数据类型. 下一步将演示如何用基础数据类型组合成数组或结构体等复杂数据类型, 然后构建用于解决实际编程问题的数据结构, 这将是第四章的讨论主题. 現在我們已經講述了Go語言中全部的基礎數據類型. 下一步將演示如何用基礎數據類型組合成數組或結構體等複雜數據類型, 然後構建用於解決實際編程問題的數據結構, 這將是第四章的討論主題.

View File

@ -1,15 +1,15 @@
## 3.6. 常量 ## 3.6. 常量
常量表达式的值在编译期计算, 而不是在运行期. 每种常量的潜在类型都是基础类型: boolean, string, 或数字. 常量表達式的值在編譯期計算, 而不是在運行期. 每種常量的潛在類型都是基礎類型: boolean, string, 或數字.
个常量的声明语句定义了常量的名字, 和变量的声明语法类似, 常量的值不可修改, 这样可以防止在运行期被意外或恶意的修改. 例如, 常量比变量更适合用于表达像 π 之类的数学常数, 因为它们的值不会变化: 個常量的聲明語句定義了常量的名字, 和變量的聲明語法類似, 常量的值不可脩改, 這樣可以防止在運行期被意外或惡意的脩改. 例如, 常量比變量更適合用於表達像 π 之類的數學常數, 因爲它們的值不會變化:
```Go ```Go
const pi = 3.14159 // approximately; math.Pi is a better approximation const pi = 3.14159 // approximately; math.Pi is a better approximation
``` ```
变量声明一样, 可以批量声明多个常量; 这比较适合声明一组相关的常量: 變量聲明一樣, 可以批量聲明多個常量; 這比較適合聲明一組相關的常量:
```Go ```Go
const ( const (
@ -18,11 +18,11 @@ const (
) )
``` ```
许多常量的运算可以在编译期完成, 这样可以减少运行时的工作, 也方便其他编译优化. 当操作数是常量时, 一些运行时的错误可以在编译时发现, 例如整数除零, 字符串索引越界, 任何导致无效浮点数的操作等. 許多常量的運算可以在編譯期完成, 這樣可以減少運行時的工作, 也方便其他編譯優化. 當操作數是常量時, 一些運行時的錯誤可以在編譯時發現, 例如整數除零, 字符串索引越界, 任何導致無效浮點數的操作等.
常量间的所有算术运算, 逻辑运算和比较运算的结果也是常量, 对常量的类型转换操作或以下函数调用都是返回常量结果: len, cap, real, imag, complex, 和 unsafe.Sizeof(§13.1). 常量間的所有算術運算, 邏輯運算和比較運算的結果也是常量, 對常量的類型轉換操作或以下函數調用都是返迴常量結果: len, cap, real, imag, complex, 和 unsafe.Sizeof(§13.1).
为它们的值是在编译期就确定的, 因此常量可以是构成类型的一部分, 例如用于指定数组类型的长度: 爲它們的值是在編譯期就確定的, 因此常量可以是構成類型的一部分, 例如用於指定數組類型的長度:
```Go ```Go
const IPv4Len = 4 const IPv4Len = 4
@ -34,7 +34,7 @@ func parseIPv4(s string) IP {
} }
``` ```
个常量的声明也可以包含一个类型和一个值, 但是如果没有显式指明类型, 那么将从右边的表达式推断类型. 在下面的代码中, time.Duration 是一个命名类型, 底层类型是 int64, time.Minute 是对应类型的常量. 下面声明的两个常量都是 time.Duration 类型, 可以通过 %T 参数打印类型信息: 個常量的聲明也可以包含一個類型和一個值, 但是如果沒有顯式指明類型, 那麽將從右邊的表達式推斷類型. 在下面的代碼中, time.Duration 是一個命名類型, 底層類型是 int64, time.Minute 是對應類型的常量. 下面聲明的兩個常量都是 time.Duration 類型, 可以通過 %T 參數打印類型信息:
```Go ```Go
const noDelay time.Duration = 0 const noDelay time.Duration = 0
@ -44,7 +44,7 @@ fmt.Printf("%T %[1]v\n", timeout) // "time.Duration 5m0s
fmt.Printf("%T %[1]v\n", time.Minute) // "time.Duration 1m0s" fmt.Printf("%T %[1]v\n", time.Minute) // "time.Duration 1m0s"
``` ```
如果是批量声明的常量, 除了第一个外其他常量的右边的表发生可以省略, 如果省略则表示使用前面的表达式, 对应的常量类型也一样. 例如: 如果是批量聲明的常量, 除了第一個外其他常量的右邊的表發生可以省略, 如果省略則表示使用前面的表達式, 對應的常量類型也一樣. 例如:
```Go ```Go
const ( const (
@ -57,7 +57,7 @@ const (
fmt.Println(a, b, c, d) // "1 1 2 2" fmt.Println(a, b, c, d) // "1 1 2 2"
``` ```
如果只是简单地复制右边的常量表达式, 并没有太实用的价值. 但是它可以带来其他的特性, 那就是 iota 常量生成器. 如果隻是簡單地複製右邊的常量表達式, 併沒有太實用的價值. 但是它可以帶來其他的特性, 那就是 iota 常量生成器.
{% include "./ch3-06-1.md" %} {% include "./ch3-06-1.md" %}