1. 下载安装Go

官网安装

访问 Go官网,根据操作系统下载并安装对应版本。

使用 Homebrew 安装 (Mac)

brew update
brew install golang

创建工作目录 (Workspace)

$HOME目录下创建一个Go工作目录:

mkdir $HOME/go_workspace

设置环境变量

编辑终端配置文件(例如 ~/.zshrc~/.bashrc),添加以下内容:

export GOROOT=/opt/homebrew/opt/go/libexec
export GOPATH=$HOME/go_workspace
export PATH=$PATH:$GOPATH/bin
export PATH=$PATH:$GOROOT/bin

通过 VSCode 扩展商店安装 Go 插件

  1. 打开 VSCode

    • 通过应用程序启动 VSCode 或者在终端中运行 code 命令(如果已安装 VSCode 命令行工具)。
  2. 进入扩展商店

    • 在左侧工具栏中,点击“扩展”图标(看起来像一个四方的拼图块),或者使用快捷键 Command+Shift+X 打开扩展商店。
  3. 搜索并安装 Go 插件

    • 在扩展商店的搜索框中,输入 “Go”,会看到由 Microsoft 提供的官方 Go 扩展。
    • 点击 “Install” 按钮进行安装。
  4. 自动安装工具

    • 安装插件后,当你首次打开一个 Go 项目或 Go 文件时,VSCode 通常会自动提示你安装所需的工具。如果有此提示,选择“安装所有工具”即可。
    • 如果没有提示,仍然可以使用 Command+Shift+P 打开命令面板,输入 Go: Install/Update Tools,然后选择并安装所有相关工具。

处理权限问题

如果在安装工具时遇到权限问题,如权限被拒绝(Permission Denied)或 /usr/local/pkg 目录不存在,可以使用以下命令修复权限问题:

sudo mkdir /usr/local/pkg
sudo chown -R $(whoami) /usr/local/pkg

这会创建所需的目录并将其权限设置为当前用户,从而避免权限问题。

2. Hello, World 示例

新建一个 hello.go 文件,内容如下:

package main

import "fmt"

func main() {
    fmt.Println("Hello, Go!")
}

运行程序:

go run hello.go

输出结果:

Hello, Go!

生成可执行文件:

go build hello.go
./hello

3. 语法基础

变量声明

Go 语言使用 var 关键字声明变量,语法为:

var identifier type

示例:

package main

import "fmt"

var a string = "Geek Go"  // 变量声明在函数外部

func main() {
    fmt.Println(a)  // 输出:Geek Go,函数体内可以放执行代码
}

简化版:

package main

import "fmt"

var a string = "Geek Go"  // 变量声明在函数外部

func main() {
    fmt.Println(a)  // 输出:Geek Go,函数体内可以放执行代码
    b, c := 1, 2  // 自动推断类型为 int
    fmt.Println(b, c)  // 输出:1 2
}

常量

常量使用 const 关键字来声明:

package main

import "fmt"

func main() {
    const pi = 3.14  // 声明常量
    fmt.Println("Pi的值是:", pi)
}

数组

数组声明并使用 for 循环遍历:

package main

import "fmt"

func main() {
    var arr [5]int = [5]int{1, 2, 3, 4, 5}
    
    // 使用 for 循环遍历数组
    for i := 0; i < len(arr); i++ {
        fmt.Println(arr[i])
    }
}

切片(Slice)

切片是长度可变的动态数组:

package main

import "fmt"

func main() {
    slice := []int{1, 2, 3, 4, 5}

    // 打印切片的一部分
    fmt.Println(slice[1:4])  // 输出:[2 3 4]

    // 向切片添加元素
    slice = append(slice, 6, 7)
    fmt.Println("追加后的切片:", slice)
}

Map(集合)

Map 是键值对的集合:

package main

import "fmt"

func main() {
    // 创建一个 Map
    cars := make(map[string]string)

    cars["A"] = "Audi"
    cars["B"] = "BMW"
    cars["T"] = "Tesla"

    // 遍历 Map
    for k, v := range cars {
        fmt.Printf("%s -> %s\n", k, v)
    }
}

控制结构

for 循环

Go 语言的 for 循环有三种常见形式:

基本形式:
package main

import "fmt"

func main() {
    for i := 0; i < 5; i++ {
        fmt.Println(i)
    }
}
类似 while 的形式:
package main

import "fmt"

func main() {
    i := 0
    for i < 5 {
        fmt.Println(i)
        i++
    }
}
无限循环:
package main

