揭开GO的语法:讽刺的旅程,穿越指针,结构,阵列,切片和地图|第2部分
#初学者 #编程 #go #microservices

在编程语言的世界中,Golang(也称为GO)以其简单,效率和鲁棒性而获得了巨大的知名度。 Golang凭借其独特的语法和功能强大的功能,为开发人员提供了一个多功能工具包,以构建可扩展和高性能应用程序。在此博客中,我们将深入研究Golang语法的一些关键方面,即指针,结构,阵列,切片和地图。让我们详细探讨每个主题:

指针:

指示器在golang中起着至关重要的作用,理解它们对于有效的记忆管理和操纵数据至关重要。本节将涵盖指针的基础知识,包括他们的声明,参考和剥夺。我们将探讨指针如何启用通过参考,并允许我们直接在内存中修改变量。

  • GO有指针。指针保存值的内存地址。
  • 类型 *t是指T值的指针。它的零值为零。
var p *int
  • &运营商生成了指向其操作数的指针。
i := 177
p = &i
  • *操作员表示指针的基本值。
fmt.Println(*p) // read i through the pointer p
*p = 146        // set i through the pointer p
  • 这被称为“删除”或“间接”。

考虑以下示例:

package main

import "fmt"

func main() {

    i, j := 146, 5256 // initialize the i,j 

    p := &i         // point to i
    fmt.Println(*p) // read i through the pointer
    *p = 177        // set i through the pointer
    fmt.Println(i)  // see the new value of i

    p = &j           // point to j
    *p = *p / 146   // divide j through the pointer
    fmt.Println(j) // see the new value of j
}

输出:

146
177
36

结构

在GO中,结构是一种复合数据类型,可通过将不同类型的值分组为单个实体来定义自己的自定义数据结构。它类似于面向对象的编程语言中的类,但没有方法。

要在GO中定义一个结构,您使用type关键字,然后使用结构的名称和一组包含在卷曲括号中的字段。这是一个例子:

  • 结构是字段的集合。
  • 使用点访问结构字段。
  • 可以通过结构指针访问结构字段。要访问struct x时,我们可以编写(*p).X。但是,该符号很麻烦,因此该语言允许我们仅编写p.X,而无需明确的解除。
  • 结构文字通过列出其字段的值表示新分配的结构值。您可以使用Name: syntax仅列出字段的子集。
package main

import "fmt"

// Structs  
type Vertex struct {
    X int
    Y int
}

var (
    v1 = Vertex{1, 2}  // has type Vertex
    v2 = Vertex{X: 1}  // Y:0 is implicit
    v3 = Vertex{}      // X:0 and Y:0
    q  = &Vertex{1, 2} // has type *Vertex
)

func main() {

    // Struct
    fmt.Println(Vertex{1, 2})

    // Struct Fields
    v := Vertex{1, 2}
    v.X = 4
    fmt.Println(v.X,v.Y)

    // Pointers to Struct
    f := Vertex{1, 2}
    p := &f
    p.X = 3
    fmt.Println(f)

    // Struct Literals
    fmt.Println(v1,v2, v3, q)   
}

输出:

{1 2}
4 2
{3 2}
{1 2} {1 0} {0 0} &{1 2}

数组

在GO中,数组是同一类型元素的固定尺寸集合。数组的大小在编译时指定,并且在运行时不能更改。这是声明和使用数组的示例:

package main

import "fmt"

func main() {
    var a [2]string
    a[0] = "Hello"
    a[1] = "World"
    fmt.Println(a[0], a[1])
    fmt.Println(a)

    primes := [6]int{2, 3, 5, 7, 11, 13}
    fmt.Println(primes)
}

输出:

Hello World
[Hello World]
[2 3 5 7 11 13]

切片

切片是动态的,可解的,并且比GO中的数组更灵活。它们建立在阵列之上,并提供其他功能。切片没有固定尺寸,可以根据需要生长或收缩。这是一个例子:

package main

import "fmt"

func main() {
    primes := []int{2, 3, 5, 7, 11, 13}

    primes = append(primes, 17)   // Appending elements to the slice
    primes = append(primes, 19)
    primes = append(primes, 23)

    fmt.Println(primes)            // Output: [2 3 5 7 11 13 17 19 23]
    fmt.Println(primes[1])         // Output: 3
    fmt.Println(len(primes))       // Output: 9 (length of the slice)

    // Slicing a slice to create a new slice
    newSlice := primes[1:5]         // Contains elements at indices 1 and 4 (inclusive:exclusive)
    fmt.Println(newSlice)           // Output: [3 5 7 11]
}

输出:

[2 3 5 7 11 13 17 19 23]
3
9
[3 5 7 11]

切片就像对数组的引用

  • 一个切片不存储任何数据,它只是描述了基础数组的一部分。
  • 更改切片的元素会修改其基础数组的相应元素。
  • 其他共享相同基础数组的切片将看到这些更改。
