通过示例学习-Go-语言-2023-八-

通过示例学习 Go 语言 2023(八)

Go (Golang) 中的 Cookies

来源:golangbyexample.com/cookies-golang/

目录

** 什么是 cookie

  • 在 golang 中设置 cookie

    • 在发起请求时设置 cookie。

    • 在响应传入请求时设置 cookie

  • 在 golang 中读取 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

我们已经提到 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/

这是 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 中的所有细节。

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 结构的 CookieCookies 方法。

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 中,反之亦然,除非 srcdst 引用同一个切片。

一个简单的整数类型复制示例,说明上述要点。

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) 中创建一个新时间实例

来源: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

在 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 是 Golang 内置的数据类型,类似于哈希表,它将键映射到值。

下面是 map 的格式:

map[key_type]value_type

key_typevalue_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 语言中的当前时间戳

来源: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

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

请注意上面的例子中:

  • 新创建切片的长度 = (endstart)

  • 新创建切片的容量 = (length_of_arraystart)

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]
posted @ 2024-10-19 01:44  绝不原创的飞龙  阅读(2)  评论(0编辑  收藏  举报