標籤:冒號 map 浮點數 -- 過程 ber body 先進先出 樣本
Go語言-數群組類型
一個數組(Array)就是一個可以容納若干類型相同的元素的容器。這個容器的大小(即數組的長度)是固定的,且是體現在數組的類型字面量之中的。比如,我們聲明了一個數群組類型:
type MyNumbers [3]int
註:型別宣告語句由關鍵字type
、類型名稱和類型字面量組成。
所謂類型字面量,就是用於表示某個類型的字面表示(或稱標記方法)。相對的,用於表示某個類型的值的字面表示可被稱為值字面量,或簡稱為字面量。比如之前提到過的3.7E-2
就可被稱為浮點數字面量。 類型字面量[3]int
由兩部分組成。第一部分是由方括弧包裹的數組長度,即[3]
。這也意味著,一個數組的長度是該數組的類型的組成部分,是固定不變的。該類型字面量的第二個組成部分是int
。它代表了該數組可以容納的元素的類型。說到這裡,上面這條型別宣告語句實際上是為數群組類型[3]int
聲明了一個別名資料型別。這使得我們可以把MyNumbers
當做數群組類型[3]int
來使用。
我們表示這樣一個數群組類型的值的時候,應該把該類型的類型字面量寫在最左邊,然後用花括弧包裹該值包含的若干元素。各元素之間以(英文半形)逗號分隔,即:
[3]int{1, 2, 3}
現在,我們把這個數組字面量賦給一個名為numbers
的變數:
var numbers = [3]int{1, 2, 3}
註:這是一條變數聲明語句。它在聲明變數的同時為該變數賦值。
另一種便捷方法是,在其中的類型字面量中省略代表其長度的數字,像這樣:
var numbers = [...]int{1, 2, 3}
這樣就可以免去我們為填入那個數字而數出元素個數的工作了。
接下來,我們可以很方便地使用索引運算式來訪問該變數的值中的任何一個元素,例如:
numbers[0] // 會得到第一個元素numbers[1] // 會得到第二個元素numbers[2] // 會得到第三個元素
註:索引運算式由字串、數組、切片或字典類型的值(或者代表此類值的變數或常量)和由方括弧包裹的索引值組成。在這裡,索引值的有效範圍是[0, 3)。也就是說,對於數組來說,索引值既不能小於0也不能大於或等於數組值的長度。另外要注意,索引值的最小有效值總是0,而不是1。
相對的,如果我們想修改數組值中的某一個元素值,那麼可以使用指派陳述式直接達到目的。例如,我們要修改numbers
中的第二個元素的話,如此即可:
numbers[1] = 4
雖然數組的長度已經體現在了它的類型字面量,但是我們在很多時候仍然需要明確的獲得它,像這樣:
var length = len(numbers)
註:len
是Go語言的內建函數的名稱。該函數用於擷取字串、數組、切片、字典或通道類型的值的長度。我們可以在Go語言源碼檔案中直接使用它。
最後,要注意,如果我們只聲明一個數群組類型的變數而不為它賦值,那麼該變數的值將會是指定長度的、其中各元素均為元素類型的零值(或稱預設值)的數組值。例如,若有這樣一個變數:
var numbers2 [5]int
則它的值會是
[5]int{0, 0, 0, 0, 0}
package main
import "fmt"
func main() {
var numbers2 [5]int
numbers2[0] = 2
numbers2[3] = numbers2[0] - 3
numbers2[1] = numbers2[2] + 5
numbers2[4] = len(numbers2)
sum := 11
// “==”用於兩個值的相等性判斷
fmt.Printf("%v\n", (sum == numbers2[0]+numbers2[1]+numbers2[2]+numbers2[3]+numbers2[4]))
}
Go語言-切片類型
切片(Slice)與數組一樣,也是可以容納若干類型相同的元素的容器。與數組不同的是,無法通過切片類型來確定其值的長度。每個切片值都會將數組作為其底層資料結構。我們也把這樣的數組稱為切片的底層數組。
表示切片類型的字面量如:
[]int
或
[]string
可以看到,它們與數組的類型字面量的唯一不同是不包含代表其長度的資訊。因此,不同長度的切片值是有可能屬於同一個類型的。相對的,不同長度的數組值必定屬於不同類型。對一個切片類型的聲明可以這樣:
type MySlice []int
這時,類型MySlice
即為切片類型[]int
的一個別名資料型別。除此之外,對切片值的表示也與數組值也極其相似,如:
[]int{1, 2, 3}
這樣的字面量與數組(值)的字面量的區別也只在於最左側的類型字面量。
我們在上一節講到的運算元組值的方法也同樣適用於切片值。不過,還有一種運算元組值的方法我們沒講到。這種操作的名稱就叫“切片”。實施切片操作的方式就是切片運算式。舉例如下:
var numbers3 = [5]int{1, 2, 3, 4, 5}var slice1 = numbers3[1:4]
請注意第二條指派陳述式中在“=”右邊那個部分。切片運算式一般由字串、數組或切片的值以及由方括弧包裹且由英文冒號“:”分隔的兩個正整數組成。這兩個正整數分別表示元素下界索引和元素上界索引。在本例中,切片運算式numbers3[1:4]
的求值結果為[]int{2, 3, 4}
。可見,切片運算式的求值結果相當於以元素下界索引和元素上界索引作為依據從被操作對象上“切下”而形成的新值。注意,被“切下”的部分不包含元素上界索引指向的元素。另外,切片運算式的求值結果會是切片類型的,且其元素類型與被“切片”的值的元素類型一致。實際上,slice1
這個切片值的底層數組正是numbers3
的值。
實際上,我們也可以在一個切片值上實施切片操作。操作的方式與上述無異。請看下面這個例子:
var slice2 = slice1[1:3]
據此,slice2
的值為[]int{3, 4}
。注意,作為切片運算式求值結果的切片值的長度總是為元素上界索引與元素下界索引的差值。
除了長度,切片值以及數組值還有另外一個屬性——容量。數組值的容量總是等於其長度。而切片值的容量則往往與其長度不同。請看。
,一個切片值的容量即為它的第一個元素值在其底層數組中的索引值與該數組長度的差值的絕對值。為了擷取數組、切片或通道類型的值的容量,我們可以使用內建函數cap
,如:
var capacity2 int = cap(slice2)
最後,要注意,切片類型屬於參考型別。它的零值即為nil
,即空值。如果我們只聲明一個切片類型的變數而不為它賦值,那麼該變數的值將會是nil
。例如,若有這樣一個變數:
var slice3 []int
則它的值會是
nil
package main
import "fmt"
func main() {
var numbers3 = [5]int{1, 2, 3, 4, 5}
slice3 := numbers3[2 : len(numbers3)]
length := 3
capacity := 3
fmt.Printf("%v, %v\n", (length == len(slice3)), (capacity == cap(slice3)))
}
Go語言-字典類型
Go語言的字典(Map)類型其實是雜湊表(Hash Table)的一個實現。字典用於儲存鍵-元素對(更通俗的說法是鍵-值對)的無序集合。注意,同一個字典中的每個鍵都是唯一的。如果我們在向字典中放入一個索引值對的時候其中已經有相同的鍵的話,那麼與此鍵關聯的那個值會被新值替換。
字典類型的字面量如下:
map[K]T
其中,“K”意為鍵的類型,而“T”則代表元素(或稱值)的類型。如果我們要描述一個鍵類型為int
、實值型別為string
的字典類型的話,應該這樣寫:
map[int]string
請注意,字典的鍵類型必須是可比較的,否則會引起錯誤。也就是說,它不能是切片、字典或函數類型。
字典值的字面量標記法實際上與數組和切片的字面量標記法很相似。首先,最左邊仍然是類型字面量,右邊緊挨著由花括弧包裹且有英文逗號分隔的索引值對。每個索引值對的鍵和值之間由英文冒號分隔。以字典類型map[int]string
為例,它的值的字面量可以是這樣的:
map[int]string{1: "a", 2: "b", 3: "c"}
我們可以把這個值賦給一個變數:
mm := map[int]string{1: "a", 2: "b", 3: "c"}
然後運用索引運算式取出字典中的值,就像這樣:
b := mm[2]
注意,在這裡,我們放入方括弧中的不再是索引值(實際上,字典中的索引值對也沒有索引),而是與我們要取出的值對應的那個鍵。在上例中變數b
的值必是字串"b"
。當然,也可以利用索引運算式來賦值,比如這樣:
mm[2] = b + "2"
這使得字典mm
中與鍵2
對應的值變為了"b2"
。現在我們再來向mm
添加一個索引值對:
mm[4] = ""
之後,在從中取出與`4
`和`5
`對應的值:
d := mm[4]e := mm[5]
此時,變數d
和e
的值都會是多少呢?答案是都為""
,即Null 字元串。對於變數d
來說,由於在字典mm
中與4
對應的值就是""
,所以索引運算式mm[4]
的求值結果必為""
。這理所應當。但是mm[5]
的求值結果為什麼也是Null 字元串呢?原因是,在Go語言中有這樣一項規定,即:對於字典值來說,如果其中不存在索引運算式欲取出的索引值對,那麼就以它的實值型別的空值(或稱預設值)作為該索引運算式的求值結果。由於字串類型的空值為""
,所以mm[5]
的求值結果即為""
。
在不知道mm
的確切值的情況下,我們無法得知mm[5]
的求值結果意味著什嗎?它意味著5
對應的值就是一個Null 字元串?還是說mm
中根本就沒有鍵為5
的索引值對?這無所判別。為瞭解決這個問題,Go語言為我們提供了另外一個寫法,即:
e, ok := mm[5]
針對字典的索引運算式可以有兩個求值結果。第二個求值結果是bool
類型的。它用於表明字典值中是否存在指定的索引值對。在上例中,變數ok
必為false
。因為mm
中不存在以5
為鍵的索引值對。
從字典中刪除索引值對的方法非常簡單,僅僅是調用內建函數delete
而已,就像這樣:
delete(mm, 4)
無論mm
中是否存在以4
為鍵的索引值對,delete
都會“無聲”地執行完畢。我們用“有則刪除,無則不做”可以很好地概括它的行為。
最後,與切片類型相同,字典類型屬於參考型別。它的零值即為nil
。
package main
import "fmt"
func main() {
mm2 := map[string]int{"golang": 42, "java": 1, "python": 8}
mm2["python"] = 0
mm2["scala"] = 25
mm2["erlang"] =50
//delete(mm2, python)
fmt.Printf("%d, %d, %d \n", mm2["scala"], mm2["erlang"], mm2["python"])
}
Go語言-通道類型
通道(Channel)是Go語言中一種非常獨特的資料結構。它可用於在不同Goroutine之間傳遞類型化的資料,並且是並發安全的。相比之下,我們之前介紹的那些資料類型都不是並發安全的。這一點需要特別注意。
Goroutine(也稱為Go程式)可以被看做是承載可被並發執行的代碼塊的載體。它們由Go語言的運行時系統調度,並依託作業系統線程(又稱核心線程)來並發地執行其中的代碼塊。至於怎樣編寫這樣的代碼塊以及怎樣驅動這樣的代碼塊執行,我們先按下不表。
通道類型的表示方法很簡單,僅由兩部分組成,如下:
chan T
在這個類型字面量中,左邊是代表通道類型的關鍵字chan
,而右邊則是一個可變的部分,即代表該通道類型允許傳遞的資料的類型(或稱通道的元素類型)。這兩部分之間需要以空格分隔。
與其它的資料類型不同,我們無法表示一個通道類型的值。因此,我們也無法用字面量來為通道類型的變數賦值。我們只能通過調用內建函數make
來達到目的。make
函數可接受兩個參數。第一個參數是代表了將被初始化的值的類型的字面量(比如chan int
),而第二個參數則是值的長度。例如,若我們想要初始化一個長度為5
且元素類型為int
的通道值,則需要這樣寫:
make(chan int, 5)
順便說一句,實際上make
函數也可以被用來初始化切片類型或字典類型的值。
確切地說,通道值的長度應該被稱為其緩衝的尺寸。換句話說,它代表著通道值中可以暫存的資料的個數。注意,暫存在通道值中的資料是先進先出的,即:越早被放入(或稱發送)到通道值的資料會越先被取出(或稱接收)。
下面,我們聲明一個通道類型的變數,並為其賦值:
ch1 := make(chan string, 5)
這樣一來,我們就可以使用接收操作符<-
向通道值發送資料了。當然,也可以使用它從通道值接收資料。例如,如果我們要向通道ch1
發送字串"value1"
,那麼應該這樣做:
ch1 <- "value1"
另一方面,我們若想從ch1
那裡接收字串,則要這樣:
<- ch1
這時,我們可以直接把接收到的字串賦給一個變數,如:
value := <- ch1
與針對字典值的索引運算式一樣,針對通道值的接收操作也可以有第二個結果值。請看下面的樣本:
value, ok := <- ch1
這樣做的目的同樣是為了消除與零值有關的歧義。這裡的變數ok
的值同樣是bool
類型的。它代表了通道值的狀態,true
代表通道值有效,而false
則代表通道值已無效(或稱已關閉)。更深層次的原因是,如果在接收操作進行之前或過程中通道值被關閉了,則接收操作會立即結束並返回一個該通道值的元素類型的零值。按照上面的第一種寫法,我們無從判斷接收到零值的原因是什麼。不過,有了第二個結果值之後,這種判斷就好做了。
說到關閉通道值,我們可以通過調用內建函數close
來達到目的,就像這樣:
close(ch1)
請注意,對通道值的重複關閉會引發運行時恐慌。這會使程式崩潰。所以一定要避免這種情況的發生。另外,在通道值有效前提下,針對它的發送操作會在通道值已滿(其中緩衝的資料的個數已等於它的長度)時被阻塞。而向一個已被關閉的通道值發送資料會引發運行時恐慌。另一方面,針對有效通道值的接收操作會在它已空(其中沒有緩衝任何資料)時被阻塞。除此之外,還有幾條與通道的發送和接收操作有關的規則。不過在這裡我們記住上面這三條就可以了。
最後,與切片和字典類型相同,通道類型屬於參考型別。它的零值即為nil
。
package main
import "fmt"
func main() {
ch2 := make(chan string, 1)
// 下面就是傳說中的通過啟用一個Goroutine來並發的執行代碼塊的方法。
// 關鍵字 go 後跟的就是需要被並發執行的代碼塊,它由一個匿名函數代表。
// 對於 go 關鍵字以及函數編寫方法,我們後面再做專門介紹。
// 在這裡,我們只要知道在花括弧中的就是將要被並發執行的代碼就可以了。
go func() {
ch2 <- "已達到!"
}()
var value string = "資料"
value = value + <-ch2
fmt.Println(value)
}
Go語言基礎(二)-----進階資料類型