import "fmt"

func main() {
    for {
        fmt.Println("This will run forever")
        break // 使用 break 来防止无限循环
    }
}

条件判断

使用 if 语句进行条件判断:

package main

import "fmt"

func main() {
    x := 10
    if x > 10 {
        fmt.Println("x is greater than 10")
    } else {
        fmt.Println("x is 10 or less")
    }
}

函数与指针

函数

Go 中函数的定义和调用:

package main

import "fmt"

// 定义一个函数来求两个数的和
func add(x int, y int) int {
    return x + y
}

func main() {
    result := add(3, 4)
    fmt.Println("3 + 4 =", result)
}

指针

Go 中指针的使用:

package main

import "fmt"

func main() {
    x := 10
    var p *int
    p = &x  // 获取变量x的地址
    fmt.Println("x的值是:", x)
    fmt.Println("p指向的地址是:", p)
    fmt.Println("p指向的值是:", *p)  // 通过指针访问值
}

结构体与接口

结构体

结构体用于定义复杂数据类型:

package main

import "fmt"

// 定义一个结构体
type Book struct {
    title  string
    author string
    price  float32
}

func main() {
    // 创建结构体实例
    book := Book{title: "Go语言教程", author: "GeekHall", price: 9.9}
    
    // 输出结构体内容
    fmt.Println(book)
}

结构体作为函数参数:

package main

import "fmt"

// 定义一个结构体
type Book struct {
    title  string
    author string
    price  float32
}

// 定义一个打印书籍信息的函数
func printBook(book *Book) {
    fmt.Println("书名:", book.title)
    fmt.Println("作者:", book.author)
    fmt.Println("价格:", book.price)
}

func main() {
    book := Book{title: "Go语言教程", author: "GeekHall", price: 9.9}
    printBook(&book)  // 传递指针
}

接口

接口定义一组方法,任何实现了这些方法的类型都实现了该接口:

package main

import "fmt"

// 定义一个接口
type ICar interface {
    drive()
}

// 定义实现接口的结构体
type Tesla struct{}

func (t Tesla) drive() {
    fmt.Println("I am a Tesla")
}

func main() {
    var car ICar = Tesla{}
    car.drive()
}

并发

goroutine

使用 go 关键字启动一个新的并发任务:

package main

import "fmt"

func concurrentTest(s string) {
    for i := 0; i < 5; i++ {
        fmt.Println(s)
    }
}

func main() {
    go concurrentTest("Tesla")
    concurrentTest("Toyota")
}

通道(Channel)

通道用于在 goroutine 之间传递数据:

package main

import "fmt"

// 定义一个求和函数,结果通过通道传递
func sum(s []int, c chan int) {
    sum := 0
    for _, v := range s {
        sum += v
    }
    c <- sum  // 发送结果到通道
}

func main() {
    s := []int{7, 2, 8, -9, 4, 0}

    c := make(chan int)
    go sum(s[:len(s)/2], c)
    go sum(s[len(s)/2:], c)

    x, y := <-c, <-c  // 从通道接收结果
    fmt.Println(x, y, x+y)
}

错误处理

Go 提供了内置的 error 接口用于错误处理:

package main

import (
    "errors"
    "fmt"
)

func divide(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("division by zero")
    }
    return a / b, nil
}

func main() {
    result, err := divide(10, 0)
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println("Result:", result)
    }
}

5. 常用数据结构和算法

1. 队列(Queue)

队列是一种先进先出(FIFO)的数据结构,可以使用切片(Slice)来模拟队列的操作:

package main

import "fmt"

func main() {
    queue := []int{1, 2, 3}

    // 入队
    queue = append(queue, 4)
    fmt.Println("队列状态:", queue)

    // 出队
    head := queue[0]
    queue = queue[1:]
    fmt.Println("队头元素:", head)
    fmt.Println("出队后的队列:", queue)
}

2. 栈(Stack)

栈是一种后进先出(LIFO)的数据结构,也可以使用切片来模拟栈的操作:

package main

import "fmt"

func main() {
    stack := []int{}

    // 入栈
    stack = append(stack, 1)
    stack = append(stack, 2)
    stack = append(stack, 3)
    fmt.Println("栈状态:", stack)

    // 出栈
    top := stack[len(stack)-1]
    stack = stack[:len(stack)-1]
    fmt.Println("栈顶元素:", top)
    fmt.Println("出栈后的栈:", stack)
}

