<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                ## 5.8\. 數據 ### 5.8.1\. new()分配 Go 有兩個分配原語,new() 和 make() 。它們做法不同,也用作不同類型上。有點亂但規則簡單。我們先談談 new() 。它是個內部函數,本質上和其它語言的同類一樣:new(T)分配一塊清零的存儲空間給類型 T 的新項并返回其地址,一個類型 *T 的值。 用 Go 的術語,它返回一個類型 T 的新分配的零值。 因為 new() 返回的內存清零, 可以用來安排使用零值的物件而不需再初始化。亦即數據結構的用戶可以直接用 new() 生成一個并馬上使用。例如, bytes.Buffer 的文檔指出“零值的 Buffer 為空并可用”。同[http://code.google.com/p/ac-me/](http://code.google.com/p/ac-me/) 61理,sync.Mutex 沒有明確的架構函數或 init 方法。 而是,一個sync.Mutex 的零值定義為開鎖的互斥。 零值有用,這個特性可以順延。考慮下面的聲明。 ``` type SyncedBuffer struct { lock sync.Mutex buffer bytes.Buffer } ``` 類型 SyncBuffer 的值在分配或者聲明后立即可用。下例,p 和 v 無需多余的安排已可以正確使用了。 ``` p := new(SyncedBuffer) // type *SyncedBuffer var v SyncedBuffer // type SyncedBuffer ``` ### 5.8.2\. 構造和結構初始化 有時零值不夠好,有必要使用一個初始化架構函數,如下面從 os 包引出的例子。 ``` func NewFile(fd int, name string) *File { if fd < 0 { return nil } f := new(File) f.fd = fd f.name = name f.dirinfo = nil f.nepipe = 0 return f } ``` 這里有很多注模。我們可用組合字面簡化之,它是個每次求值即生成新實例的表達式。 ``` func NewFile(fd int, name string) *File { if fd < 0 { return nil } f := File{fd, name, nil, 0} return &f } ``` 注意返回局部變量的地址是完全 OK 的;變量對應的存儲空間在函數返回后仍然存在。實際上,取一個組合字面的地址使每次它求值時都生成一個新實例,因此我們可以把最后兩行合起來。 ``` return &File{fd, name, nil, 0} ``` 組合字面的域必須按順序給出并全部出現。可是,明確的用域:值對兒標記元素,初始化可用任意順序,未出現的對應著零值。所以我們可以講 ``` return &File{fd: fd, name: name} ``` 特別的,如果一個組合字面一個域也沒有,它生成此類型的零值。表達式 new(File) 和 &File{} 是等價的。 組合字面也可以生成數組、切片和映射,其域為合適的下標或映射鍵。下例中,無論 Enone Eio 和 Einval 是什么值都可以,只要它們是不同的。 ``` a := [...]string {Enone: "no error", Eio: "Eio", Einval: "invalid argument"} s := []string {Enone: "no error", Eio: "Eio", Einval: "invalid argument"} m := map[int]string{Enone: "no error", Eio: "Eio", Einval: "invalid argument"} ``` ### 5.8.3\. make()分配 回到分配。內部函數 make(T, args) 的服務目的和 new(T) 不同。它只生成切片,映射和信道,并返回一個初始化的(不是零)的,type T的,不是 *T 的值。這種區分的原因是,這三種類型,揭開蓋子,底下引用的數據結構必須在用前初始化。比如切片是一個三項的描述符,包含數據指針(數組內),長度,和容量;在這些項初始化前,切片為 nil 。對于切片、映射和信道,make 初始化內部數據結構,并準備要用的值。例如, ``` make([]int, 10, 100) ``` 分配一個 100 個整數的數組,然后生成一個切片結構,長度為10,容量是100的指向此數組的首10項。(生成切片時,容量可以不寫;詳見切片一節。)對應的,new([]int) 返回一個新分配的,清零的切片結構,亦即,一個 nil 切片值的指針。 下面的例子展示了 new() 和 make() 的不同。 ``` var p *[]int = new([]int) // allocates slice structure; *p == nil; rarely useful var v []int = make([]int, 100) // v now refers to a new array of 100 ints // Unnecessarily complex: var p *[]int = new([]int) *p = make([]int, 100, 100) // Idiomatic: v := make([]int, 100) ``` 記住 make() 只用于映射、切片和信道,不返回指針。要明確的得到指針用 new() 分配。 ### 5.8.4\. 數組 數組用于安排詳細的內存布局,還有助于避免分配,但其主要作為切片的構件,即下節的主題。這里先講幾句打個底兒。 Go 和 C 的數組的主要不同在于: * 數組為值。數組賦值給另一數組拷貝其全部元素。 * 特別是,如果你傳遞數組給一個函數,它受到此數組的拷貝,不是指針。 * 數組的尺寸是其類型的一部分。[10]int 和 [20]int 是完全不同的類型。 值的屬性可用但昂貴;如你所需的是類似 C 的行為和效率,你可以傳遞一個指針給數組。 ``` func Sum(a *[3]float) (sum float) { for _, v := range *a { sum += v } return } array := [...]float{7.0, 8.5, 9.1} x := Sum(&array) // Note the explicit address-of operator ``` 即便如此也不是地道的 Go 風格。切片才是。 ### 5.8.5\. Slices 切片 切片包裝數組,給數據系列一個通用、強力、方便的界面。除了像變換矩陣那種要求明確尺寸的情況,絕大部分的數組編程在 Go 里使用切片、而不是簡單的數組。 切片是引用類型,即如果賦值切片給另一個切片,它們都指向同一底層數組。例如,如果某函數取切片參量,對其元素的改動會顯現在調用者中,類似于傳遞一個底層數組的指針。因此 Read 函數可以接受切片參量,而不需指針和計數;切片的長度決定了可讀數據的上限。這里是 os 包的 File 型的 Read 方法的簽名: ``` func (file *File) Read(buf []byte) (n int, err os.Error) ``` 此方法返回讀入字節數和可能的錯誤值。要讀入一個大的緩沖 b 的首32字節, 切片(動詞)緩沖。 ``` n, err := f.Read(buf[0:32]) ``` 這種切片常用且高效。實際上,先不管效率,此片段也可讀緩沖的首32字節。 ``` var n int var err os.Error for i := 0; i < 32; i++ { nbytes, e := f.Read(buf[i:i+1]) // Read one byte. if nbytes == 0 || e != nil { err = e break } n += nbytes } ``` 只要還在底層數組的限制內,切片的長度可以改變,只需賦值自己。切片的容量,可用內部函數 cap 取得,給出此切片可用的最大長度。下面的函數給切片添值。如果數據超過容量,切片重新分配,返回結果切片。此函數利用了 len 和 cap 對 nil 切片合法、返回0的事實。 ``` func Append(slice, data[]byte) []byte { l := len(slice) if l + len(data) > cap(slice) { // reallocate // Allocate double what's needed, for future growth. newSlice := make([]byte, (l+len(data))*2) // Copy data (could use bytes.Copy()). for i, c := range slice { newSlice[i] = c } slice = newSlice } slice = slice[0:l+len(data)] for i, c := range data { slice[l+i] = c } return slice } ``` 我們必須返回切片,因為盡管 Append 可以改變 slice 的元素, 切片自身(持有指針、長度和容量的運行態數據結構)是值傳遞的。添加切片的主意很有用,因此由內置函數 append 實現。要理解此函數的設計,我們需要多一些信息,所以稍后再講。 ### 5.8.6\. Maps 字典 映射提供了一個方便強力的內部數據結構,用來聯合不同的類型。鍵可以是任何定義了相等操作符的類型,如整型,浮點型,字串,指針,界面(只要其動態類型支持相等)。結構,數組和切片不可用作映射鍵,因為其類型未定義相等。類似切片,映射是引用類型。如果你傳遞映射給某函數,對映射的內容的改動顯現給調用者。 映射的生成使用平常的冒號隔開的鍵值伴組合字面句法,所以很容易初始化時建好它們。 ``` var timeZone = map[string] int { "UTC": 0*60*60, "EST": -5*60*60, "CST": -6*60*60, "MST": -7*60*60, "PST": -8*60*60, } ``` 賦值和獲取映射值語法上就像數組,只是下標不需是整型。 ``` offset := timeZone["EST"] ``` 試圖獲取不存在的鍵的映射值返回對應條目類型的零值。例如,如果映射包含整型數,查找不存在的鍵返回0。 有時你需區分不在鍵和零值。 是沒有 “UTC” 的條目,還是因為其值為零?你可以用多值賦值的形式加以區分。 ``` var seconds int var ok bool seconds, ok = timeZone[tz] ``` 道理很明顯,此習語稱為“逗號ok”。此例中,如果 tz 存在,seconds 相應賦值,ok為真;否則,seconds 為0,ok為假。下面的函數加上了中意的出錯報告: ``` func offset(tz string) int { if seconds, ok := timeZone[tz]; ok { return seconds } log.Stderr("unknown time zone", tz) return 0 } ``` 要檢查映射的存在,又不想管實際值,你可以用空白標識,即下劃線( _ )。空白標識可以賦值或聲明為任意類型的任意值,會被無害的丟棄。如只要測試映射是否存在, 在平常變量的地方使用空白標識即可。 ``` _, present := timeZone[tz] ``` 要刪除映射條目,翻轉多值賦值,在右邊多放個布爾;如果布爾為假,條目被刪。即便鍵已經不再了,這樣做也是安全的。 ``` timeZone["PDT"] = 0, false // Now on Standard Time ``` ### 5.8.7\. 打印 Go 的排版打印風格類似 C 的 printf 族但更豐富更通用。這些函數活在 fmt 包里,叫大寫的名字:fmt.Printf,fmt.Fprintf, fmt.Sprintf 等等。字串函數(Sprintf 等)返回字串,而不是填充給定的緩沖。 你不需給出排版字串。對應每個 Printf,Fprintf 和 Sprintf 都有另一對函數。例如 Print 和 Println。 它們不需排版字串,而是用每個參量默認的格式。Println 版本還會在參量間加入空格和輸出新行,而 Print 版本只當操作數的兩邊都不是字串時才添加空格。下例每行的輸出都是一樣的: ``` fmt.Printf("Hello %d\n", 23) fmt.Fprint(os.Stdout, "Hello ", 23, "\n") fmt.Println(fmt.Sprint("Hello ", 23)) ``` 如《輔導》里所講,fmt.Fprint 和伙伴們的第一個參量可以是任何實現 io.Writer 界面的物件。變量 os.Stdout 和 os.Stderr 是熟悉的實例。 從此事情開始偏離 C 了。首先,數字格式如 %d 沒有正負和尺寸的標記;打印例程使用參量的類型決定這些屬性。 ``` var x uint64 = 1<<64 - 1 fmt.Printf("%d %x; %d %x\n", x, x, int64(x), int64(x)) ``` 打印出: ``` 18446744073709551615 ffffffffffffffff; -1 -1 ``` 如果你只需默認的轉換,例如整數用十進制,你可以用全拿格式 %v(代表 value);結果和Print 與 Println 打印的完全一樣。再有,此格式可打印任意值,包括數組,結構和映射。這里是上節定義的時區映射的打印語句。 ``` fmt.Printf("%v\n", timeZone) // or just fmt.Println(timeZone) ``` 打印出: ``` map[CST:-21600 PST:-28800 EST:-18000 UTC:0 MST:-25200] ``` 當然,映射的鍵會以任意順序輸出。打印結構時,改進的格式 %+v 用結構的域名注釋,對任意值格式 %#v 打印出完整的 Go 句法。 ``` type T struct { a int b float c string } t := &T{ 7, -2.35, "abc\tdef" } fmt.Printf("%v\n", t) fmt.Printf("%+v\n", t) fmt.Printf("%#v\n", t) fmt.Printf("%#v\n", timeZone) ``` 打印出: ``` &{7 -2.35 abc def} &{a:7 b:-2.35 c:abc def} &main.T{a:7, b:-2.35, c:"abc\tdef"} map[string] int{"CST":-21600, "PST":-28800, "EST":-18000, "UTC":0, "MST":-25200} ``` (注意和號&)。引號括起的字串也可以 %q 用在 string 或 []byte 類型的值上,對應的格式 %#q 如果可能則使用反引號。還有,%x 可用于字串、字節數組和整型,得到長的十六進制串,有空格的格式(% x)會在字節間加空格。 另一好用的格式是 %T,打印某值的類型。 ``` fmt.Printf("%T\n", timeZone) ``` 打印出: ``` map[string] int ``` 如果你要控制某定制類型的默認格式, 只需在其類型上定義方法String() string。對我們簡單的類型 T,可以是: ``` func (t *T) String() string { return fmt.Sprintf("%d/%g/%q", t.a, t.b, t.c) } fmt.Printf("%v\n", t) ``` 來打印 ``` 7/-2.35/"abc\tdef" ``` 我們的String() 方法可以調用 Sprint,因為打印例程是完全可以重入可以遞歸的。我們可以更進一步,把一個打印例程的參量直接傳遞給另一打印例程。 Printf 的簽名的首參量使用類型 ...interface{},來指定任意數量任意類型的參量可以出現在格式字串的后面。 ``` func Printf(format string, v ...) (n int, errno os.Error) { ``` Printf 函數中,v 像是一個 []interface{} 類的變量。但如果把它傳遞給另一個多維函數,它就像一列普通的參量。這里是我們上面用過的log.Println 的實現。它把自己的參量直接傳遞給 fmt.Sprintln 來實際打印。 ``` // Stderr is a helper function for easy logging to stderr. It is analogous to Fprint(os.Stderr). func Stderr(v ...) { stderr.Output(2, fmt.Sprintln(v)) // Output takes parameters (int, string) } ``` 我們在 Sprintln 的調用的 v 后寫 ... 告訴編譯器把 v 作為一列參量;否則它只是傳遞一個單一的切片參量。 還有很多打印的內容我們還沒講,細節可參考 godoc 的 fmt 包的文檔。 順便提一句, ... 參量可以是任意給定的類型,例如,...int 在 min 函數里可以選一列整數的最小值。 ``` func Min(a ...int) int { min := int(^uint(0) >> 1) // largest int for _, i := range a { if i < min { min = i } } return min } ``` ### 5.8.8\. Append 現在我們解釋 append 的設計。append 的簽名和上面我們定制的Append 函數不同。大體上是: ``` func append(slice []T, elements...T) []T ``` T 替代的是任意類型。 實際中你不能寫 Go 的函數由調用者決定 T 的類型,所以 append 內置:它需要編譯器的支持。 append 所做的是在切片尾添加元素并返回結果。結果需要返回因為,正如我們手寫的 Append,底層的數組可能更改。下面簡單的例子: ``` x := []int{1,2,3} x = append(x, 4, 5, 6) fmt.Println(x) ``` 打印 [1 2 3 4 5](https://golang-china.googlecode.com/svn/trunk/Chinese/golang.org/6)。所以 append 有點像 Printf 收集任意數量的參量。 但如何像我們 Append 一樣給切片添加切片呢?容易:使用 ... 在調用的地方,正如我們上面我們調用 Output。 下例產生如上同樣的輸出: ``` x := []int{1,2,3} y := []int{4,5,6} x = append(x, y...) fmt.Println(x) ``` 沒有 ... 將不能編譯,因為類型錯誤; y 不是 int 類型。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看