透過示例學習 Go 語言 2023(八)
Go (Golang) 中的 Cookies
來源:
golangbyexample.com/cookies-golang/
目錄
** 什麼是 cookie
-
在 golang 中設定 cookie
-
在發起請求時設定 cookie。
-
在響應傳入請求時設定 cookie
-
-
在 golang 中讀取 cookie
什麼是 cookie
Cookies 是一種在客戶端儲存資訊的方式。客戶端可以是瀏覽器、移動應用程式或任何發起 HTTP 請求的事物。Cookies 基本上是儲存在瀏覽器快取記憶體中的一些檔案。當您瀏覽任何支援 cookies 的網站時,會在 cookie 中記錄與您活動相關的某種資訊。這些資訊可以是任何內容。簡而言之,cookies 儲存使用者活動的歷史資訊。這些資訊儲存在客戶端的計算機上。由於 cookie 儲存在檔案中,因此即使使用者關閉瀏覽器視窗或重啟計算機,這些資訊也不會丟失。cookie 還可以儲存登入資訊。實際上,諸如令牌等登入資訊通常僅儲存在 cookies 中。cookies 是按域儲存的。屬於特定域的本地儲存 cookies 在每個請求中傳送到該域。它們在每個請求中作為頭的一部分傳送。因此,cookie 本質上只是一種頭。
您可以在這裡閱讀關於 HTTP cookie 的一般資訊 – en.wikipedia.org/wiki/HTTP_cookie
Cookies 可以傳送
-
作為 HTTP 客戶端的 Cookie 頭
-
作為 HTTP 伺服器響應中的 Set-cookie 頭
golang 中的 cookie 如下所示
golang.org/src/net/http/cookie.go
type Cookie struct {
Name string
Value string
Path string // optional
Domain string // optional
Expires time.Time // optional
RawExpires string // for reading cookies only
// MaxAge=0 means no 'Max-Age' attribute specified.
// MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'
// MaxAge>0 means Max-Age attribute present and given in seconds
MaxAge int
Secure bool
HttpOnly bool
SameSite SameSite
Raw string
Unparsed []string // Raw text of unparsed attribute-value pairs
}
詳細資訊請參見 tools.ietf.org/html/rfc6265
中上述 cookie 的每個欄位。
讓我們詳細瞭解與 cookies 相關的兩個方面
-
在 golang 中設定 cookie
-
讀取 cookie
在 golang 中設定 cookie
我們已經提到 cookie 只是一個頭。因此,要設定特定的 cookie,我們只需設定該頭。
有兩種情況
-
在發起請求時設定 cookie。
-
在響應傳入請求時設定 cookie
讓我們詳細瞭解每一個
在發起請求時設定 cookie。
這是 golang 作為 HTTP 客戶端的情況。AddCookie 方法可用於新增 cookie。如果我們對兩個不同的名稱和值呼叫此方法,那麼這兩個名稱和值都將新增到結果 cookie 中
package main
import (
"fmt"
"log"
"net/http"
"net/http/cookiejar"
)
var client http.Client
func init() {
jar, err := cookiejar.New(nil)
if err != nil {
log.Fatalf("Got error while creating cookie jar %s", err.Error())
}
client = http.Client{
Jar: jar,
}
}
func main() {
cookie := &http.Cookie{
Name: "token",
Value: "some_token",
MaxAge: 300,
}
cookie2 := &http.Cookie{
Name: "clicked",
Value: "true",
MaxAge: 300,
}
req, err := http.NewRequest("GET", "http://google.com", nil)
if err != nil {
log.Fatalf("Got error %s", err.Error())
}
req.AddCookie(cookie)
req.AddCookie(cookie2)
for _, c := range req.Cookies() {
fmt.Println(c)
}
resp, err := client.Do(req)
if err != nil {
log.Fatalf("Error occured. Error is: %s", err.Error())
}
defer resp.Body.Close()
fmt.Printf("StatusCode: %d\n", resp.StatusCode)
}
輸出
token=some_token
clicked=true
StatusCode: 200
在上述程式中,HTTP 客戶端新增了兩個 cookies。這兩個 cookies 將在呼叫 google.com 時傳送。
golang 中的 HTTP 客戶端還允許您指定一個 CookieJar,該 jar 管理在進行外部 HTTP 請求時儲存和傳送 cookies。顧名思義,可以把它看作一個裝有 cookies 的 jar。
HTTP 客戶端以兩種方式使用此 jar。
-
在此 Jar 中新增 cookies。您可以顯式地將 cookies 新增到此 jar。如果伺服器在響應頭中傳送 Set-Cookies 頭,cookies 也將被新增到 jar 中。所有在 Set-Cookie 頭中指定的 cookies 將被新增。
-
在進行任何外部 HTTP 請求時諮詢此 jar。它檢查此 jar 以瞭解特定域需要傳送哪些 cookies。
有關 golang 中 CookieJar 的更多資訊,可以參考此連結 golangbyexample.com/cookiejar-golang/
。
在響應傳入請求時設定 cookie。
這是 golang 作為 HTTP 伺服器的情況。http.ResponseWriter 結構提供了一個方便的方法來設定 cookie。下面是該方法的簽名。
func SetCookie(w ResponseWriter, cookie *Cookie)
此方法用於在 ResponseWriter 上設定 cookies。它向響應頭新增一個 Set-Cookie 頭。此 Set-Cookie 頭用於傳送在客戶端或瀏覽器端設定的 cookie。然後,當客戶端向伺服器發出後續呼叫時,該 cookie 將被髮送回伺服器。
以下是相應的程式。
package main
import (
"net/http"
)
func main() {
docHandler := http.HandlerFunc(docHandler)
http.Handle("/doc", docHandler)
http.ListenAndServe(":8080", nil)
}
func docHandler(w http.ResponseWriter, r *http.Request) {
cookie := &http.Cookie{
Name: "id",
Value: "abcd",
MaxAge: 300,
}
http.SetCookie(w, cookie)
w.WriteHeader(200)
w.Write([]byte("Doc Get Successful"))
return
}
使用以下命令執行上述程式。
go run main.go
伺服器將在 8080 埠啟動。
現在從瀏覽器發起 API 呼叫 localhost:8080/doc。伺服器在響應中傳送了以下 Set-Cookie。
Set-Cookie: id=abcd; Max-Age=300
這在 API 呼叫的響應頭中也可見。見下圖。
![](https://gitee.com/OpenDocCN/geekdoc-golang-zh/raw/master/docs/go-exam-2023/img/5743ff6b7299452f20052240e39f56a7.png)
有關 Set-Cookie 頭的更多詳細資訊,請參閱此 link。該連結包含了解 Set-Cookie 頭在 golang 中的所有細節。
在 golang 中讀取 cookie。
net/http Request 結構提供了一個方便的方法來讀取特定名稱的 cookie。下面是該方法的簽名。golang.org/pkg/net/http/#Request.Cookie
func (r *Request) Cookie(name string) (*Cookie, error)
要列印所有 cookies,我們可以遍歷 http.Request 結構的 Cookies 方法。我們可以為此使用 range 關鍵字。
for _, c := range r.Cookies() {
fmt.Println(c)
}
以下是相應的程式,演示 http.Request 結構的 Cookie 和 Cookies 方法。
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
docHandler := http.HandlerFunc(docHandler)
http.Handle("/doc", docHandler)
http.ListenAndServe(":8080", nil)
}
func docHandler(w http.ResponseWriter, r *http.Request) {
fmt.Println("Cookies in API Call:")
tokenCookie, err := r.Cookie("token")
if err != nil {
log.Fatalf("Error occured while reading cookie")
}
fmt.Println("\nPrinting cookie with name as token")
fmt.Println(tokenCookie)
fmt.Println("\nPrinting all cookies")
for _, c := range r.Cookies() {
fmt.Println(c)
}
fmt.Println()
w.WriteHeader(200)
w.Write([]byte("Doc Get Successful"))
return
}
執行上述程式並進行以下 curl 呼叫。
curl -X GET localhost:8080/doc --cookie "id=abcd; token=some_token"
curl 呼叫傳遞了兩個 cookie 名稱-值對。
-
id=abcd。
-
token=some_token。
它將產生以下輸出。
Cookies in API Call:
Printing cookie with name as token
token=some_token
Printing all cookies
id=abcd
token=some_token
這就是我們如何列印給定名稱 “token” 的特定 cookie。
tokenCookie, err := r.Cookie("token")
它的輸出如所示。
token=some_token
這就是我們如何列印所有 cookies。
for _, c := range r.Cookies() {
fmt.Println(c)
}
它輸出我們在 curl 呼叫中傳送的 cookie 名稱-值對。
id=abcd
token=some_token
這就是關於 golang 中的 cookie 的所有內容。希望你喜歡這個教程。請在評論中分享反饋。
另外,檢視我們的 Golang 高階教程系列 – Golang 高階教程
在 Go 語言中複製陣列或切片
來源:
golangbyexample.com/copy-an-array-or-slice-golang/
目錄
-
概述
-
複製一個陣列
-
複製一個切片
概述
在 Go 語言中,陣列是值型別,而切片是引用型別。因此,陣列或切片如何複製到另一個陣列或切片的方式是不同的。
複製一個陣列
如上所述,陣列在 Go 語言中是值型別。因此,陣列變數名不是指向第一個元素的指標,而是表示整個陣列。當
-
一個陣列變數被賦值給另一個陣列變數。
-
一個陣列變數作為引數傳遞給一個函式。
我們用一個例子來說明上述要點
package main
import "fmt"
func main() {
sample1 := [2]string{"a", "b"}
fmt.Printf("Sample1 Before: %v\n", sample1)
sample2 := sample1
sample2[1] = "c"
fmt.Printf("Sample1 After assignment: %v\n", sample1)
fmt.Printf("Sample2: %v\n", sample2)
test(sample1)
fmt.Printf("Sample1 After Test Function Call: %v\n", sample1)
}
func test(sample [2]string) {
sample[0] = "d"
fmt.Printf("Sample in Test function: %v\n", sample)
}
輸出
Sample1 Before: [a b]
Sample1 After assignment: [a b]
Sample2: [a c]
Sample in Test function: [d b]
Sample1 After Test Function Call: [a b]
在上述例子中,
-
我們將sample1賦值給sample2,然後在sample2的 0 索引處改變為不同的值。之後,當我們列印sample1時,發現它沒有改變。這是因為當我們將sample1賦值給sample2時,會建立一個副本,改變sample2不會影響sample1。
-
我們將sample1傳遞給測試函式,然後在測試函式的 0 索引處再次改變其值。之後,當我們列印sample1時,發現它沒有改變。原因相同,當sample1作為引數傳遞給測試函式時,會建立sample1的一個副本。
複製一個切片
Go 的builtin包提供了一個copy函式,可以用來複制切片。下面是這個函式的簽名。它返回複製的元素數量。
func copy(dst, src []Type) int
在使用 copy 函式時,有兩種情況需要考慮:
-
如果src的長度大於dst的長度,則複製的元素數量為dst的長度
-
如果dst的長度大於src的長度,則複製的元素數量為src的長度
基本上,複製的元素數量是(src, dst)長度的最小值。
還要注意,一旦複製完成,dst中的任何更改將不會反映在src中,反之亦然。
package main
import "fmt"
func main() {
src := []int{1, 2, 3, 4, 5}
dst := make([]int, 5)
numberOfElementsCopied := copy(dst, src)
fmt.Printf("Number Of Elements Copied: %d\n", numberOfElementsCopied)
fmt.Printf("dst: %v\n", dst)
fmt.Printf("src: %v\n", src)
//After changing numbers2
dst[0] = 10
fmt.Println("\nAfter changing dst")
fmt.Printf("dst: %v\n", dst)
fmt.Printf("src: %v\n", src)
}
輸出
Number Of Elements Copied: 5
dst: [1 2 3 4 5]
src: [1 2 3 4 5]
After changing dst
dst: [10 2 3 4 5]
src: [1 2 3 4 5]
```*
<!--yml
類別:未分類
日期:2024-10-13 06:18:40
-->
# Go 中的複製函式(Golang)
> 來源:[`golangbyexample.com/copy-function-in-golang/`](https://golangbyexample.com/copy-function-in-golang/)
Go 的 **builtin** 包提供了 **copy** 函式,可以用於複製切片。以下是此函式的簽名。它接收兩個切片 **dst** 和 **src**,並將資料從 **src** 複製到 **dst**。它返回複製的元素數量。
```go
func copy(dst, src []Type) int
根據簽名,複製函式可以用於從 src 複製不同型別的切片到 dst。在使用複製函式時,有兩種情況需要考慮:
-
如果 src 的長度大於 dst 的長度,則複製的元素數量為 dst 的長度。
-
如果 dst 的長度大於 src 的長度,則複製的元素數量為 src 的長度。
基本上,複製的元素數量為 (src, dst) 長度的最小值。
還要注意,一旦複製完成,dst 中的任何更改將不會反映在 src 中,反之亦然,除非 src 和 dst 引用同一個切片。
一個簡單的整數型別複製示例,說明上述要點。
package main
import "fmt"
func main() {
src := []int{1, 2, 3, 4, 5}
dst := make([]int, 5)
numberOfElementsCopied := copy(dst, src)
fmt.Printf("Number Of Elements Copied: %d\n", numberOfElementsCopied)
fmt.Printf("dst: %v\n", dst)
fmt.Printf("src: %v\n", src)
//After changing dst
dst[0] = 10
fmt.Println("\nAfter changing dst")
fmt.Printf("dst: %v\n", dst)
fmt.Printf("src: %v\n", src)
//Length of destination is less than length of source
dst = make([]int, 4)
numberOfElementsCopied = copy(dst, src)
fmt.Println("\nLength of dst less than src")
fmt.Printf("Number Of Elements Copied: %d\n", numberOfElementsCopied)
fmt.Printf("dst: %v\n", dst)
fmt.Printf("src: %v\n", src)
//Length of destination is greater than length of source
dst = make([]int, 6)
numberOfElementsCopied = copy(dst, src)
fmt.Println("\nLength of dst less than src")
fmt.Printf("Number Of Elements Copied: %d\n", numberOfElementsCopied)
fmt.Printf("dst: %v\n", dst)
fmt.Printf("src: %v\n", src)
}
輸出
Number Of Elements Copied: 5
dst: [1 2 3 4 5]
src: [1 2 3 4 5]
After changing dst
dst: [10 2 3 4 5]
src: [1 2 3 4 5]
Length of dst less than src
Number Of Elements Copied: 4
dst: [1 2 3 4]
src: [1 2 3 4 5]
Length of dst less than src
Number Of Elements Copied: 5
dst: [1 2 3 4 5 0]
src: [1 2 3 4 5]
字串的複製函式
在 Go 中,字串只是一系列位元組。因此,將字串複製到位元組切片是合法的。
package main
import "fmt"
func main() {
src := "abc"
dst := make([]byte, 3)
numberOfElementsCopied := copy(dst, src)
fmt.Printf("Number Of Elements Copied: %d\n", numberOfElementsCopied)
fmt.Printf("dst: %v\n", dst)
fmt.Printf("src: %v\n", src)\
}
輸出
Number Of Elements Copied: 3
dst: [97 98 99]
src: abc
在使用複製函式時,源和目標也可以重疊。因此,可以從一個切片複製到自身。以下示例中,我們將切片的最後兩個元素複製到同一順序的前兩個元素中。
package main
import "fmt"
func main() {
src := []int{1, 2, 3, 4, 5}
numberOfElementsCopied := copy(src, src[3:])
fmt.Printf("Number Of Elements Copied: %d\n", numberOfElementsCopied)
fmt.Printf("src: %v\n", src)
}
輸出
Number Of Elements Copied: 2
src: [4 5 3 4 5]
結論
這就是 Go 中的複製函式。希望你喜歡這篇文章。
在 Go(Golang)中計算字串中子字串的例項
來源:
golangbyexample.com/instances-substring-string-go/
目錄
-
概述**
-
程式碼:
概述
在 GO 中,字串採用 UTF-8 編碼。GO 的strings包提供了一個Count方法,可以用來獲取特定字串中非重疊子字串的數量。
以下是該函式的簽名
func Count(s, substr string) int
另外請注意,如果傳遞給函式的substr是一個空字串,則返回值將為 1 加上給定字串中的 Unicode 字元點數。
讓我們看一下工作程式碼
程式碼:
package main
import (
"fmt"
"strings"
)
func main() {
//Case 1 Input string contains the substring
res := strings.Count("abcdabcd", "ab")
fmt.Println(res)
//Case 1 Input string doesn't contains the substring
res = strings.Count("abcdabcd", "xy")
fmt.Println(res)
//Case 1 Substring supplied is an empty string
res = strings.Count("abcdabcd", "")
fmt.Println(res)
}
輸出:
2
0
9
```*
<!--yml
類別:未分類
日期:2024-10-13 06:47:48
-->
# 計算數字序列可能解碼為字母的方式(Golang)
> 來源:[`golangbyexample.com/count-possible-decodings-digit-golang/`](https://golangbyexample.com/count-possible-decodings-digit-golang/)
目錄
+ 概述
+ 程式
## **概述**
假設我們有以下數字到字母的對映
```go
'A' -> "1"
'B' -> "2"
...
'Z' -> "26"
目標是計算給定數字序列的可能解碼數量
示例
Input: '15'
Output: 2
15 can be decoded as "AE" or "O"
程式
這裡是相應的程式。
package main
import (
"fmt"
"strconv"
)
func numDecodings(s string) int {
runeArray := []rune(s)
length := len(runeArray)
if length == 0 {
return 0
}
if string(runeArray[0]) == "0" {
return 0
}
numwaysArray := make([]int, length)
numwaysArray[0] = 1
if length == 1 {
return numwaysArray[0]
}
secondDigit := string(runeArray[0:2])
num, _ := strconv.Atoi(secondDigit)
if num > 10 && num <= 19 {
numwaysArray[1] = 2
} else if num > 20 && num <= 26 {
numwaysArray[1] = 2
} else if num == 10 || num == 20 {
numwaysArray[1] = 1
} else if num%10 == 0 {
numwaysArray[1] = 0
} else {
numwaysArray[1] = 1
}
for i := 2; i < length; i++ {
firstDigit := string(runeArray[i])
if firstDigit != "0" {
numwaysArray[i] = numwaysArray[i] + numwaysArray[i-1]
}
secondDigit := string(runeArray[i-1 : i+1])
fmt.Println(i)
fmt.Println(secondDigit)
num, _ := strconv.Atoi(secondDigit)
if num >= 10 && num <= 26 {
numwaysArray[i] = numwaysArray[i] + numwaysArray[i-2]
}
}
return numwaysArray[length-1]
}
func main() {
output := numDecodings("15")
fmt.Println(output)
}
輸出
2
注意: 請檢視我們的 Golang 高階教程。本系列教程詳盡,並且我們努力涵蓋所有概念及例項。本教程適合那些希望獲得專業知識和紮實理解 Golang 的人 - Golang 高階教程
如果你有興趣瞭解如何在 Golang 中實現所有設計模式,那麼這篇文章適合你 - 所有設計模式 Golang
在 Go (Golang) 中計數無防守單元格的程式
來源:
golangbyexample.com/count-unguarded-cells-golang/
目錄
-
概述
-
程式
概述
給定兩個整數 m 和 n,表示一個 m*n 網格。除此之外,還給定兩個 2D 整數陣列
-
守衛的位置為 guards[i] = [rowi , columni]。它表示第 i 個守衛的位置
-
牆壁的位置為 guards[j] = [rowi , columni]。它表示第 i 個牆壁的位置
守衛可以向所有方向看
-
北
-
南
-
東
-
西
除非被牆壁阻擋。守衛能看到的所有單元格都算作已防守。目標是找出無防守單元格的數量
這裡是相同的 leetcode 連結 – https://leetcode.com/problems/count-unguarded-cells-in-the-grid/
程式
以下是相同的程式
package main
import "fmt"
func countUnguarded(m int, n int, guards [][]int, walls [][]int) int {
occupancy := make([][]int, m)
for i := 0; i < m; i++ {
occupancy[i] = make([]int, n)
}
for _, val := range guards {
i := val[0]
j := val[1]
occupancy[i][j] = 2
}
for _, val := range walls {
i := val[0]
j := val[1]
occupancy[i][j] = -1
}
for i := 0; i < m; i++ {
for j := 0; j < n; j++ {
if occupancy[i][j] == 2 {
countUtil(i, j, m, n, &occupancy)
}
}
}
count := 0
for i := 0; i < m; i++ {
for j := 0; j < n; j++ {
if occupancy[i][j] == 0 {
count += 1
}
}
}
return count
}
func countUtil(x, y, m, n int, occupancy *([][]int)) {
for i := x + 1; i < m; i++ {
if (*occupancy)[i][y] == 0 {
(*occupancy)[i][y] = 1
}
if (*occupancy)[i][y] == -1 || (*occupancy)[i][y] == 2 {
break
}
}
for i := x - 1; i >= 0; i-- {
if (*occupancy)[i][y] == 0 {
(*occupancy)[i][y] = 1
}
if (*occupancy)[i][y] == -1 || (*occupancy)[i][y] == 2 {
break
}
}
for i := y + 1; i < n; i++ {
if (*occupancy)[x][i] == 0 {
(*occupancy)[x][i] = 1
}
if (*occupancy)[x][i] == -1 || (*occupancy)[x][i] == 2 {
break
}
}
for i := y - 1; i >= 0; i-- {
if (*occupancy)[x][i] == 0 {
(*occupancy)[x][i] = 1
}
if (*occupancy)[x][i] == -1 || (*occupancy)[x][i] == 2 {
break
}
}
}
func main() {
output := countUnguarded(4, 6, [][]int{{0, 0}, {1, 1}, {2, 3}}, [][]int{{0, 1}, {2, 2}, {1, 4}})
fmt.Println(output)
output = countUnguarded(3, 3, [][]int{{1, 1}}, [][]int{{0, 1}, {1, 0}, {2, 1}, {1, 2}})
fmt.Println(output)
}
輸出
7
4
注意:檢視我們的 Golang 高階教程。本系列教程內容詳盡,我們嘗試覆蓋所有概念並附有示例。本教程適合那些希望獲得專業知識和對 golang 有深入理解的人 – Golang 高階教程
如果你有興趣瞭解所有設計模式在 Golang 中如何實現。如果是,那麼這篇文章適合你 – 所有設計模式 Golang
此外,檢視我們的系統設計教程系列 – 系統設計教程系列
在 Go (Golang)中計算無向圖中無法到達的節點對
來源:
golangbyexample.com/count-unreachable-pair-nodes-golang/
目錄
-
概述
-
程式
概述
給定一個整數 n。有 n 個節點編號為 0 到 n-1。還有一個二維整數陣列edges,其中edges[i] = [ai, bi]表示從 ai 到 bi 有一個無向節點。
目標是找到彼此無法到達的節點對數量
示例 1
n=3
edges=[{0,1}]
圖
![](https://gitee.com/OpenDocCN/geekdoc-golang-zh/raw/master/docs/go-exam-2023/img/8473545ae2c070f9173a090845f5e532.png)
輸出
2
我們有兩個未連線的節點
[{0,2}, {1,2}]
示例 2
n=9
edges=[{0,1},{0,4},{0,5},{2,3},{2,6},{7,8}]
圖
![](https://gitee.com/OpenDocCN/geekdoc-golang-zh/raw/master/docs/go-exam-2023/img/649eee1225b6dbfbb0bd449cfb524479.png)
輸出:
26
我們有 26 對未連線的節點
[{0,2}, {0,3}, {0,6}, {0,7}, {0,8},
{1,2}, {1,3}, {1,6}, {1,7}, {1,8},
{4,2}, {4,3}, {4,6}, {4,7}, {4,8},
{5,2}, {5,3}, {5,6}, {5,7}, {5,8},
{7,2}, {7,3}, {7,6},
{8,2}, {8,3}, {8,6}]
思路是從每個未訪問的節點開始進行深度優先搜尋,以識別每個連線圖中的節點數。在上面的例子中,每個連線圖中的節點數是
4
3
2
然後我們簡單地找到每個連線圖中的節點對數量
程式
以下是相應的程式
package main
import "fmt"
func countPairs(n int, edges [][]int) int64 {
nodeMap := make(map[int][]int)
for i := 0; i < len(edges); i++ {
nodeMap[edges[i][0]] = append(nodeMap[edges[i][0]], edges[i][1])
nodeMap[edges[i][1]] = append(nodeMap[edges[i][1]], edges[i][0])
}
visited := make(map[int]bool)
var output int64
var totalNodesVisited int64
for i := 0; i < n; i++ {
if !visited[i] {
nodeVisited := visit(i, nodeMap, &visited)
if totalNodesVisited != 0 {
output += totalNodesVisited * nodeVisited
}
totalNodesVisited += nodeVisited
}
}
return output
}
func visit(source_node int, nodeMap map[int][]int, visited *map[int]bool) int64 {
(*visited)[source_node] = true
var totalNodeVisited int64
totalNodeVisited = 1
neighbours, ok := nodeMap[source_node]
if ok {
for _, neighbour := range neighbours {
if !(*visited)[neighbour] {
nodeVisited := visit(neighbour, nodeMap, visited)
totalNodeVisited += nodeVisited
}
}
}
return totalNodeVisited
}
func main() {
n := 3
edges := [][]int{{0, 1}}
output := countPairs(n, edges)
fmt.Println(output)
n = 9
edges = [][]int{{0, 1}, {0, 4}, {0, 5}, {2, 3}, {2, 6}, {7, 8}}
output = countPairs(n, edges)
fmt.Println(output)
}
輸出:
2
26
注意: 請檢視我們的 Golang 高階教程。本系列教程內容詳盡,努力覆蓋所有概念及示例。此教程適合希望獲得 Golang 專業知識和紮實理解的人 - Golang 高階教程
如果你有興趣瞭解如何在 Golang 中實現所有設計模式,那麼這篇文章適合你 - 所有設計模式 Golang
此外,請檢視我們的系統設計教程系列 - 系統設計教程系列
在 Go (Golang) 中建立目錄或資料夾
來源:
golangbyexample.com/create-directory-folder-golang/
目錄
-
概述
-
程式碼
概述
os.Mkdir() 函式可以用於在 Go 中建立目錄或資料夾。
以下是該函式的簽名。
func Mkdir(name string, perm FileMode)
它接受兩個引數。
-
第一個引數是命名目錄。如果命名目錄是一個完全合格的路徑,它將在該路徑下建立一個目錄。如果不是,它將相對於當前工作目錄建立一個目錄。
-
第二個引數指定許可權位。資料夾或目錄是使用此許可權位建立的。
程式碼
package main
import (
"log"
"os"
)
func main() {
//Create a folder/directory at a full qualified path
err := os.Mkdir("/Users/temp", 0755)
if err != nil {
log.Fatal(err)
}
//Create a folder/directory at a full qualified path
err = os.Mkdir("temp", 0755)
if err != nil {
log.Fatal(err)
}
}
輸出
It will create a directory temp at location /Users location and at the current working directory location
- 建立* 目錄* 資料夾* go* golang*
在 Go (Golang) 中建立一個新時間例項
來源:
golangbyexample.com/create-new-time-instance-go/
在 Go 中,time.Time 結構用於表示時間或日期的例項。下面是建立新時間例項的三種方法
目錄
** 使用 time.Now()")
-
使用 time.Date()")
-
使用 time.Parse()")
使用 time.Now()
time.Now() 函式可用於獲取當前本地時間戳。該函式的簽名是
func Now() Time
使用 time.Date()
time.Date() 函式接受年份、月份、日期、小時、分鐘、秒、納秒和位置,並返回一個時間格式為 yyyy-mm-dd hh:mm:ss + nsec,具有與給定位置相對應的適當時區。該函式的簽名是
func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) Time
使用 time.Parse()
time.Parse() 可用於將時間的字串表示轉換為 time.Time 例項。該函式的簽名是
func Parse(layout, value string) (Time, error)
time.Parse 函式接受兩個引數 –
-
第一個引數是包含時間格式佔位符的佈局
-
第二個引數是表示時間的實際格式化字串。
下面是一個演示上述三種方法的工作程式碼示例
package main
import (
"fmt"
"time"
)
func main() {
//time.Now() example
t := time.Now()
fmt.Println(t)
//time.Date() Example
t = time.Date(2021, time.Month(2), 21, 1, 10, 30, 0, time.UTC)
fmt.Println(t)
//time.Parse() Example
//Parse YYYY-MM-DD
t, _ = time.Parse("2006-01-02", "2020-01-29")
fmt.Println(t)
}
輸出:
2020-02-03 11:34:10.85639 +0530 IST m=+0.000297951
2021-02-21 01:10:30 +0000 UTC
2020-01-29 00:00:00 +0000 UTC
- golang*
在 Go(Golang)中建立一個空檔案。
來源:
golangbyexample.com/create-empty-file-go/
目錄
-
概述
-
程式碼:
概述
os.Create() 可用於在 Go 中建立一個空檔案。該函式的簽名為
func Create(name string) (*File, error)
基本上這個函式
-
建立一個模式為 0666 的命名檔案。
-
如果檔案已經存在,它將截斷該檔案。
-
如果路徑有問題,它將返回一個路徑錯誤。
-
它返回一個檔案描述符,可用於讀取和寫入。
程式碼:
package main
import (
"log"
"os"
)
func main() {
file, err := os.Create("emptyFile.txt")
if err != nil {
log.Fatal(err)
}
defer file.Close()
}
輸出:
Check the contents of the file. It will be empty
- 空檔案* 檔案*
在 Go (Golang)中建立字串的重複副本
來源:
golangbyexample.com/create-count-repeating-copies-string-golang/
目錄
-
概述**
-
程式碼:
概述
GO 的strings包提供了一個Repeat方法,可用於建立給定字串的重複副本。它的輸入為重複次數。
以下是函式的簽名。它返回字串的副本。
func Repeat(s string, count int) string
讓我們看看有效的程式碼
程式碼:
package main
import (
"fmt"
"strings"
)
func main() {
res := strings.Repeat("abc", 4)
fmt.Println(res)
}
輸出:
abcabcabcabc
在 Go(Golang)中建立/初始化/宣告 map
來源:
golangbyexample.com/create-map-golang/
目錄
-
概述
-
宣告一個 Map
-
建立一個 Map
-
使用 map[<key_type>]<value_type>格式
-
使用 Make
-
概述
map 是 Golang 內建的資料型別,類似於雜湊表,它將鍵對映到值。
下面是 map 的格式:
map[key_type]value_type
key_type和value_type可以是不同型別或相同型別。在下面的例子中,鍵型別是string,值型別是int
map[string]int
宣告一個 Map
map 也可以使用 var 關鍵字宣告,但它會建立一個 nil map,因為 map 的預設零值是 nil。向該 map 新增任何鍵值對將導致恐慌。讓我們看看這個例子
package main
func main() {
var employeeSalary map[string]int
employeeSalary["Tom"] = 2000
}
輸出
panic: assignment to entry in nil map
上面的程式因 map 為 nil 而導致恐慌。
使用var關鍵字宣告 map 的一個用例是當需要將一個已存在的 map 賦值給它或當我們想要賦值函式的結果時。
建立一個 Map
建立 map 的兩種方式
-
使用 map[<key_type>]<value_type>{}格式也稱為 map 字面量
-
使用 make
讓我們逐一看看上述每種方法。
使用 map[<key_type>]<value_type>格式
建立 map 的最常見方式之一是使用 map 字面量:
map[key_type]value_type{}
上述例子的鍵型別是字串,值型別是整數
employeeSalary := map[string]int{}
map 也可以建立並初始化一些鍵值
employeeSalary := map[string]int{
"John": 1000
"Sam": 2000
}
也可以向 map 中新增鍵值對
employeeSalary["Tom"] = 2000
讓我們看看一個程式
package main
import "fmt"
func main() {
//Declare
employeeSalary := map[string]int{}
fmt.Println(employeeSalary)
//Intialize using map lieteral
employeeSalary = map[string]int{
"John": 1000,
"Sam": 1200,
}
//Adding a key value
employeeSalary["Tom"] = 2000
fmt.Println(employeeSalary)
}
輸出
map[]
map[John:1000 Sam:1200 Tom:2000]
在上面的程式中,我們建立了一個初始化為某些值的 map 字面量。然後我們在其中新增了另一個鍵值對。接著我們使用fmt.Println列印它,以格式 map[key:value key:value]列印所有的鍵值對。
使用 Make
這是建立 map 的另一種方式。內建函式make可用於建立 map。它返回一個初始化的 map。因此可以向其中新增鍵值對。
package main
import "fmt"
func main() {
//Declare
employeeSalary := make(map[string]int)
//Adding a key value
employeeSalary["Tom"] = 2000
fmt.Println(employeeSalary)
}
輸出
map[Tom:2000]
在上面的程式中,我們使用 make 函式建立了一個 map。然後我們在其中新增了一個鍵值對。接著我們使用fmt.Println列印它,列印出所有的鍵值對。
在 Go (Golang)中建立或初始化一個新字串
來源:
golangbyexample.com/create-string-golang/
目錄
** 概覽
- 程式
概覽
下面是一種簡單的方法來初始化或建立一個 Go 中的字串。在下面的程式中,我們簡單地宣告並定義了一個名為sample的字串。
注意語法
sample := "test"
這是完整的程式
程式
package main
import "fmt"
func main() {
sample := "test"
fmt.Println(sample)
}
輸出
test
如果我們只想宣告一個字串變數,下面就是方法。宣告的字串會初始化為空字串。
package main
import "fmt"
func main() {
var sample string
fmt.Println(sample)
}
輸出
注意: 請檢視我們的 Golang 高階教程。本系列教程內容詳盡,我們嘗試覆蓋所有概念及示例。此教程適合希望獲得專業知識和紮實理解 Golang 的人士 – Golang 高階教程
如果你有興趣瞭解所有設計模式如何在 Golang 中實現。如果是,那麼這篇文章就是為你準備的 –
所有設計模式 Golang
注意: 請檢視我們的系統設計教程系列 系統設計問題*
透過在 Go (Golang) 中呼叫恐慌函式建立恐慌
來源:
golangbyexample.com/create-panic-golang/
目錄
-
概述
-
示例
概述
Go 提供了一個特殊函式來建立恐慌。下面是該函式的語法
func panic(v interface{})
此函式可以由程式設計師顯式呼叫以建立恐慌。它接受一個空介面作為引數。
在程式設計師可以顯式呼叫恐慌函式的一些情況包括:
-
函式期望一個有效的引數,但卻提供了一個 nil 引數。在這種情況下,程式無法繼續,並且會因傳入 nil 引數而引發恐慌。
-
程式無法繼續的任何其他情況。
示例
讓我們看一個例子
package main
import "fmt"
func main() {
a := []string{"a", "b"}
checkAndPrint(a, 2)
}
func checkAndPrint(a []string, index int) {
if index > (len(a) - 1) {
panic("Out of bound access for slice")
}
fmt.Println(a[index])
}
輸出
panic: runtime error: index out of range [2] with length 2
goroutine 1 [running]:
main.checkAndPrint(0xc00009af58, 0x2, 0x2, 0x2)
main.go:15 +0x31
main.main()
main.go:8 +0x7d
exit status 2
在上面的程式中,我們再次有一個函式 checkAndPrint,它接受一個切片作為引數和一個索引。然後它檢查傳入的索引是否大於切片長度減去 1。如果是,那麼它就是超出切片邊界的訪問,因此會引發恐慌。如果不是,則列印該索引處的值。再注意輸出中有兩件事
-
錯誤資訊
-
發生恐慌時的堆疊跟蹤
在 Go (Golang)中建立浮點切片或陣列。
來源:
golangbyexample.com/slice-array-floats-golang/
。
目錄。
** 概述
-
浮點切片
-
浮點陣列
概述
在 Golang 中也可以建立浮點資料型別的切片或陣列。實際上,任何資料型別都可以在 Go 中建立切片或陣列。本教程包含建立浮點資料型別切片或陣列的簡單示例。
在此補充說明,Golang 中的陣列大小是固定的,而切片可以具有可變大小。更多詳情請見這裡。
陣列 – golangbyexample.com/understanding-array-golang-complete-guide/
切片 – golangbyexample.com/slice-in-golang/
。
浮點切片
package main
import "fmt"
func main() {
//First Way
var floats_first []float64
floats_first = append(floats_first, 1.1)
floats_first = append(floats_first, 2.2)
floats_first = append(floats_first, 3.3)
fmt.Println("Output for First slice of floats")
for _, c := range floats_first {
fmt.Println(c)
}
//Second Way
floats_second := make([]float64, 3)
floats_second[0] = 3.3
floats_second[1] = 2.2
floats_second[2] = 1.1
fmt.Println("\nOutput for Second slice of floats")
for _, c := range floats_second {
fmt.Println(c)
}
}
輸出
Output for First slice of floats
1.1
2.2
3.3
Output for Second slice of floats
3.3
2.2
1.1
我們有兩種建立浮點切片的方法。第一種方法是
var floats_first []float64
floats_first = append(floats_first, 1.1)
floats_first = append(floats_first, 2.2)
floats_first = append(floats_first, 3.3)
在第二種方法中,我們使用 make 命令來建立浮點切片。
floats_second := make([]float64, 3)
floats_second[0] = 3.3
floats_second[1] = 2.2
floats_second[2] = 1.1
浮點陣列
package main
import "fmt"
func main() {
var floats_first [3]float64
floats_first[0] = 1.1
floats_first[1] = 2.2
floats_first[2] = 3.3
fmt.Println("Output for First Array of floats")
for _, c := range floats_first {
fmt.Println(c)
}
floats_second := [3]float64{
3.3,
2.2,
1.1,
}
fmt.Println("\nOutput for Second Array of floats")
for _, c := range floats_second {
fmt.Println(c)
}
}
輸出
Output for First Array of floats
1.1
2.2
3.3
Output for Second Array of floats
3.3
2.2
1.1
我們有兩種建立陣列的方法。第一種方法是
var floats_first [3]float64
floats_first[0] = 1.1
floats_first[1] = 2.2
floats_first[2] = 3.3
在第二種方法中,我們直接用建立的浮點數初始化陣列。
floats_second := [3]float64{
3.3,
2.2,
1.1,
}
請檢視我們的 Golang 高階教程。本系列教程內容詳盡,我們盡力涵蓋所有概念和示例。本教程適合希望獲得專業知識和對 Golang 有深入理解的讀者 – Golang 高階教程。
如果您有興趣瞭解如何在 Golang 中實現所有設計模式。如果是的話,這篇文章適合您 – 所有設計模式 Golang。
在 Go 中建立整數的切片或陣列(Golang)
來源:
golangbyexample.com/slice-array-integers-golang/
目錄
-
概述**
-
整數切片
-
整數陣列
概述
在 Golang 中也可以建立int資料型別的切片或陣列。實際上,Go 中可以建立任何資料型別的切片或陣列。本教程包含簡單的示例,演示如何在 Golang 中建立 int 資料型別的切片或陣列。
這裡補充一下,在 Golang 中陣列是固定大小的,而切片可以有可變大小。更多細節在這裡
陣列 – golangbyexample.com/understanding-array-golang-complete-guide/
切片 – golangbyexample.com/slice-in-golang/
整數切片
package main
import "fmt"
func main() {
//First Way
var integers_first []int
integers_first = append(integers_first, 1)
integers_first = append(integers_first, 2)
integers_first = append(integers_first, 3)
fmt.Println("Output for First slice of integers")
for _, c := range integers_first {
fmt.Println(c)
}
//Second Way
integers_second := make([]int, 3)
integers_second[0] = 3
integers_second[1] = 2
integers_second[2] = 1
fmt.Println("\nOutput for Second slice of integers")
for _, c := range integers_second {
fmt.Println(c)
}
}
輸出
Output for First slice of integers
1
2
3
Output for Second slice of integers
3
2
1
我們有兩種建立整數切片的方法。第一種方法是
var integers_first []int
integers_first = append(integers_first, 1)
integers_first = append(integers_first, 2)
integers_first = append(integers_first, 3)
在第二種方法中,我們使用 make 命令來建立整數切片
integers_second := make([]int, 3)
integers_second[0] = 3
integers_second[1] = 2
integers_second[2] = 1
無論哪種方法都可以。這就是我們如何建立整數切片
整數陣列
package main
import "fmt"
func main() {
var integers_first [3]int
integers_first[0] = 1
integers_first[1] = 2
integers_first[2] = 3
fmt.Println("Output for First Array of integers")
for _, c := range integers_first {
fmt.Println(c)
}
integers_second := [3]int{
3,
2,
1,
}
fmt.Println("\nOutput for Second Array of integers")
for _, c := range integers_second {
fmt.Println(c)
}
}
輸出
Output for First Array of integers
1
2
3
Output for Second Array of integers
3
2
1
我們有兩種建立陣列的方法。第一種方法是
var integers_first [3]int
integers_first[0] = 1
integers_first[1] = 2
integers_first[2] = 3
在第二種方法中,我們直接用建立的整數初始化陣列
integers_second := [3]int{
3,
2,
1,
}
檢視我們的 Golang 高階教程。本系列教程詳盡,我們努力覆蓋所有概念及示例。本教程適合那些希望獲得專業知識和紮實理解 Golang 的人 – Golang 高階教程
如果你有興趣瞭解所有設計模式如何在 Golang 中實現。如果是的話,這篇文章適合你 –所有設計模式 Golang
在 Go (Golang)中建立字串切片或陣列
來源:
golangbyexample.com/slice-array-string-golang/
目錄
-
概述
-
字串切片
-
字串陣列
概述
在 Golang 中,也可以建立string資料型別的切片或陣列。實際上,Go 中可以建立任何資料型別的切片或陣列。本教程包含建立string資料型別的切片或陣列的簡單示例。
這裡補充一下,在 golang 中,陣列是固定大小的,而切片可以有可變大小。更多詳細資訊請檢視這裡。
陣列 – golangbyexample.com/understanding-array-golang-complete-guide/
切片 – golangbyexample.com/slice-in-golang/
字串切片
package main
import "fmt"
func main() {
//First Way
var string_first []string
string_first = append(string_first, "abc")
string_first = append(string_first, "def")
string_first = append(string_first, "ghi")
fmt.Println("Output for First slice of string")
for _, c := range string_first {
fmt.Println(c)
}
//Second Way
string_second := make([]string, 3)
string_second[0] = "ghi"
string_second[1] = "def"
string_second[2] = "abc"
fmt.Println("\nOutput for Second slice of string")
for _, c := range string_second {
fmt.Println(c)
}
}
輸出
Output for First slice of string
abc
def
ghi
Output for Second slice of string
ghi
def
abc
我們有兩種建立字串切片的方法。第一種方法是
var string_first []string
string_first = append(string_first, "abc")
string_first = append(string_first, "def")
string_first = append(string_first, "ghi")
在第二種方法中,我們使用 make 命令來建立字串切片。
string_second := make([]string, 3)
string_second[0] = "ghi"
string_second[1] = "def"
string_second[2] = "abc"
無論哪種方式都可以。這是我們建立字串切片的方法。
字串陣列
package main
import "fmt"
func main() {
var string_first [3]string
string_first[0] = "abc"
string_first[1] = "def"
string_first[2] = "ghi"
fmt.Println("Output for First Array of string")
for _, c := range string_first {
fmt.Println(c)
}
string_second := [3]string{
"ghi",
"def",
"abc",
}
fmt.Println("\nOutput for Second Array of string")
for _, c := range string_second {
fmt.Println(c)
}
}
輸出
Output for First Array of string
abc
def
ghi
Output for Second Array of string
ghi
def
abc
我們有兩種建立陣列的方法。第一種方法是
var string_first [3]string
string_first[0] = "abc"
string_first[1] = "def"
string_first[2] = "ghi"
在第二種方法中,我們直接用一些字串初始化陣列。
string_second := [3]string{
"ghi",
"def",
"abc",
}
檢視我們的 Golang 高階教程。本系列的教程內容詳盡,盡力涵蓋所有概念及示例。本教程適合那些希望掌握 golang 並深入理解的讀者 – Golang 高階教程
如果你有興趣瞭解所有設計模式如何在 Golang 中實現。如果是的話,這篇文章就是為你準備的 – 所有設計模式 Golang
在 Go (Golang) 中的數字立方根
來源:
golangbyexample.com/cube-root-number-golang/
目錄
-
概述
-
程式碼:
概述
GO 的 math 包提供了一個 Cbrt 方法,可以用於獲取該數字的立方根。
以下是該函式的簽名。它接受一個浮點數作為輸入,並返回一個浮點數。
func Cbrt(x float64) float64
Cbrt 函式的一些特殊情況是
-
Cbrt(±0) = ±0
-
Cbrt(±Inf) = ±Inf
-
Cbrt(NaN) = NaN
程式碼:
package main
import (
"fmt"
"math"
)
func main() {
res := math.Cbrt(8)
fmt.Println(res)
res = math.Cbrt(27)
fmt.Println(res)
res = math.Cbrt(30.33)
fmt.Println(res)
}
輸出:
2
3
3.118584170228812
- 立方根* go* 數字*
Go 語言中的當前時間戳
來源:
golangbyexample.com/current-timestamp-in-golang/
目錄
概述
- 程式碼
概述
在本教程中,我們將看到如何使用 Go 語言中的 time 包獲取當前時間戳。當前時間可以用不同的方式表示。
- time.Time物件
t := time.Now() //It will return time.Time object with current timestamp
- Unix 時間(也稱為紀元時間) – 它是自 1970 年 1 月 1 日 00:00:00 UTC 以來經過的秒數。這個時間也被稱為 Unix 紀元。
t := time.Now().Unix()
//Will return number of seconds passed since Unix epoch
- Unix 納秒 – 自 1970 年 1 月 1 日 00:00:00 UTC 以來經過的納秒數
t := time.Now().UnixNano()
//Will return number of nano seconds passed since Unix epoch
- Unix 毫秒 – 自 1970 年 1 月 1 日 00:00:00 UTC 以來經過的毫秒數
t:= int64(time.Nanosecond) * t.UnixNano() / int64(time.Millisecond)/ time.Millisecond
//Number of millisecond elapsed since Unix epoch
- Unix 微秒 – 自 1970 年 1 月 1 日 00:00:00 UTC 以來經過的微秒數
t:= int64(time.Nanosecond) * t.UnixNano() / int64(time.Millisecond)/ time.Millisecond
//Number of millisecond elapsed since Unix epoch
程式碼
package main
import (
"fmt"
"time"
)
func main() {
t := time.Now() //It will return time.Time object with current timestamp
fmt.Printf("time.Time %s\n", t)
tUnix := t.Unix()
fmt.Printf("timeUnix: %d\n", tUnix)
tUnixNano := t.UnixNano()
fmt.Printf("timeUnixNano: %d\n", tUnixNano)
tUnixMilli := int64(time.Nanosecond) * t.UnixNano() / int64(time.Millisecond)
fmt.Printf("timeUnixMilli: %d\n", tUnixMilli)
tUnixMicro := int64(time.Nanosecond) * t.UnixNano() / int64(time.Microsecond)
fmt.Printf("timeUnixMicro: %d\n", tUnixMicro)
}
輸出:
time.Time 2020-01-24 09:43:42.196901 UTC m=+0.000229700
timeUnix: 1579839222
timeUnixNano: 1579839222196901000
timeUnixMilli: 1579839222196
timeUnixMicro: 1579839222196901
- 紀元* 毫秒* 納秒* unix*
Go (Golang) 中的 defer 中的自定義函式
來源:
golangbyexample.com/custom-function-defer-golang/
目錄
-
概述
-
示例
概述
我們也可以在 defer 中呼叫自定義函式。讓我們來看一個示例
示例
package main
import "fmt"
func main() {
defer test()
fmt.Println("Executed in main")
}
func test() {
fmt.Println("In Defer")
}
輸出
Executed in main
In Defer
在上述程式中,有一個defer語句呼叫了名為test的自定義函式。從輸出可以看到,test函式在主函式中的所有操作執行完畢後被呼叫,並在主函式返回之前呼叫。這就是為什麼
Executed in main
會在之前列印
In Defer
上述函式還顯示在主函式中使用 defer 是完全可以的。
Go 中的日期 (Golang)
來源:
golangbyexample.com/date-in-golang/
在 Go 中,日期僅透過 time.Time 結構表示。Go 中沒有單獨的 Date 結構。可以使用 time.Date 函式構造日期。該函式返回格式為 yyyy-mm-dd hh:mm:ss + nsec 的時間,包含與給定位置對應的適當時區。函式的簽名是:
func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) Time
從簽名中可以看出,函式的引數是
-
年
-
月
-
天
-
小時
-
分
-
秒
-
毫秒
-
位置
關於 time.Date 函式的一些注意事項
-
如果傳入的位置資訊為 nil,Date 函式將會引發 panic。
-
月、日、時、分、秒、納秒值會被規範化。因此,如果傳入的月份是 14,它將被轉換為 2。
讓我們看看一個工作示例:
package main
import (
"fmt"
"time"
)
func main() {
t := time.Date(2021, time.Month(2), 21, 1, 10, 30, 0, time.UTC)
fmt.Println(t)
}
輸出:
2021-02-21 01:10:30 +0000 UTC
在 Go(Golang)中宣告/初始化/建立陣列或切片
來源:
golangbyexample.com/declare-initialize-create-array-slice-golang/
目錄
概述
-
建立切片
-
使用 []<型別>{} 格式
-
從另一個切片或陣列建立切片
-
從陣列建立切片
-
從切片建立切片
-
-
使用 make 函式
-
使用新函式
-
-
建立陣列 *# 概述
與其他程式語言類似,Golang 也有陣列資料結構。但在 Go 中,陣列的行為與其他語言略有不同,我們還有一種叫切片的結構,它類似於陣列的引用。切片比陣列更強大且更方便使用。事實上,切片在其他程式語言中更類似於陣列。
在這篇文章中,我們將學習如何
-
建立切片例項
-
建立陣列例項
建立切片
建立切片的方式有四種
-
使用 []<型別>{} 格式
-
從另一個切片或陣列建立切片
-
使用 make
-
使用新方法
讓我們逐一檢視上述每種方法。
使用 []<型別>{} 格式
宣告切片的最常見方式是這樣的
s := []int
它宣告瞭一個長度為 0、容量為 0 的空切片。我們也可以在宣告時初始化切片。
s := []int{1,2}
它宣告瞭一個長度為 2、容量也為 2 的整數切片。容量等於實際指定的切片元素數量。我們還有兩個由 Go 提供的庫函式可以用來獲取切片的 長度 和 容量。
-
len() 函式 – 用於切片的長度
-
cap() 函式 – 用於切片的容量
讓我們看看一個小程式,展示以上幾點。
package main
import "fmt"
func main() {
sample := []int{}
fmt.Println(len(sample))
fmt.Println(cap(sample))
fmt.Println(sample)
letters := []string{"a", "b", "c"}
fmt.Println(len(letters))
fmt.Println(cap(letters))
fmt.Println(letters)
}
輸出
0
0
[]
3
3
[a b c]
當實際元素未指定時,切片的長度和容量均為零。當指定實際元素時,長度和容量等於指定的實際元素數量。
從另一個切片或陣列建立切片
切片可以透過重新切片現有切片或陣列來建立。
從陣列建立切片
透過重新切片現有陣列建立新切片的格式為
[n]sample[start:end]
上述操作將返回一個新切片,從陣列的索引 start 開始到索引 end-1。因此,索引 end 的元素不包含在新建立的切片中。在重新切片時,起始和結束索引都是可選的。
-
起始索引的預設值為零
-
結束索引的預設值是陣列的長度。
讓我們看看一個例子。
package main
import "fmt"
func main() {
numbers := [5]int{1, 2, 3, 4, 5}
//Both start and end
num1 := numbers[2:4]
fmt.Println("Both start and end")
fmt.Printf("num1=%v\n", num1)
fmt.Printf("length=%d\n", len(num1))
fmt.Printf("capacity=%d\n", cap(num1))
//Only start
num2 := numbers[2:]
fmt.Println("\nOnly start")
fmt.Printf("num1=%v\n", num2)
fmt.Printf("length=%d\n", len(num2))
fmt.Printf("capacity=%d\n", cap(num2))
//Only end
num3 := numbers[:3]
fmt.Println("\nOnly end")
fmt.Printf("num1=%v\n", num3)
fmt.Printf("length=%d\n", len(num3))
fmt.Printf("capacity=%d\n", cap(num3))
//None
num4 := numbers[:]
fmt.Println("\nOnly end")
fmt.Printf("num1=%v\n", num4)
fmt.Printf("length=%d\n", len(num4))
fmt.Printf("capacity=%d\n", cap(num4))
}
輸出
Both start and end
num1=[3 4]
length=2
capacity=3
Only start
num1=[3 4 5]
length=3
capacity=3
Only end
num1=[1 2 3]
length=3
capacity=5
Only end
num1=[1 2 3 4 5]
length=5
capacity=5
請注意上面的例子中:
-
新建立切片的長度 = (end–start)
-
新建立切片的容量 = (length_of_array–start)
num1切片的樣子如下。
![](https://gitee.com/OpenDocCN/geekdoc-golang-zh/raw/master/docs/go-exam-2023/img/33586fc71a4b7b6f0294dc7dc24f41df.png)
新建立的切片仍然引用原始陣列。要檢查這一點,可以在陣列的任意索引處更改元素,然後重新列印切片。
numbers[3] = 8
fmt.Printf("num1=%v\n", num2)
fmt.Printf("num3=%v\n", num3)
fmt.Printf("num4=%v\n", num4)
輸出如下:
num1=[3 8 5]
num3=[1 2 3 8]
num4=[1 2 3 8 5]
這證明每個新切片仍然引用原始陣列。
從切片建立切片
我們關於從陣列重新切片的討論同樣適用於這裡。請參見下面的例子以說明同樣的內容。
package main
import "fmt"
func main() {
numbers := []int{1, 2, 3, 4, 5}
//Both start and end
num1 := numbers[2:4]
fmt.Println("Both start and end")
fmt.Printf("num1=%v\n", num1)
fmt.Printf("length=%d\n", len(num1))
fmt.Printf("capacity=%d\n", cap(num1))
//Only start
num2 := numbers[2:]
fmt.Println("\nOnly start")
fmt.Printf("num1=%v\n", num2)
fmt.Printf("length=%d\n", len(num2))
fmt.Printf("capacity=%d\n", cap(num2))
//Only end
num3 := numbers[:3]
fmt.Println("\nOnly end")
fmt.Printf("num1=%v\n", num3)
fmt.Printf("length=%d\n", len(num3))
fmt.Printf("capacity=%d\n", cap(num3))
//None
num4 := numbers[:]
fmt.Println("\nOnly end")
fmt.Printf("num1=%v\n", num4)
fmt.Printf("length=%d\n", len(num4))
fmt.Printf("capacity=%d\n", cap(num4))
}
輸出
Both start and end
num1=[3 4]
length=2
capacity=3
Only start
num1=[3 4 5]
length=3
capacity=3
Only end
num1=[1 2 3]
length=3
capacity=5
Only end
num1=[1 2 3 4 5]
length=5
capacity=5
在這裡,新建立的切片也引用與原始切片所引用的相同基礎陣列。要檢查這一點,可以在原始切片的任意索引處更改元素,然後重新列印所有新建立的切片。
numbers[3] = 8
fmt.Printf("num1=%v\n", num2)
fmt.Printf("num3=%v\n", num3)
fmt.Printf("num4=%v\n", num4)
使用 make 函式
make是 Go 提供的一個內建函式,也可以用來建立切片。以下是 make 函式的簽名。
func make([]{type}, length, capacity int) []{type}
建立切片時,make
函式的容量是一個可選引數。當省略容量時,切片的容量等於指定的長度。使用make
函式時,Go 背後會分配一個與容量相等的陣列。分配的陣列的所有元素都初始化為該型別的預設零值。讓我們看看一個說明這一點的程式。
package main
import "fmt"
func main() {
numbers := make([]int, 3, 5)
fmt.Printf("numbers=%v\n", numbers)
fmt.Printf("length=%d\n", len(numbers))
fmt.Printf("capacity=%d\n", cap(numbers))
//With capacity ommited
numbers = make([]int, 3)
fmt.Println("\nCapacity Ommited")
fmt.Printf("numbers=%v\n", numbers)
fmt.Printf("length=%d\n", len(numbers))
fmt.Printf("capacity=%d\n", cap(numbers))
}
輸出
numbers=[0 0 0]
length=3
capacity=5
With Capacity Ommited
numbers=[0 0 0]
length=3
capacity=3
使用 new 函式
new是 Go 提供的一個內建函式,也可以用來建立切片。這種建立切片的方式並不常用,因為make在功能上更加靈活。一般情況下不常使用,並且使用new函式會返回指向 nil 切片的指標。讓我們看一個例子。在下面的例子中,我們使用解引用運算子‘*’,因為new函式返回指向 nil 切片的指標。
package main
import "fmt"
func main() {
numbers := new([]int)
fmt.Printf("numbers=%v\n", *numbers)
fmt.Printf("length=%d\n", len(*numbers))
fmt.Printf("capacity=%d\n", cap(*numbers))
}
輸出
numbers=[]
length=0
capacity=0
建立一個陣列
陣列宣告中的元素數量和實際元素都是可選的。
在下面的例子中,我們看到建立陣列的 4 種方式。
- 同時指定陣列的長度和實際元素。例如:
[2]int{1, 2}
- 僅長度——在這種情況下,所有實際元素都填充為該型別的預設值零。例如:
[2]int{}
- 僅實際元素——在這種情況下,陣列的長度將等於實際元素的數量。符號‘…’在不指定長度時需要在方括號內使用,例如[…]。該符號是指令,指示編譯器計算長度。
[...]int{2, 3}
- 沒有長度和實際元素——在這種情況下,將建立一個空陣列。與上述相似,符號‘…’在這種情況下也需要使用。
[...]int{}
讓我們看一個程式碼示例來說明上述要點。還請記住,可以使用內建函式len()來計算陣列的長度。在下面的程式中,我們使用len()函式來計算陣列的長度。
package main
import "fmt"
func main() {
//Both number of elements and actual elements
sample1 := [2]int{1, 2}
fmt.Printf("Sample1: Len: %d, %v\n", len(sample1), sample1)
//Only actual elements
sample2 := [...]int{2, 3}
fmt.Printf("Sample2: Len: %d, %v\n", len(sample2), sample2)
//Only number of elements
sample3 := [2]int{}
fmt.Printf("Sample3: Len: %d, %v\n", len(sample3), sample3)
//Without both number of elements and actual elements
sample4 := [...]int{}
fmt.Printf("Sample4: Len: %d, %v\n", len(sample4), sample4)
}
輸出
Sample1: Len: 2, [1 2]
Sample2: Len: 2, [2 3]
Sample3: Len: 2, [0 0]
Sample4: Len: 0, []
請注意,上述示例中,對於sample3變數,實際元素用預設值 0 填充。
如果指定的實際元素少於陣列的長度也是可以的。其餘元素將用指定型別的預設值填充。請參見下面的示例。指定的陣列長度為 4,而只有 2 個實際元素被宣告。因此,剩餘的兩個元素被賦值為 0,這是int的預設零值。
package main
import "fmt"
func main() {
sample := [4]int{5, 8}
fmt.Printf("Sample: Len: %d, %v\n", len(sample), sample)
}
輸出
Sample: Len: 4, [5 8 0 0]