透過示例學習-Go-語言-2023-二十六-

绝不原创的飞龙發表於2024-10-19

透過示例學習 Go 語言 2023(二十六)

Go(Golang)中的指標接收者方法

來源:golangbyexample.com/pointer-receiver-method-golang/

為了更好地理解指標接收者,我們首先必須理解方法的值接收者。

值接收者上的方法

讓我們看看值接收者的一個例子。

package main

import "fmt"

type employee struct {
    name   string
    age    int
    salary int
}

func (e employee) details() {
    fmt.Printf("Name: %s\n", e.name)
    fmt.Printf("Age: %d\n", e.age)
}

func (e employee) getSalary() int {
    return e.salary
}

func main() {
    emp := employee{name: "Sam", age: 31, salary: 2000}
    emp.details()
    fmt.Printf("Salary %d\n", emp.getSalary())
}

輸出

Name: Sam
Age: 31
Salary 2000

注意,接收者在方法內部是可用的,接收者的欄位可以在方法內部訪問。接收者的欄位也可以在方法內部更改嗎?

讓我們看看這個。

package main

import "fmt"

type employee struct {
    name   string
    age    int
    salary int
}

func (e employee) setNewName(newName string) {
    e.name = newName
}

func main() {
    emp := employee{name: "Sam", age: 31, salary: 2000}
    emp.setNewName("John")
    fmt.Printf("Name: %s\n", emp.name)
}

輸出

Name: Sam

在上述程式碼中,定義了一個方法setNewName,它在employee結構體上。在這個方法中,我們像這樣更新員工的名字。

e.name = newName

在設定新名字後,當我們在主函式中再次列印員工名字時,我們看到列印的是舊名字“Sam”,而不是“John”。這是因為方法是在值接收者上定義的。

func (e employee) setNewName(newName string) 

由於方法是在值接收者上定義的,當呼叫該方法時,會生成接收者的副本,該副本在方法內部可用。由於這是一個副本,對值接收者所做的任何更改對呼叫者都是不可見的。這就是為什麼它列印舊名字“Sam”而不是“John”。現在,腦海中產生的問題是是否有辦法修復這個問題。答案是有,這就是指標接收者進入的場景。

指標接收者上的方法

在上述示例中,我們看到了一個值接收者的方法。對值接收者所做的任何更改對呼叫者不可見。方法也可以在指標接收者上定義。對指標接收者所做的任何更改對呼叫者是可見的。讓我們來看一個例子。

package main

import "fmt"

type employee struct {
    name   string
    age    int
    salary int
}

func (e *employee) setNewName(newName string) {
    e.name = newName
}

func main() {
    emp := &employee{name: "Sam", age: 31, salary: 2000}
    emp.setNewName("John")
    fmt.Printf("Name: %s\n", emp.name)
}

輸出

Name: John

在上面的程式中,我們在指標接收者上定義了方法setNewName

func (e *employee) setNewName(newName string)

然後我們建立了一個員工指標,並在其上呼叫了setNewName方法。我們看到在setNewName內部對員工指標所做的更改對呼叫者是可見的,並且列印了新名字。

是否有必要建立員工指標以呼叫帶有指標接收者的方法?不,沒有必要。可以在員工例項上呼叫該方法,語言會自動將接收者作為指標傳遞給方法。這種靈活性是語言提供的。

讓我們看看一個例子。

package main

import "fmt"

type employee struct {
    name   string
    age    int
    salary int
}

func (e *employee) setNewName(newName string) {
    e.name = newName
}

func main() {
    emp := employee{name: "Sam", age: 31, salary: 2000}
    emp.setNewName("John")

    fmt.Printf("Name: %s\n", emp.name)

    (&emp).setNewName("Mike")
    fmt.Printf("Name: %s\n", emp.name)
}

輸出

Name: John
Name: Mike

我們在上面的程式中看到,即使方法在指標接收者上定義,但我們仍然使用非指標的員工例項呼叫該方法。

emp.setNewName("John")

但是語言將接收者作為指標傳遞,因此更改對呼叫者是可見的。即使這樣呼叫也是有效的。

(&emp).setNewName("Mike")

現在,反過來,如果一個方法在值接收者上定義,可以用接收者的指標來呼叫該方法嗎?

是的,這也是有效的,語言會確保正確地將引數作為值接收者傳遞,無論方法是呼叫在指標還是普通結構上。讓我們看看一個例子。

package main

import "fmt"

type employee struct {
    name   string
    age    int
    salary int
}

func (e employee) setNewName(newName string) {
    e.name = newName
}

func main() {
    emp := employee{name: "Sam", age: 31, salary: 2000}
    emp.setNewName("John")

    fmt.Printf("Name: %s\n", emp.name)
    (&emp).setNewName("Mike")

    fmt.Printf("Name: %s\n", emp.name)
    emp2 := &employee{name: "Sam", age: 31, salary: 2000}
    emp2.setNewName("John")
    fmt.Printf("Name: %s\n", emp2.name)
}

輸出

Name: Sam
Name: Sam
Name: Sam

請注意,在這三種情況下,setNewName 方法具有值接收者,因此更改對呼叫者不可見,因為值是作為副本傳遞的。在這三種情況下,它都列印舊名稱。

總結一下我們上面學到的內容:

  • 如果一個方法有一個值接收者,它支援用值和指標接收者來呼叫該方法。

  • 如果方法有一個指標接收者,那麼它也支援使用值和指標接收者來呼叫該方法。

這與函式不同,如果

  • 如果一個函式有一個指標引數,那麼它只會接受指標作為引數。

  • 如果一個函式有一個值引數,那麼它只會接受值作為引數。

何時使用指標接收者

  • 當方法內部對接收者所做的更改必須對呼叫者可見時。

  • 當結構體較大時,最好使用指標接收者,否則每次呼叫方法時都會建立結構體的副本,這將非常耗費資源。

Go (Golang)中的指向指標

來源:golangbyexample.com/pointer-to-pointer-golang/

目錄

  • 概述

  • 程式

概述

在 Go 中也可以建立指向指標的指標。

a := 2
b := &a
c := &b

c在這裡是一個指向指標的指標。它儲存b的地址,而b又儲存a的地址。可以使用***運算子進行雙重解引用,從而列印指向指標的值。因此,****c將列印值 2。