package main

import "fmt"

func main() {
    names := [4]string{
        "Ram",
        "Lakshman",
        "Parshuram",
        "Samvad",
    }
    fmt.Println(names)

    a := names[0:2] // 0 to 1
    b := names[1:3] // 1 to 2
    fmt.Println(a, b)

    b[0] = "Krishna"
    // Changing the element of a Slice b
    // Modifies the corresponding elements of its underlying array names
    fmt.Println(a, b)
    fmt.Println(names)
}

输出:

[Ram Lakshman Parshuram Samvad]
[Ram Lakshman] [Lakshman Parshuram]
[Ram Krishna] [Krishna Parshuram]
[Ram Krishna Parshuram Samvad]

切片文字

slice字面形式就像一个数字字面的,没有长度。

这是一个阵列文字:
[3]bool{true, true, false}

这会创建与上面相同的数组,然后构建一个引用它的切片:
[]bool{true, true, false}

package main

import "fmt"

func main() {
    q := []int{2, 3, 5, 7, 11, 13}
    fmt.Println(q)

    r := []bool{true, false, true, true, false, true}
    fmt.Println(r)

    s := []struct {
        i int
        b bool
    }{
        {2, true},
        {3, false},
        {5, true},
        {7, true},
        {11, false},
        {13, true},
    }
    fmt.Println(s)
}

输出:

[2 3 5 7 11 13]
[true false true true false true]
[{2 true} {3 false} {5 true} {7 true} {11 false} {13 true}]

切片默认值

  • 切片时,您可能会省略高或低界限以使用其默认值。
  • 对于高界的低结合和切片的长度为零。

阵列

var a [10]int

这些切片表达式等效:

a[0:10]
a[:10]
a[0:]
a[:]
package main

import "fmt"

func main() {
    s := []int{2, 3, 5, 7, 11, 13}

    s = s[1:4] // [3 5 7]
    fmt.Println(s)

    s = s[:2] // [3 5]
    fmt.Println(s)

    s = s[1:] // [5]
    fmt.Println(s)
}

输出:

[3 5 7]
[3 5]
[5]

切片长度和容量

  • 切片既有长度又具有容量。
  • 切片的长度是它包含的元素
  • 切片的容量是基础数组中的元素数量,从slice
  • 中的第一个元素计数。
  • 可以使用表达式len(s)cap(s)获得切片的长度和容量。
package main

import "fmt"

func main() {
    s := []int{2, 3, 5, 7, 11, 13}
    printSlice(s)

    // Slice the slice to give it zero length.
    s = s[:0]
    printSlice(s)

    // Extend its length.
    s = s[:4]
    printSlice(s)

    // Drop its first two values.
    s = s[2:]
    printSlice(s)
}

func printSlice(s []int) {
    fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}

输出:

len=6 cap=6 [2 3 5 7 11 13]
len=0 cap=6 []
len=4 cap=6 [2 3 5 7]
len=2 cap=4 [5 7]

零切片

  • 切片的零值为零。
  • 一个零切片的长度和容量为0,没有基础阵列。
package main

import "fmt"

func main() {
    var s []int
    fmt.Println(s, len(s), cap(s))
    if s == nil {
        fmt.Println("nil!")
    }
}

输出:

[] 0 0
nil!

用make创建切片

  • 可以使用内置的make函数创建切片;这就是您创建动态大小的数组的方式。
  • make函数分配了一个零数组,并返回一个指的切片,该切片指的是该数组:
a := make([]int, 5)  // len(a)=5
  • 要指定能力,请将第三个论点传递给make
b := make([]int, 0, 5) // len(b)=0, cap(b)=5

b = b[:cap(b)] // len(b)=5, cap(b)=5
b = b[1:]      // len(b)=4, cap(b)=4

这是一个示例:

package main

import "fmt"

func main() {
    a := make([]int, 5)
    printSlice("a", a)

    b := make([]int, 0, 5)
    printSlice("b", b)

    c := b[:2]
    printSlice("c", c)

    d := c[2:5]
    printSlice("d", d)
}

func printSlice(s string, x []int) {
    fmt.Printf("%s len=%d cap=%d %v\n",
        s, len(x), cap(x), x)
}

输出:

a len=5 cap=5 [0 0 0 0 0]
b len=0 cap=5 []
c len=2 cap=5 [0 0]
d len=3 cap=3 [0 0 0]

切片

切片可以包含任何类型,包括其他切片。

package main

import (
    "fmt"
    "strings"
)