3. 链表(Linked List)

Go 标准库没有直接提供链表的实现,但可以使用 container/list 包来创建双向链表:

package main

import (
    "container/list"
    "fmt"
)

func main() {
    // 创建一个双向链表
    l := list.New()

    // 插入元素
    l.PushBack(1)
    l.PushBack(2)
    l.PushBack(3)

    // 遍历链表
    for e := l.Front(); e != nil; e = e.Next() {
        fmt.Println(e.Value)
    }

    // 删除第一个元素
    l.Remove(l.Front())
    fmt.Println("删除第一个元素后:")
    for e := l.Front(); e != nil; e = e.Next() {
        fmt.Println(e.Value)
    }
}

4. 哈希表(Map)

Go 内置 map 类型来实现哈希表(键值对映射):

package main

import "fmt"

func main() {
    // 创建一个 map
    fruits := map[string]string{
        "apple":  "red",
        "banana": "yellow",
        "grape":  "purple",
    }

    // 打印 map
    for fruit, color := range fruits {
        fmt.Printf("%s is %s\n", fruit, color)
    }

    // 查找元素
    color, found := fruits["apple"]
    if found {
        fmt.Println("The color of apple is", color)
    } else {
        fmt.Println("Apple not found!")
    }

    // 删除元素
    delete(fruits, "banana")
    fmt.Println("删除后:", fruits)
}

5. 集合(Set)

Go 没有直接提供 Set 类型,但可以使用 map 来模拟集合。map 的键代表集合的元素,值可以忽略或设置为 true

package main

import "fmt"

func main() {
    // 使用 map 模拟集合
    set := make(map[string]bool)

    // 添加元素
    set["apple"] = true
    set["banana"] = true
    set["orange"] = true

    // 检查元素是否存在
    if set["apple"] {
        fmt.Println("集合中有 apple")
    }

    // 删除元素
    delete(set, "banana")
    fmt.Println("删除 banana 后的集合:", set)

    // 遍历集合
    fmt.Println("集合中的元素:")
    for fruit := range set {
        fmt.Println(fruit)
    }
}

6. 二叉树(Binary Tree)

Go 标准库没有提供树结构,但可以手动实现二叉树。以下是一个简单的二叉搜索树的实现:

package main

import "fmt"

// 定义二叉树节点
type TreeNode struct {
    value int
    left  *TreeNode
    right *TreeNode
}

// 插入元素到二叉搜索树
func insert(node *TreeNode, value int) *TreeNode {
    if node == nil {
        return &TreeNode{value: value}
    }
    if value < node.value {
        node.left = insert(node.left, value)
    } else {
        node.right = insert(node.right, value)
    }
    return node
}

// 中序遍历二叉树
func inorderTraversal(node *TreeNode) {
    if node != nil {
        inorderTraversal(node.left)
        fmt.Println(node.value)
        inorderTraversal(node.right)
    }
}

func main() {
    var root *TreeNode
    values := []int{8, 3, 10, 1, 6, 14, 4, 7, 13}

    for _, value := range values {
        root = insert(root, value)
    }

    fmt.Println("二叉树的中序遍历:")
    inorderTraversal(root)
}

7. 图(Graph)

图可以使用邻接表来表示,Go 中可以用 map[string][]string 实现邻接表:

package main

import "fmt"

// 图的邻接表表示
type Graph struct {
    edges map[string][]string
}

// 添加边
func (g *Graph) addEdge(node, neighbor string) {
    g.edges[node] = append(g.edges[node], neighbor)
}

// 打印图
func (g *Graph) printGraph() {
    for node, neighbors := range g.edges {
        fmt.Printf("%s -> %v\n", node, neighbors)
    }
}

func main() {
    g := Graph{edges: make(map[string][]string)}

    g.addEdge("A", "B")
    g.addEdge("A", "C")
    g.addEdge("B", "D")
    g.addEdge("C", "E")

    g.printGraph()
}

8. 链表(Linked List)

Go 的 container/list 标准库支持双向链表。以下代码展示如何插入和遍历链表:

package main

import (
	"container/list"
	"fmt"
)

func main() {
	// 创建链表
	myList := list.New()

	// 在链表末尾添加元素
	myList.PushBack(1)
	myList.PushBack(2)
	myList.PushBack(3)

	// 遍历链表
	for element := myList.Front(); element != nil; element = element.Next() {
		fmt.Println(element.Value)
	}
}

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