下圖描繪了指向指標的指標。

  • b包含a的地址。

  • c包含b的地址。

![](https://gitee.com/OpenDocCN/geekdoc-golang-zh/raw/master/docs/go-exam-2023/img/648843d5f7bbd0caf2d036d24bddb2bd.png)

程式

讓我們來看一個描繪指標的程式。

package main

import "fmt"

func main() {
	a := 2
	b := &a
	c := &b

	fmt.Printf("a: %d\n", a)
	fmt.Printf("b: %x\n", b)
	fmt.Printf("c: %x\n", c)

	fmt.Println()
	fmt.Printf("a: %d\n", a)
	fmt.Printf("*&a: %d\n", *&a)
	fmt.Printf("*b: %d\n", *b)
	fmt.Printf("**c: %d\n", **c)

	fmt.Println()
	fmt.Printf("&a: %d\n", &a)
	fmt.Printf("b: %d\n", b)
	fmt.Printf("&*b: %d\n", &*b)
	fmt.Printf("*&b: %d\n", *b)
	fmt.Printf("*c: %d\n", *c)

	fmt.Println()
	fmt.Printf("b: %d\n", &b)
	fmt.Printf("*c: %d\n", c)
}

輸出

a: 2
b: c000018078
c: c00000e028

a: 2
*&a: 2
*b: 2
**c: 2

&a: 824633819256
b: 824633819256
&*b: 824633819256
*&b: 824633819256
*c: 824633819256

b: 824633778216
*c: 824633778216

從輸出中可以清楚看出。

以下是等價且值為變數a的 2。

  • a

  • *&a

  • *b

  • **c

以下是等價且值為變數ba的地址。

*** &a

  • b

  • &*b

  • *&b

  • *c

以下是等價且值為變數cb的地址。**

***** b

  • *c

Go(Golang)中的結構體指標

來源:golangbyexample.com/pointer-to-struct-golang/

在 Golang 中,結構體是命名的資料欄位集合。這些欄位可以是不同型別。結構體充當容器,包含不同的異構資料型別,共同表示一個實體。例如,不同的屬性用於表示組織中的員工。員工可以有:

  • 字串型別的名稱

  • 年齡型別為 int

  • DOB 型別為 time.Time

  • 薪水型別為 int

Golang 中的指標是一個變數,它儲存另一個變數的記憶體地址。

現在我們已經理解了結構體和指標,接下來讓我們看看如何定義指向結構體的指標。

假設我們有一個員工結構體

type employee struct {
    name   string
    age    int
    salary int
}

建立指向結構體的指標有兩種方法

  • 使用&運算子

  • 使用 new 關鍵字

讓我們逐一檢視上述方法。

使用 & 運算子

& 運算子可用於獲取結構體變數的指標。

emp := employee{name: "Sam", age: 31, salary: 2000}
empP := &emp

結構體指標也可以直接建立

empP := &employee{name: "Sam", age: 31, salary: 2000}

讓我們看一個程式

package main

import "fmt"

type employee struct {
    name   string
    age    int
    salary int
}

func main() {
    emp := employee{name: "Sam", age: 31, salary: 2000}
    empP := &emp
    fmt.Printf("Emp: %+v\n", empP)
    empP = &employee{name: "John", age: 30, salary: 3000}
    fmt.Printf("Emp: %+v\n", empP)
}

輸出

Emp: &{name:Sam age:31 salary:2000}
Emp: &{name:John age:30 salary:3000}

使用 new 關鍵字

使用 new() 關鍵字將:

  • 建立結構體

  • 將所有欄位初始化為其型別的零預設值

  • 返回指向新建立結構體的指標

這將返回一個指標

empP := new(employee)

指標地址可以使用%p格式修飾符列印

fmt.Printf("Emp Pointer: %p\n", empP)

還要注意,*運算子可用於解引用指標,這意味著獲取儲存在指標中的地址的值。

fmt.Printf("Emp Value: %+v\n", *empP)

它將列印

Emp Value: {name: age:0 salary:0}

當不使用解引用指標而使用格式識別符號%+v時,將在結構體之前附加&符號,表示這是一個指標。

fmt.Printf("Emp Value: %+v\n", empP)

它將列印

Emp Value: &{name: age:0 salary:0}

讓我們看看完整程式以說明上述要點

package main

import "fmt"

type employee struct {
    name   string
    age    int
    salary int
}

func main() {
    empP := new(employee)
    fmt.Printf("Emp Pointer Address: %p\n", empP)
    fmt.Printf("Emp Pointer: %+v\n", empP)
    fmt.Printf("Emp Value: %+v\n", *empP)
}

輸出

Emp Pointer: 0xc0000a6020
Emp Value: &{name: age:0 salary:0}
Emp Value: {name: age:0 salary:0}

列印的指標地址在你的機器上會有所不同。

在 Go(Golang)中實現介面時,指標與值接收器的方法

來源:golangbyexample.com/pointer-vs-value-receiver-method-golang/

一個型別的方法可以有指標接收器或值接收器。當該型別實現介面時,指標與值接收器有一些注意事項

  • 如果一個型別使用值接收器實現了介面的所有方法,那麼該型別的值和指標都可以用於賦值給該介面變數或傳遞給接受該介面作為引數的函式。

  • 如果一個型別使用指標接收器實現了介面的所有方法,那麼只有該型別的指標可以用於賦值給該介面變數或傳遞給接受該介面作為引數的函式。

示例以演示上述第一個要點

假設我們有一個介面動物如下

type animal interface {
    breathe()
    walk()
}

我們也有一個獅子結構體實現了這個動物介面

type lion struct {
    age int
}

程式碼

package main

import "fmt"

type animal interface {
    breathe()
    walk()
}

type lion struct {
    age int
}

func (l lion) breathe() {
    fmt.Println("Lion breathes")
}

func (l lion) walk() {
    fmt.Println("Lion walk")
}

func main() {
    var a animal

    a = lion{age: 10}
    a.breathe()
    a.walk()

    a = &lion{age: 5}
    a.breathe()
    a.walk()
}

輸出

Lion breathes
Lion walk
Lion breathes
Lion walk

獅子結構體使用值接收器實現了動物介面。因此它適用於獅子型別的兩個變數以及指向獅子型別變數的指標。

這樣可以

 a = lion{age: 10}

以及這個

a = &lion{age: 5}

示例以演示上述第二個要點。獅子結構體使用指標接收器實現了動物介面。因此它僅適用於指向獅子型別變數的指標。

所以這樣可以

a = &lion{age: 5}

但這會導致編譯錯誤

a = lion{age: 10}
cannot use lion literal (type lion) as type animal in assignment:
        lion does not implement animal (breathe method has pointer receiver)

檢視完整工作程式碼

package main

import "fmt"

type animal interface {
	breathe()
	walk()
}

type lion struct {
	age int
}

func (l *lion) breathe() {
	fmt.Println("Lion breathes")
}

func (l *lion) walk() {
	fmt.Println("Lion walk")
}

func main() {
	var a animal

	//a = lion{age: 10}
	a.breathe()
	a.walk()

	a = &lion{age: 5}
	a.breathe()
	a.walk()
}

取消註釋這一行

a = lion{age: 10}

這將導致編譯錯誤

cannot use lion literal (type lion) as type animal in assignment:
        lion does not implement animal (breathe method has pointer receiver)

在 Go (Golang) 中的二叉樹後序遍歷

來源:golangbyexample.com/postorder-binary-tree-golang/

目錄

  • 概述

  • 程式

概述

在二叉樹的後序遍歷中,我們遵循以下順序

  • 訪問左子樹

  • 訪問右子樹

  • 訪問根節點

例如,假設我們有如下二叉樹

![](https://gitee.com/OpenDocCN/geekdoc-golang-zh/raw/master/docs/go-exam-2023/img/9a9347838908483552b24df3dc54cd38.png)

然後後序遍歷結果為

[4 2 5 6 3 1]

程式

下面是相應的程式

package main

import (
	"fmt"
)

type TreeNode struct {
	Val   int
	Left  *TreeNode
	Right *TreeNode
}

func postorderTraversal(root *TreeNode) []int {
	if root == nil {
		return nil
	}

	left := postorderTraversal(root.Left)
	right := postorderTraversal(root.Right)

	output := make([]int, 0)

	output = append(output, left...)

	output = append(output, right...)

	output = append(output, root.Val)
	return output
}

func main() {
	root := TreeNode{Val: 1}
	root.Left = &TreeNode{Val: 2}
	root.Left.Left = &TreeNode{Val: 4}
	root.Right = &TreeNode{Val: 3}
	root.Right.Left = &TreeNode{Val: 5}
	root.Right.Right = &TreeNode{Val: 6}

	output := postorderTraversal(&root)
	fmt.Println(output)

}

輸出

[4 2 5 6 3 1]

注意: 請檢視我們的 Golang 高階教程。本系列教程內容詳盡,我們努力覆蓋所有概念及示例。本教程適合希望掌握 Golang 知識的使用者 – Golang 高階教程

如果你對了解所有設計模式在 Golang 中的實現感興趣,那這篇文章正適合你 – 所有設計模式 Golang

在 Go 語言中,二叉樹的先序遍歷

來源:golangbyexample.com/preorder-binary-tree-golang/

目錄

  • 概述

  • 程式

概述

在二叉樹的先序遍歷中,我們遵循以下順序

  • 訪問根節點

  • 訪問左子樹

  • 訪問右子樹

例如,我們有以下二叉樹

![](https://gitee.com/OpenDocCN/geekdoc-golang-zh/raw/master/docs/go-exam-2023/img/9a9347838908483552b24df3dc54cd38.png)

然後先序遍歷為

[1 2 4 3 5 6]

程式

這是相應的程式

package main

import (
	"fmt"
)

type TreeNode struct {
	Val   int
	Left  *TreeNode
	Right *TreeNode
}

func preorderTraversal(root *TreeNode) []int {
	if root == nil {
		return nil
	}

	left := preorderTraversal(root.Left)
	right := preorderTraversal(root.Right)

	output := make([]int, 0)

	output = append(output, root.Val)
	output = append(output, left...)
	output = append(output, right...)
	return output
}

func main() {
	root := TreeNode{Val: 1}
	root.Left = &TreeNode{Val: 2}
	root.Left.Left = &TreeNode{Val: 4}
	root.Right = &TreeNode{Val: 3}
	root.Right.Left = &TreeNode{Val: 5}
	root.Right.Right = &TreeNode{Val: 6}

	output := preorderTraversal(&root)
	fmt.Println(output)

}

輸出

[1 2 4 3 5 6]

注意: 檢視我們的 Golang 高階教程。本系列教程內容詳盡,我們盡力覆蓋所有概念並附有示例。本教程適合希望深入掌握 Golang 的使用者 – Golang 高階教程

如果你有興趣瞭解如何在 Golang 中實現所有設計模式。如果是的話,這篇文章就是為你準備的 – 所有設計模式 Golang

在 Go (Golang)中美觀地列印結構體變數

來源:golangbyexample.com/print-struct-variables-golang/

注意: 如果你對學習 Golang 感興趣,我們有一個全面的 Golang 教程系列。請檢視一下 – Golang 全面教程系列。現在讓我們來看當前的教程。以下是目錄。

目錄

  • 概述

  • 使用 fmt 包

  • 以 JSON 格式列印結構體

概述

有兩種方法可以列印所有結構體變數,包括所有的鍵和值。

  • 使用fmt

  • 使用json/encoding包以 JSON 格式列印結構體。這也允許美觀地列印結構體。

假設我們有一個員工結構體,如下所示:

type employee struct {
    name   string
    age    int
    salary int
}

讓我們看看兩種列印員工結構體例項的方法。

使用 fmt 包

fmt.Printf()函式可以用來列印一個結構體。可以使用不同的格式識別符號以不同的方式列印結構體。讓我們看看如何使用不同的格式識別符號以不同的格式列印結構體。

首先讓我們建立一個員工的例項。

emp := employee{name: "Sam", age: 31, salary: 2000}
  • %v – 僅列印值,欄位名不會被列印。這是列印結構體的預設方式。例如:
fmt.Printf("%v", emp)  -  {Sam 31 2000}
  • %+v – 它將列印欄位和值。例如:
fmt.Printf("%+v", emp) - {name:Sam age:31 salary:2000}
  • %#v – 它將列印結構體名稱,以及欄位和值。例如:
fmt.Printf("%#v", emp) - main.employee{name:"Sam", age:31, salary:2000}

fmt.Println()函式也可以用來列印一個結構體。由於fmt.Println()函式的預設值是%v,因此輸出將與使用%v 的fmt.Printf()相同。

fmt.Println(emp) - {Sam 31 2000}

我們也來看一個工作程式。

package main

import "fmt"

type employee struct {
    name   string
    age    int
    salary int
}

func main() {
    emp := employee{name: "Sam", age: 31, salary: 2000}
    fmt.Printf("Emp: %v\n", emp)
    fmt.Printf("Emp: %+v\n", emp)
    fmt.Printf("Emp: %#v\n", emp)
    fmt.Println(emp)
}

輸出

Emp: {Sam 31 2000}
Emp: {name:Sam age:31 salary:2000}
Emp: main.employee{name:"Sam", age:31, salary:2000}
{Sam 31 2000}

以 JSON 格式列印結構體

第二種方法是以 JSON 格式列印結構體。encoding/json包的MarshalMarshalIndent函式可以用來以 JSON 格式列印結構體。這裡是區別:

  • Marshal – 以下是Marshal函式的簽名。該函式透過遞迴遍歷值返回v的 JSON 編碼。
Marshal(v interface{}) ([]byte, error)
  • MarshalIndent– 以下是MarshalIndent函式的簽名。它類似於Marshal函式,但應用了縮排以格式化輸出。因此,可以用來美觀地列印一個結構體。
MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)

需要注意的是,MarshalMarshalIndent函式只能訪問結構體的匯出欄位,這意味著只有以大寫字母開頭的欄位才能被訪問和編碼為 JSON 格式。

package main

import (
    "encoding/json"
    "fmt"
    "log"
)

type employee struct {
    Name   string
    Age    int
    salary int
}

func main() {
    emp := employee{Name: "Sam", Age: 31, salary: 2000}
    //Marshal
    empJSON, err := json.Marshal(emp)
    if err != nil {
        log.Fatalf(err.Error())
    }
    fmt.Printf("Marshal funnction output %s\n", string(empJSON))

    //MarshalIndent
    empJSON, err = json.MarshalIndent(emp, "", "  ")
    if err != nil {
        log.Fatalf(err.Error())
    }
    fmt.Printf("MarshalIndent funnction output %s\n", string(empJSON))
}

輸出:

Marshal funnction output {"Name":"Sam","Age":31}

MarshalIndent funnction output {
  "Name": "Sam",
  "Age": 31
}

工資欄位未在輸出中列印,因為它以小寫字母開頭且未匯出。Marshal函式的輸出未經過格式化,而MarshalIndent函式的輸出是格式化的。

golang 還允許透過使用結構體元欄位使 JSON 編碼結構體的鍵名不同。我們先來理解一下什麼是結構體元欄位。在 Go 中,結構體也允許為其欄位新增後設資料。這些元欄位可以用於編碼解碼成不同形式,對結構體欄位進行某些形式的驗證等。因此,基本上任何元資訊都可以與結構體的欄位儲存,並可以被任何包或庫用於不同的目的。

以下是附加後設資料的格式。後設資料是一個字串文字,即它被反引號包圍。

type strutName struct{
   fieldName type `key:value key2:value2`
}

現在針對我們的用例,我們將為員工結構體新增 JSON 標籤,如下所示。Marshal 函式將使用標籤中指定的鍵名。

type employee struct {
    Name   string `json:"n"`
    Age    int    `json:"a"`
    Salary int    `json:"s"`
}

讓我們看看完整的程式

package main

import (
    "encoding/json"
    "fmt"
    "log"
)

type employee struct {
    Name   string `json:"n"`
    Age    int    `json:"a"`
    Salary int    `json:"s"`
}

func main() {
    emp := employee{Name: "Sam", Age: 31, Salary: 2000}
    //Converting to jsonn
    empJSON, err := json.MarshalIndent(emp, '', '  ')
    if err != nil {
        log.Fatalf(err.Error())
    }
    fmt.Println(string(empJSON))
}

輸出:

{
  "n": "Sam",
  "a": 31,
  "s": 2000
}

輸出中的鍵名與 JSON 元標籤中指定的相同。

列印巢狀結構體

即使結構體包含另一個結構體,也可以使用上述討論的方法列印相同內容。

package main

import (
    "encoding/json"
    "fmt"
    "log"
)

type address struct {
    City    string `json:"city"`
    Country string `json:"country"`
}

type employee struct {
    Name    string  `json:"name"`
    Age     int     `json:"age"`
    Salary  int     `json:"salary"`
    Address address `json:"address"`
}

func main() {
    address := address{City: "some_city", Country: "some_country"}
    emp := employee{Name: "Sam", Age: 31, Salary: 2000, Address: address}
    //Converting to json
    empJSON, err := json.MarshalIndent(emp, "", "  ")
    if err != nil {
        log.Fatalf(err.Error())
    }
    fmt.Printf("MarshalIndent funnction output\n %s\n", string(empJSON))
}

輸出

MarshalIndent function output
 {
  "name": "Sam",
  "age": 31,
  "salary": 2000,
  "address": {
    "city": "some_city",
    "country": "some_country"
  }
}

這就是關於列印結構體的全部內容。希望你喜歡這篇文章。請在評論中分享反饋。

在 Go (Golang)中列印所有二叉樹路徑

來源:golangbyexample.com/print-all-binary-tree-paths-go/

目錄

  • 概述

  • 程式

概述

給定一棵樹的根節點。目標是列印所有從根節點開始的樹路徑。

示例

![](https://gitee.com/OpenDocCN/geekdoc-golang-zh/raw/master/docs/go-exam-2023/img/9a9347838908483552b24df3dc54cd38.png)

輸出:

1->2->4
1->3->5
1->3->6

程式

下面是相同程式的程式碼

package main

import (
	"fmt"
	"strconv"
)

type TreeNode struct {
	Val   int
	Left  *TreeNode
	Right *TreeNode
}

func binaryTreePaths(root *TreeNode) []string {
	output := make([]string, 0)

	binaryTreePathsUtil(root, "", &output)
	return output
}

func binaryTreePathsUtil(root *TreeNode, curr string, output *[]string) {
	if root == nil {
		return
	}

	valString := strconv.Itoa(root.Val)
	if curr == "" {
		curr = valString
	} else {
		curr = curr + "->" + valString
	}
	if root.Left == nil && root.Right == nil {
		*output = append(*output, curr)
		return
	}

	binaryTreePathsUtil(root.Left, curr, output)
	binaryTreePathsUtil(root.Right, curr, output)

}

func main() {
	root := TreeNode{Val: 1}
	root.Left = &TreeNode{Val: 2}
	root.Left.Left = &TreeNode{Val: 4}
	root.Right = &TreeNode{Val: 3}
	root.Right.Left = &TreeNode{Val: 5}
	root.Right.Right = &TreeNode{Val: 6}

	output := binaryTreePaths(&root)
	fmt.Println(output)
}

輸出:

[1->2->4 1->3->5 1->3->6]

注意: 請檢視我們的 Golang 高階教程。本系列的教程內容詳盡,我們嘗試用示例覆蓋所有概念。這個教程是為了那些希望獲得 Golang 專業知識和紮實理解的人準備的 – Golang 高階教程

如果你有興趣瞭解如何在 Golang 中實現所有設計模式。如果是的話,那麼這篇文章就是為你準備的 – 所有設計模式 Golang

另外,請檢視我們的系統設計教程系列 – 系統設計教程系列

在 Go(Golang)中列印陣列或切片元素

來源:golangbyexample.com/print-an-array-or-slice-elements-golang/

目錄

概述

  • 列印切片

  • 列印陣列

概述

列印陣列或切片的方式是相同的。讓我們逐個檢視

列印切片

一起列印切片元素

使用

  • fmt.Println 和

  • fmt.Printf

package main
import "fmt"
func main() {
    numbers := []int{1, 2, 3}
    fmt.Println(numbers)
    fmt.Printf("Numbers: %v", numbers)
}

輸出

[1 2 3]
Numbers: [1 2 3]

列印單個切片元素

使用

  • for-range 迴圈
package main
import "fmt"
func main() {
    numbers := []int{1, 2, 3}
    for _, num := range numbers {
        fmt.Println(num)
    }
}

輸出

1
2
3

列印陣列

一起列印陣列元素

使用

  • fmt.Println 和

  • fmt.Printf

package main
import "fmt"
func main() {
    numbers := [3]int{1, 2, 3}
    fmt.Println(numbers)
    fmt.Printf("Numbers: %v", numbers)
}

輸出

[1 2 3]
Numbers: [1 2 3]

列印單個陣列元素

使用

  • for-range 迴圈
package main
import "fmt"
func main() {
    numbers := [3]int{1, 2, 3}
    for _, num := range numbers {
        fmt.Println(num)
    }
}

輸出

1
2
3
  • 陣列* 切片*

在 Go(Golang)中列印/輸出刪除線文字

來源:golangbyexample.com/print-text-crossout-golang/

目錄

  • 概述

  • 程式

概述

我們可以使用 faith 包來實現相同的功能

github.com/fatih/color

程式

package main

import (
	"github.com/fatih/color"
)

func main() {
	whilte := color.New(color.FgWhite)
	boldWhite := whilte.Add(color.CrossedOut)
	boldWhite.Println("This will print text in crossout")
}

輸出

![](https://gitee.com/OpenDocCN/geekdoc-golang-zh/raw/master/docs/go-exam-2023/img/12f7cb1df58a4a34263e258c0b035cd1.png)

注意: 檢視我們的 Golang 高階教程。本系列教程內容詳盡,涵蓋了所有概念和例項。此教程適合那些希望獲得 Golang 專業知識和紮實理解的人 – Golang 高階教程

如果你有興趣瞭解所有設計模式如何在 Golang 中實現。如果是的話,這篇文章就是為你準備的 –所有設計模式 Golang

在 Go(Golang)中以粗體列印/輸出文字

來源:golangbyexample.com/print-text-bold-go/

目錄

  • 概述

  • 程式

概述

我們可以使用 faith 包來實現相同的功能

github.com/fatih/color

程式

package main

import "github.com/fatih/color"

func main() {
    whilte := color.New(color.FgWhite)
    boldWhite := whilte.Add(color.Bold)
    boldWhite.Println("This will print text in bold white.")
}

輸出

![](https://gitee.com/OpenDocCN/geekdoc-golang-zh/raw/master/docs/go-exam-2023/img/a60bfb1e239c23e59b81528874806bee.png)* go* golang*

在控制檯中以顏色輸出文字

來源:golangbyexample.com/print-output-text-color-console/

注意: 如果你有興趣學習 Golang,我們有一個全面的 Golang 教程系列。請檢視一下 – Golang 全面教程系列。現在讓我們看看當前的教程。下面是目錄。

目錄

  • 概述

  • 程式碼

概述

ANSI 轉義碼可以用來在控制檯中輸出彩色文字。請注意

  • 在 MAC/Linux 系統終端支援 ANSI 轉義碼

  • Windows 命令提示符不支援它。在 Windows 上,你可以安裝 Cygwin。ANSI 轉義碼在那個環境下可以工作。

另外需要提到的是,在下面的程式碼中,我們在列印後使用了 colorReset。如果不使用它,顏色效果將保持,不會被清除。去掉下面程式碼中的 colorReset,它將以青色顯示文字“next”

下面是用 Golang 實現相同功能的程式碼。

程式碼

package main

import (
    "fmt"
)

func main() {
    colorReset := "\0330m"

    colorRed := "\033[31m"
    colorGreen := "\033[32m"
    colorYellow := "\033[33m"
    colorBlue := "\033[34m"
    colorPurple := "\033[35m"
    colorCyan := "\033[36m"
    colorWhite := "\033[37m"

    fmt.Println(string(colorRed), "test")
    fmt.Println(string(colorGreen), "test")
    fmt.Println(string(colorYellow), "test")
    fmt.Println(string(colorBlue), "test")
    fmt.Println(string(colorPurple), "test")
    fmt.Println(string(colorWhite), "test")
    fmt.Println(string(colorCyan), "test", string(colorReset))
    fmt.Println("next")
}

輸出:

在我的 Mac 機器上

![* ansi* color* console* go* output* print* terminal* text*

在 Go (Golang) 中以斜體列印/輸出文字

來源:golangbyexample.com/print-italic-text-golang/

目錄

  • 概述

  • 程式

概述

我們可以使用 faith 包來實現相同的功能。

github.com/fatih/color

程式

package main

import (
	"fmt"

	"github.com/fatih/color"
)

func main() {
	whilte := color.New(color.FgWhite)
	boldWhite := whilte.Add(color.Italic)
	boldWhite.Println("This will print text in italic white.")
}

輸出

![](https://gitee.com/OpenDocCN/geekdoc-golang-zh/raw/master/docs/go-exam-2023/img/57e8598f752454129e75169d77836aff.png)* 前往* Golang*

在 Go(Golang)中列印/輸出下劃線文字

來源:golangbyexample.com/print-text-underline-golang/

目錄

  • 概述

  • 程式

概述

我們可以使用 faith 包來實現相同的功能。

github.com/fatih/color

程式

package main

import (
	"github.com/fatih/color"
)

func main() {
	whilte := color.New(color.FgWhite)
	boldWhite := whilte.Add(color.Underline)
	boldWhite.Println("This will print text in underline")
}

輸出

![](https://gitee.com/OpenDocCN/geekdoc-golang-zh/raw/master/docs/go-exam-2023/img/2eb07d779bd64ce2bab39e19b5d1c89e.png)

注意: 請檢視我們的 Golang 高階教程。本系列教程詳細闡述了所有概念,並提供了示例。本教程適合那些希望獲得專業知識和對 golang 有深入理解的人——Golang 高階教程

如果你有興趣瞭解所有設計模式在 Golang 中的實現方法。如果是的話,那麼這篇文章就是為你準備的——所有設計模式 Golang

在 Go (Golang) 中列印/輸出帶背景的文字

來源:golangbyexample.com/print-text-background-golang/

目錄

  • 概述

  • 程式

概述

我們可以使用 faith 包來實現相同的功能

github.com/fatih/color

程式

在下面的程式中,我們將文字以白色字型列印在紅色背景上

package main

import (
	"fmt"

	"github.com/fatih/color"
)

func main() {
	whilte := color.New(color.FgWhite)
	boldWhite := whilte.Add(color.BgRed)
	boldWhite.Print("This will print white text with red background")
	fmt.Println()

	boldWhite = whilte.Add(color.BgGreen)
	boldWhite.Print("This will print white text with green background")
	fmt.Println()

}

輸出

![](https://gitee.com/OpenDocCN/geekdoc-golang-zh/raw/master/docs/go-exam-2023/img/ea7fb62b0e03bad6bfa5fc3f9a2cb0e0.png)

其他背景顏色選項有

github.com/fatih/color/blob/master/color.go

// Background text colors
const (
    BgBlack Attribute = iota + 40
    BgRed
    BgGreen
    BgYellow
    BgBlue
    BgMagenta
    BgCyan
    BgWhite
)
// Background Hi-Intensity text colors
const (
    BgHiBlack Attribute = iota + 100
    BgHiRed
    BgHiGreen
    BgHiYellow
    BgHiBlue
    BgHiMagenta
    BgHiCyan
    BgHiWhite
)

注意: 請檢視我們的 Golang 高階教程。本系列的教程內容詳盡,我們嘗試涵蓋所有概念並附有示例。本教程適合那些希望獲得專業知識和紮實理解 Golang 的人 – Golang 高階教程

如果你有興趣瞭解所有設計模式如何在 Golang 中實現。如果是的話,這篇文章就是為你準備的 – 所有設計模式 Golang

在 Go (Golang) 中用雙引號列印字串

來源:golangbyexample.com/print-double-quotes-string-golang/

目錄

  • 概述**

  • 雙引號字串的程式

概述

反斜槓是跳脫字元。要列印包含字面雙引號的任何字串,我們需要在字串用雙引號括起來時轉義這兩個引號。然而,用反引號括起來的字串是原始字面字串,不會遵循任何轉義。因此,反引號也可以用來列印包含字面雙引號的字串。

雙引號字串的程式

package main

import "fmt"

func main() {
	fmt.Println("\"test\"")
}

輸出

"test"

注意到我們在那個字串中轉義了單引號和雙引號

反引號的程式

package main
import "fmt"
func main() {
    fmt.Println(`"test"`)
}

輸出

"test"

在 Go 語言中列印給定字元的下一個或上一個字元。

來源:golangbyexample.com/next-previous-char-golang/

目錄

  • 概述

  • 程式

概述

透過對當前字元進行加 1 和減 1,我們可以得到下一個和上一個字元。

程式

這裡是相應的程式。

package main

import "fmt"

func main() {
	var curr byte
	curr = 'b'

	fmt.Println(string(curr + 1))
	fmt.Println(string(curr - 1))
} 

輸出:

c
a

注意: 請檢視我們的 Golang 高階教程。本系列教程內容豐富,我們努力涵蓋所有概念並附有示例。這個教程適合那些希望獲得專業知識和紮實理解 Golang 的人 – Golang 高階教程

如果你有興趣瞭解所有設計模式如何在 Golang 中實現。如果是的話,這篇文章就是為你準備的 – 所有設計模式 Golang

在 Go(Golang)中列印介面的基礎型別和值

來源:golangbyexample.com/print-type-value-interface-golang/

目錄

  • 概述

  • 程式碼

概述

Golang 提供格式識別符號來列印介面值所表示的基礎型別和基礎值。

  • %T 可以用來列印介面值的具體型別

  • %v 可以用來列印介面值的具體值。

假設我們有一個介面動物如下

type animal interface {
    breathe()
    walk()
}

我們還有一個獅子結構體實現了這個動物介面。

type lion struct {
    age int
}

程式碼

package main

import "fmt"

type animal interface {
    breathe()
    walk()
}

type lion struct {
    age int
}

func (l lion) breathe() {
    fmt.Println("Lion breathes")
}

func (l lion) walk() {
    fmt.Println("Lion walk")
}

func main() {
    var a animal
    a = lion{age: 10}
    fmt.Printf("Underlying Type: %T\n", a)
    fmt.Printf("Underlying Value: %v\n", a)
}

輸出

Concrete Type: main.lion
Concrete Value: {10}

Go (Golang)中的 Println vs Print vs Printf

來源:golangbyexample.com/println-printf-print-golang/

目錄

  • 概述

  • 關於 Println 函式

  • 關於 Print 函式

  • 關於 Printf 函式

    • 列印字串變數

    • 列印整數

    • 列印結構體

概述

PrintlnPrintPrintffmt包中定義,用於格式化字串並寫入標準輸出

golang.org/pkg/fmt/

它們之間的基本區別是

  • Println使用預設格式說明符格式化字串,並在字串後新增新行

  • Print使用預設格式說明符格式化字串,但在字串後不新增新行

  • Printf使用自定義說明符格式化字串。它也不會新增新行

讓我們詳細瞭解每個例子

關於 Println 函式

以下是Println的函式原型

func Println(a ...interface{}) (n int, err error)

Println接受可變數量的引數,每個引數都是一個空介面。它返回列印的字元數和任何發生的錯誤。由於引數型別是空介面,我們可以傳遞任何資料型別。我們可以傳遞字串、整型、浮點型、結構體或任何其他資料型別。每個傳遞給Println函式的引數都根據該引數型別的預設格式說明符進行格式化。例如,結構體將根據以下說明符進行格式化

%v

此格式說明符僅列印結構體中的值部分。讓我們看一個例子

package main
import "fmt"
type employee struct {
    Name string
    Age  int
}
func main() {
    name := "John"
    age := 21
    fmt.Println("Name is: ", name)
    fmt.Println("Age is: ", age)
    e := employee{
        Name: name,
        Age:  age,
    }
    fmt.Println(e)
    fmt.Println("a", 12, "b", 12.0)

    bytesPrinted, err := fmt.Println("Name is: ", name)
    if err != nil {
	log.Fatalln("Error occured", err)
    }
    fmt.Println(bytesPrinted)
}

輸出

Name is: John
Age is: 21
{John 21}
a 12 b 12
Name is: John
14

關於Println函式的一些重要注意事項

  • 它在末尾附加新行。這就是為什麼每個輸出都在不同的行上

  • 輸出中的每個引數將用空格分隔。這就是為什麼

fmt.Println("Name is: ", name)

列印

Name is: John

兩個引數之間會自動引入空格。

  • 它返回列印的字元數或任何發生的錯誤
bytesPrinted, err := fmt.Println("Name is: ", name)
if err != nil {
    log.Fatalln("Error occured", err)
}
fmt.Println(bytesPrinted)

將輸出如下

Name is: John
14

bytesPrinted的數量為 14,因為輸出了 14 個字元

關於 Print 函式

Print的函式原型

func Print(a ...interface{}) (n int, err error)

Print函式與Println函式完全相同,除了兩個區別

  • 它在末尾不附加新行。我們需要使用新行識別符號來新增新行“\n”。

  • 只有當兩個運算元都不是字串時,才會在引數之間新增空格

讓我們看一個相同的例子

package main

import "fmt"

type employee struct {
	Name string
	Age  int
}

func main() {
	name := "John"
	age := 21
	fmt.Print("Name is:", name, "\n")
	fmt.Print("Age is:", age, "\n")

	e := employee{
		Name: name,
		Age:  age,
	}

	fmt.Print(e, "\n")

	fmt.Print("a", 12, "b", 12.0, "\n")

	fmt.Print(12, 12.0, "\n")

        bytesPrinted, err := fmt.Print("Name is: ", name, "\n")
	if err != nil {
		log.Fatalln("Error occured", err)
	}
	fmt.Print(bytesPrinted)
}

輸出

Name is:John
Age is:21
{John 21}
a12b12
12 12
Name is: John
14

關於Print函式的一些重要注意事項

  • 它在末尾不會附加新行。這就是為什麼需要使用“\n”來新增新行。

  • 只有在每個引數都是非字串時,它才會在兩個引數之間新增空格。這就是原因

fmt.Print(12, 12.0, "\n")

列印

12 12

fmt.Print("a", 12, "b", 12.0, "\n")

列印

a12b12
  • 它還會返回列印的字元數以及任何可能發生的錯誤

關於 Printf 函式

Printf 的函式原型

func Printf(format string, a ...interface{}) (n int, err error)

Printf 也是一個可變引數函式,這意味著它可以有多個引數。關於它的引數列表有兩個重要點

  • 請注意,第一個引數是 格式模板 字串。

  • 下一個是可變數量的引數。這些引數可以是字串、整數、結構體或其他任何型別。出於上述相同的原因,這就是為什麼它是一個空介面

格式模板 字串包含需要格式化的實際字串加上一些格式化動詞。這些格式化動詞告訴後續引數在最終字串中將如何格式化。因此,基本上格式字串引數包含某些符號,這些符號會被後續引數替換。

例如

列印字串變數

  • %s 符號被使用

  • 示例

name := "John"
fmt.Printf("Name is: %s\n", name)

列印整數

  • %d 符號被使用

  • 示例

age := 21
fmt.Printf("Age is: %d\n", age)

列印結構體

例如,列印結構體有三種格式說明符。

  • %v – 它將只列印值。欄位名稱將不會被列印。這是使用 Println 列印結構體的預設方式

  • %+v – 它將列印欄位和值。

  • %#v – 它將列印結構體,同時列印欄位名稱和對應的值

這就是為什麼

fmt.Printf("Employee is %v\n", e)
fmt.Printf("Employee is %+v\n", e)
fmt.Printf("Employee is %#v\n", e)

分別列印如下

Employee is {John 21}
Employee is {Name:John Age:21}
Employee is main.employee{Name:"John", Age:21}

這與上述解釋一致。

此外,請注意,此函式返回列印的字元數以及任何可能發生的錯誤。與 Println 不同,它不會自動新增新行。你需要顯式新增 “\n”

下面是相應的工作程式

package main

import (
	"fmt"
	"log"
)

type employee struct {
	Name string
	Age  int
}

func main() {
	name := "John"
	age := 21

	fmt.Printf("Name is: %s\n", name)
	fmt.Printf("Age is: %d\n", age)

	fmt.Printf("Name: %s Age: %d\n", name, age)

	e := employee{
		Name: name,
		Age:  age,
	}

	fmt.Printf("Employee is %v\n", e)
	fmt.Printf("Employee is %+v\n", e)
	fmt.Printf("Employee is %#v\n", e)

	bytesPrinted, err := fmt.Printf("Name is: %s\n", name)
	if err != nil {
		log.Fatalln("Error occured", err)
	}
	fmt.Println(bytesPrinted)
}

輸出

Name is: John
Age is: 21
Name: John Age: 21
Employee is {John 21}
Employee is {Name:John Age:21}
Employee is main.employee{Name:"John", Age:21}
Name is: John
14

請注意下面的 Printf

fmt.Printf("Name: %s Age: %d\n", name, age)
  • %s 被替換為名稱。

  • %d 被替換為年齡。

所以基本上,格式字串引數中的符號或動詞會按順序被後續引數替換如果格式字串中的格式說明符數量與後續可變引數的數量不匹配,則格式說明符將按原樣列印。例如,在下面的程式碼中,我們有兩個格式說明符

*** %d

  • %s

而下一個可變數量的引數只有一個。因此,當我們列印時,它將按照原樣列印第二個格式說明符,並顯示“MISSING”作為警告

package main
import "fmt"
type employee struct {
    Name string
    Age  int
}
func main() {
    name := "John"
    fmt.Printf("Name is: %s %d\n", name)
}

輸出

Name is: John %!d(MISSING)

另外,檢視我們的 Golang 高階教程系列 – Golang 高階教程

Go(Golang)中在已排序陣列中進行二分查詢的程式

來源:golangbyexample.com/binary-search-sorted-array-golang/

目錄

  • 概述

  • 程式

概述

思路是在輸入陣列中對給定的目標元素進行二分查詢。如果目標元素存在,則輸出其索引。如果輸出元素不存在,則輸出 -1。

預期時間複雜度為 O(logn)

示例 1

Input: [1, 4, 5, 6]
Target Element: 4
Output: 1

Target element 4 is present at index 1

示例 2

Input: [1, 2, 3]
Target Element: 4
Output: -1

Target element 4 is present at index 1

程式

package main

import "fmt"

func search(nums []int, target int) int {
	return binarySearch(nums, 0, len(nums)-1, target)
}

func binarySearch(nums []int, start, end, target int) int {
	if start > end {
		return -1
	}
	mid := (start + end) / 2

	if nums[mid] == target {
		return mid
	}

	if target < nums[mid] {
		return binarySearch(nums, start, mid-1, target)
	} else {
		return binarySearch(nums, mid+1, end, target)
	}
}

func main() {
	index := search([]int{1, 4, 5, 6}, 4)
	fmt.Println(index)

	index = search([]int{1, 2, 3, 6}, 4)
	fmt.Println(index)
}

輸出

1
-1

注意: 請檢視我們的 Golang 高階教程。該系列教程詳盡,我們努力涵蓋所有概念並提供示例。本教程適合那些希望獲得 Golang 專業知識和紮實理解的人 - Golang 高階教程

如果你對如何在 Golang 中實現所有設計模式感興趣,那麼這篇文章就是為你準備的 - 所有設計模式 Golang

此外,請檢視我們的系統設計教程系列 - 系統設計教程系列

用 Go (Golang) 編寫的房屋搶劫問題程式

來源:golangbyexample.com/house-robber-golang/

目錄

概述

  • 程式 * * ## 概述

鄰里中有幾所房子。每所房子裡都有一些錢。這些房子被表示為一個陣列,陣列中的每個條目表示該房子裡的錢數。

例如,如果我們有以下陣列

[2, 3, 4, 2]

然後

  • 第一所房子裡有 2 元錢。

  • 第二所房子裡有 3 元錢。

  • 第三所房子裡有 4 元錢。

  • 第四所房子裡有 2 元錢。

搶劫者可以搶劫任意數量的房子,但他不能搶劫兩個相鄰的房子。例如,他可以在上述陣列的以下組合中進行搶劫。

  • 1 和 3

  • 1 和 4

  • 2 和 4

上述組合中沒有相鄰的房子。問題是確定哪個組合能為搶劫者帶來最大的收益。

例如,在上述情況下,第一個組合(1 和 3)將給他帶來最大的搶劫金額,即 2+4=6。因此,搶劫者可以在第一和第三所房子中搶劫,得到 2+4=6。

另一個例子

Input: [1, 6, 8, 2, 3, 4]
Output: 13

搶劫者可以在第一、第三和第六所房子中搶劫,得到 1+8+4=13。

這是一個動態規劃問題,因為它具有最優子結構。假設陣列的名稱是 money

  • dp[0] = money[0]

  • dp[1] = max(money[0], money[1])

  • dp[2] = max(money[0] + money[1], money[2])

  • dp[i] = dp[i] + max(dp[i-1], dp[i-2])

其中 dp[i] 表示如果包括第 i 所房子,搶劫者可以搶劫的金額。最後,我們返回 dp 陣列中的最大值。

程式

這裡是相同程式的程式碼。

package main

import "fmt"

func rob(nums []int) int {
	lenNums := len(nums)

	if lenNums == 0 {
		return 0
	}

	maxMoney := make([]int, lenNums)

	maxMoney[0] = nums[0]

	if lenNums > 1 {
		maxMoney[1] = nums[1]
	}

	if lenNums > 2 {
		maxMoney[2] = nums[2] + nums[0]
	}

	for i := 3; i < lenNums; i++ {
		if maxMoney[i-2] > maxMoney[i-3] {
			maxMoney[i] = nums[i] + maxMoney[i-2]
		} else {
			maxMoney[i] = nums[i] + maxMoney[i-3]
		}

	}

	max := 0
	for i := lenNums; i < lenNums; i++ {
		if maxMoney[i] > max {
			max = maxMoney[i]
		}
	}

	return max
}

func main() {
	output := rob([]int{2, 3, 4, 2})
	fmt.Println(output)

	output = rob([]int{1, 6, 8, 2, 3, 4})
	fmt.Println(output)

}

輸出

6
13

注意: 檢視我們的 Golang 高階教程。本系列教程內容詳盡,我們試圖用例子涵蓋所有概念。這個教程是為那些希望獲得專業知識和對 Golang 有深入理解的人準備的 – Golang 高階教程

如果你有興趣瞭解如何在 Golang 中實現所有設計模式。如果是的話,這篇文章就是為你準備的 – 所有設計模式 Golang

相關文章