func main() {
    // Create a tic-tac-toe board.
    board := [][]string{
        []string{"_", "_", "_"},
        []string{"_", "_", "_"},
        []string{"_", "_", "_"},
    }

    // The players take turns.
    board[0][0] = "X"
    board[2][2] = "O"
    board[1][2] = "X"
    board[1][0] = "O"
    board[0][2] = "X"

    for i := 0; i < len(board); i++ {
        fmt.Printf("%s\n", strings.Join(board[i], " "))
    }
}

输出:

X _ X
O _ X
_ _ O

附加到切片

要将元素附加到GO中,您可以使用内置的append()函数。 append()函数将切片和一个或多个元素作为参数,并带有附加元素的新切片。这是一个例子:

package main

import "fmt"

func main() {
    var s []string
    printSlice(s)

    // append works on nil slices.
    s = append(s, "Ram")
    printSlice(s)

    // The slice grows as needed.
    s = append(s, "Lakshman")
    printSlice(s)

    // We can add more than one element at a time.
    s = append(s, "Parshuram", "Samvad", "Krishna", "Balarama")
    printSlice(s)
}

func printSlice(s []string) {
    fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}

输出:

len=0 cap=0 []
len=1 cap=1 [Ram]
len=2 cap=2 [Ram Lakshman]
len=6 cap=6 [Ram Lakshman Parshuram Samvad Krishna Balarama]
  • for循环的范围形式在切片或地图上迭代。
  • 在切片上范围时,每次迭代都会返回两个值。第一个是索引,第二个是该索引处的元素的副本。
  • 您可以通过分配给_来跳过索引或值。
for i, _ := range pow
for _, value := range pow
  • 如果您只需要索引,则可以省略第二个变量。
for i := range pow

这是一个示例:

package main

import "fmt"

var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}

func main() {
    for i, v := range pow {
        fmt.Printf("2**%d = %d\n", i, v)
    }

    fmt.Println()

    pow := make([]int, 10)
    for i := range pow {
        pow[i] = 1 << uint(i) // == 2**i
    }
    for _, value := range pow {
        fmt.Printf("%d ", value)
    }
}

输出:

2**0 = 1
2**1 = 2
2**2 = 4
2**3 = 8
2**4 = 16
2**5 = 32
2**6 = 64
2**7 = 128

1 2 4 8 16 32 64 128 256 512 

地图

在GO中,地图是一个内置数据结构,代表了键值对的无序集合。它类似于其他编程语言中的字典或哈希表。地图提供了一种基于唯一键存储和检索值的有效方法。这是在GO中使用地图的一个示例:

package main

import "fmt"

type Vertex struct {
    Lat, Long float64
}

var m = map[string]Vertex{
    "Bell Labs": Vertex{
        40.68433, -74.39967,
    },
    "Google": Vertex{
        37.42202, -122.08408,
    },
}

func main() {
    fmt.Println(m["Bell Labs"])
    fmt.Println(m)  
}

输出:

{40.68433 -74.39967}
map[Bell Labs:{40.68433 -74.39967} Google:{37.42202 -122.08408}]

突变图

  • 在地图M中插入或更新元素:
m[key] = elem
  • 检索一个元素:
elem = m[key]
  • 删除一个元素:
delete(m, key)
  • 测试键是否存在两个值分配:
elem, ok = m[key]
  • 如果keym中,oktrue。如果没有,okfalse
  • 如果key不在地图中,则elem是地图元素类型的零值。
package main

import "fmt"

func main() {
    m := make(map[string]int)

    m["Answer"] = 177
    fmt.Println("The value:", m["Answer"])

    m["Answer"] = 146
    fmt.Println("The value:", m["Answer"])

    delete(m, "Answer")
    fmt.Println("The value:", m["Answer"])

    v, ok := m["Answer"]
    fmt.Println("The value:", v, "Present?", ok)
}

输出:

The value: 177
The value: 146
The value: 0
The value: 0 Present? false

功能值

  • 函数也是值。它们可以像其他价值观一样传递。
  • 函数值可以用作函数参数和返回值。
package main

import (
    "fmt"
    "math"
)

func compute(fn func(float64, float64) float64) float64 {
    return fn(3, 4)
}

func main() {
    hypot := func(x, y float64) float64 {
        return math.Sqrt(x*x + y*y)
    }
    fmt.Println(hypot(5, 12)) // sqrt(5^2 + 12^2) = sqrt(144 + 25) = 13

    fmt.Println(compute(hypot)) // sqrt(3^2 + 12^2) = sqrt(16+9) = 5

    fmt.Println(compute(math.Pow)) // pow(3,4) = 3^4 = 81
}

输出:

13
5
81
感谢您潜入博客的这一章!我们已经涵盖了很多基础,但是旅程并没有在这里结束。下一章等待着,准备带您进一步进入我们主题的深处。
要继续阅读和探索下一章,只需遵循以下链接:Link To Next Chapter

“解锁指针,结构,阵列,切片和地图的神秘世界 - 语法幽默地混乱和娱乐!