Golang 并发编程goroutine Golang

package main

import "fmt"

// 学习并发前应了解的一些基本内容

// 操作系统基础:进程与线程
// 进程(Process):一个进程是操作系统中正在执行的程序。每个进程都有自己的内存空间、代码和数据。进程是操作系统进行资源管理的基本单位。
// 线程(Thread):线程是进程中的一个执行单元,一个进程可以有多个线程。线程共享进程的资源,但每个线程有自己的执行栈。线程是操作系统调度的基本单位。
// 多线程和多进程:多进程是指操作系统同时运行多个独立的进程,而多线程是指在一个进程内部运行多个线程。Go 中的 goroutines 是一种轻量级线程,与操作系统线程类似但更轻量。
// 上下文切换:操作系统会根据一定的调度算法,暂停当前进程或线程的执行,并切换到另一个进程或线程的执行,这个过程称为上下文切换。

// 并发和并行
// 并发(Concurrency):并发指的是程序中有多个任务正在进行,但是它们可能并不同时执行。不同任务之间通过切换执行来模拟“同时”进行。
// 并行(Parallelism):并行指的是多个任务真正同时执行。并行通常发生在多核处理器上,多个任务在不同的 CPU 核心上同时执行。
// 你可以将 并发 想象成同时处理多个任务,但每次只做其中一个任务,通过快速切换来产生“同时”的效果。Go 的 goroutine 是一种并发模型,它可以非常高效地处理成千上万的任务。

// 并发编程
func main(){

    // Go 的并发编程非常强大,它通过 goroutines 和 channels 来帮助开发者轻松处理并发任务。
    // Go 的并发模型基于 CSP(Communicating Sequential Processes)理论,通过通信而不是共享内存来处理并发。

    // Goroutines
    // Go 中的并发单元是 goroutine,它是一个轻量级的线程。goroutine 通过 go 关键字启动,Go 的调度器会自动管理多个 goroutine 的执行

    // 启动一个新的 goroutine
    go printMessage()

    // 主线程等待 goroutine 完成
    // fmt.Scanln()

    // go printMessage() 启动了一个新的 goroutine,这个函数会并发执行。

    // 主函数 fmt.Scanln() 用于阻塞,等待用户输入,确保 goroutine 可以有足够的时间执行。

    // Channels
    // Channel 是 Go 提供的一个强大的并发原语,允许 goroutines 之间进行数据传递。Go 中的 Channel 可以是有缓冲的或无缓冲的。

    // 无缓冲 Channel
    // 无缓冲 Channel 是一种同步的通信方式,当一个 goroutine 向 Channel 发送数据时,另一个 goroutine 必须接收这个数据才能继续。

    ch := make(chan int)

    go sender(ch)

    // 接收数据并打印
    data := <-ch
    fmt.Println("Received data:", data)

    // 有缓冲Channel
    // 有缓冲 Channel 允许多个数据被发送到 Channel 中,而不需要立即被接收。它允许一定数量的缓冲区

    chs := make(chan int, 3) // 创建一个缓冲区为 3 的 Channel

    go senders(chs)

    for i := 0; i < 5; i++ {
        datas := <-chs
        fmt.Println("Received:", datas)
    }
    //缓冲 Channel ch := make(chan int, 3) 允许最多存储 3 个整数。
    //发送者可以发送多个数据而不需要立即被接收。

    // 关闭Channel
    // 当所有的发送操作完成后,可以关闭 Channel。关闭 Channel 使得接收者能够知道不再有更多数据。
    // func sender(ch chan int) {
    //  for i := 0; i < 3; i++ {
    //      ch <- i
    //  }
    //  close(ch) // 关闭 Channel
    // }

    // func main() {
    //  ch := make(chan int)

    //  go sender(ch)

    //  for data := range ch {
    //      fmt.Println("Received:", data)
    //  }
    // }
    // close(ch) 关闭 Channel,表示不再有数据发送到该 Channel。
    // 使用 for range 循环来接收数据,直到 Channel 被关闭

    // Select 语句
    // select 语句允许你等待多个 Channel 的操作。它会选择一个可用的 Channel 操作执行

    // func sender(ch chan int) {
    //  ch <- 42
    // }

    // func main() {
    //  ch1 := make(chan int)
    //  ch2 := make(chan int)

    //  go sender(ch1)
    //  go sender(ch2)

    //  select {
    //  case data := <-ch1:
    //      fmt.Println("Received from ch1:", data)
    //  case data := <-ch2:
    //      fmt.Println("Received from ch2:", data)
    //  }
    // }

    //select 等待多个 Channel 中的一个操作完成,执行对应的 case 语句。
    //这个例子中,select 会从 ch1 或 ch2 中接收数据,并执行相应的打印操作。

    // WaitGroup
    // WaitGroup 用于等待一组 goroutine 执行完成。它通常用于等待并发任务完成
    // import (
    //  "fmt"
    //  "sync"
    // )

    // func task(id int, wg *sync.WaitGroup) {
    //  defer wg.Done() // 告诉 WaitGroup 任务已完成
    //  fmt.Println("Task", id, "started")
    // }

    // func main() {
    //  var wg sync.WaitGroup

    //  for i := 1; i <= 5; i++ {
    //      wg.Add(1) // 每启动一个 goroutine,就增加计数
    //      go task(i, &wg)
    //  }

    //  wg.Wait() // 等待所有 goroutines 完成
    //  fmt.Println("All tasks finished.")
    // }
    // wg.Add(1) 增加等待计数,每启动一个 goroutine 都调用。
    // defer wg.Done() 在每个任务完成时减少计数。
    // wg.Wait() 等待所有任务完成。

    // Worker Pool 模式
    // 在实际应用中,常常需要将任务分配给多个 worker goroutine 来处理,这就是 worker pool 模式

    // import (
    //  "fmt"
    //  "sync"
    // )

    // func worker(id int, ch chan int, wg *sync.WaitGroup) {
    //  defer wg.Done()
    //  for task := range ch {
    //      fmt.Printf("Worker %d processing task %d\n", id, task)
    //  }
    // }

    // func main() {
    //  var wg sync.WaitGroup
    //  ch := make(chan int, 10)

    //  // 启动 3 个 worker
    //  for i := 1; i <= 3; i++ {
    //      wg.Add(1)
    //      go worker(i, ch, &wg)
    //  }

    //  // 发送任务
    //  for task := 1; task <= 10; task++ {
    //      ch <- task
    //  }

    //  close(ch) // 关闭 Channel,表示没有更多任务
    //  wg.Wait()  // 等待所有 worker 完成
    // }

    // 这里有 3 个 worker,每个 worker 从 Channel 中获取任务进行处理。
    // 通过 close(ch) 关闭 Channel 来通知 worker 没有更多任务。

    // 并发调试

    // Go 提供了 -race 标志来检测并发代码中的数据竞争问题。你可以使用它来运行程序并查找潜在的并发问题。
    // -race 标志会检测并发访问共享数据时的竞争条件,帮助你找出代码中的潜在问题。

    // 总结
    // Go 的并发编程非常强大,核心原语是 goroutines 和 channels。你可以通过以下步骤提升并发编程能力:

    // 理解 goroutines 和它们如何启动。
    // 学习如何通过 channels 在 goroutines 之间传递数据。
    // 使用 select 语句在多个 channels 之间进行选择。
    // 利用 WaitGroup 来同步多个 goroutines。
    // 掌握 worker pool 模式来处理并发任务。
    // 调试并发代码,确保没有数据竞争问题。

}

func senders(ch chan int) {
    for i := 0; i < 5; i++ {
        ch <- i
        fmt.Println("Sent:", i)
    }
}

func sender(ch chan int) {
    ch <- 42
    fmt.Println("Sent data")
}

func printMessage(){
    fmt.Println("Hello from Goroutines")
}

// 更通俗简单的理解go 并发
// 什么是并发?
// 想象一下,你正在做一份工作报告,而你有两个任务:

// 任务 1:查找资料。
// 任务 2:整理内容。
// 在传统的工作模式下,你可能会先做任务 1,然后再做任务 2,这叫做 顺序执行。但假设你现在有两个人,你可以让一个人做任务 1,另一个人做任务 2,这样你可以 并行 完成这两个任务。

// 并发 是指我们有多个任务,它们可能并不会真正同时执行,但它们的执行是交替进行的,就像你在忙碌的时刻,切换任务一样。并行则是任务在不同的 CPU 核心上同时执行。

// 在 Go 中,虽然我们通常是通过 goroutine 来实现并发,但很多时候我们只是希望它们看起来像是“同时”运行的。

// 什么是 Goroutine?
// Goroutine 是 Go 中的轻量级线程,它和操作系统的线程不一样。你可以把 goroutine 想象成你在做任务时的“临时助手”。你给它一个任务,它就开始工作。

// 启动一个 goroutine:通过 go 关键字,你可以启动一个新的 goroutine。每个 goroutine 都是并发执行的。
// 举例
// func sayHello() {
//     fmt.Println("Hello, Goroutine!")
// }

// func main() {
//     // 启动一个新的 goroutine
//     go sayHello()

//     // 主线程继续执行
//     fmt.Println("Hello from main!")
// }

// 这里,go sayHello() 启动了一个新的 goroutine,它会和 main 函数一起并发执行。你会看到输出顺序是随机的,因为 sayHello 和 main 是并发运行的。

// Goroutine 是如何工作的?
// Go 的 goroutine 非常轻量,启动一个 goroutine 的成本非常低,可能只需要几 KB 的内存。
// 它们由 Go 运行时调度和管理,你不需要显式地创建或销毁 goroutine。
// 你可以理解为:在 Go 中,启动一个新的任务(goroutine)就像给你指派一个新的助手,而你无需关心每个助手的具体管理细节。

// 什么是 Channel?
// Channel 是 Go 中用于在 goroutines 之间传递信息的通道。你可以把它理解为“邮递员”,它负责把消息从一个 goroutine 传递到另一个 goroutine。

// 假设你有两个任务:

// 任务 1:从一堆数字中筛选出偶数。
// 任务 2:将筛选出的偶数做一些加法处理。
// 你不想让任务 1 和任务 2 直接互相操作,而是希望它们通过传递信息的方式进行合作。

// 示例
// func filterEven(numbers []int, ch chan int) {
//     for _, num := range numbers {
//         if num%2 == 0 {
//             ch <- num // 发送偶数到 channel
//         }
//     }
//     close(ch) // 关闭 channel,表示任务结束
// }

// func addNumbers(ch chan int) {
//     for num := range ch {
//         fmt.Println("Adding number:", num+1)
//     }
// }

// func main() {
//     numbers := []int{1, 2, 3, 4, 5, 6}
//     ch := make(chan int) // 创建一个 channel

//     // 启动两个 goroutine
//     go filterEven(numbers, ch)
//     go addNumbers(ch)

//     // 主线程等待 goroutines 执行完成
//     fmt.Scanln()
// }

// filterEven 任务会筛选出偶数,并通过 ch <- num 把偶数发送到 channel。
// addNumbers 任务会从 ch 中接收偶数,并在其基础上加 1 后打印出来。
// Channel 在这里充当了一个 消息传递系统,确保两个 goroutine 之间的信息能够正确传递。

// Go 中的并发调度:如何协作?
// Go 的调度器会将多个 goroutine 安排到有限的 CPU 核心上执行。Go 的并发执行并不意味着所有 goroutines 会真正“同时”运行。实际上,Go 会在不同的时刻给 goroutine 分配执行的时间片,快速切换它们的执行。你可以理解为:在一个 CPU 核心上,Go 的调度器会以非常高的速度在多个 goroutines 之间切换任务。

// 核心概念:Go 的并发调度是通过 抢占式调度 来管理的,而不像传统操作系统中那样使用重量级线程。

// 如何用 Select 同时处理多个 Channel?
// 假设你有多个任务需要同时处理,但它们的数据需要通过不同的 channel 传递。Go 提供了 select 语句,它可以让你同时监听多个 channel,哪个 channel 有数据,它就会处理哪个。

// func sender(ch chan string) {
//     ch <- "Hello from sender!"
// }

// func main() {
//     ch1 := make(chan string)
//     ch2 := make(chan string)

//     go sender(ch1)
//     go sender(ch2)

//     select {
//     case msg1 := <-ch1:
//         fmt.Println("Received from ch1:", msg1)
//     case msg2 := <-ch2:
//         fmt.Println("Received from ch2:", msg2)
//     }
// }

// 这里,select 语句会同时监听 ch1 和 ch2,哪个 channel 先有数据,它就会接收并执行对应的 case。

// 注意,select 可以立即为一次性的,一个select只会执行一次case ,比如同时AB两个任务,select 监听AB,A收到数据select中case会执行,B收到数据不会执行case

// 总结
// 并发编程的核心思想就是让多个任务并行或者交替地执行。Go 中通过 goroutine 来实现并发,通过 channel 在 goroutines 之间传递数据。这些机制让 Go 编程语言非常适合进行并发编程。理解并发的关键是:

// goroutine:让任务并发执行。
// channel:让 goroutines 之间进行通信。
// select:处理多个 channel 操作。

杨佳乐 发布于  2025-1-10 15:09 

Golang 方法 Golang

package main

import "fmt"

// 方法

// 方法 是一种与特定类型关联的函数。
// 它与函数的不同之处在于,方法绑定到某个类型上,可以通过该类型的实例调用。
// Go 中的函数和方法在语法上非常相似,区别主要在于方法有一个接收者(Receiver),它指定了该方法属于哪个类型

//基本定义
//在 Go 中,方法是与类型关联的函数。方法的定义与函数类似,但方法定义需要指定一个 接收者,接收者是方法的“主人”,即该方法操作的类型实例
// func (receiver variable) MethodName(parameters) returnType {
//  // 方法体
// }

// receiver:接收者,表示方法绑定的类型。它类似于其他语言中的 this 或 self,但在 Go 中,接收者是显式的,在方法定义时直接指定。
// MethodName:方法名。
// parameters:参数列表,和普通函数一样。
// returnType:返回类型(可以没有)。

// 定义一个结构体类型
type Rectangle struct {
    Width, Height int
}

// 定义一个方法,计算矩形的面积
func (r Rectangle) Area() int {
    return r.Width * r.Height
}

// 引用接收者方法
func (r *Rectangle) SetWidth(width int) {
    r.Width = width  // 修改原对象
}

func main(){
    tr := Rectangle{
        Width:5,
        Height:10,
    }
    // 调用结构体的Area方法
    fmt.Println(tr.Area())

    // 值接收者 vs 引用接收者

    // 值接收者
    // 值接收者意味着方法接收的是该类型的副本(拷贝)。这种方式会复制接收者对象,因此对于大型对象,可能会带来性能损失。
    // 用值接收者定义的方法不能修改接收者的值。

    // 例如下面这种方法,无法修改原始Rectangle的Width,因为接收者是值类型
    // func (r Rectangle) SetWidth(width int) {
    //  r.Width = width
    // }

    // 引用接收者
    //引用接收者意味着方法接收的是该类型的指针,可以修改接收者的值。通常情况下,使用引用接收者可以提高性能,避免不必要的拷贝,尤其是当接收者是大型结构体时。
    //使用指针接收者的方法可以修改接收者实例的字段。

    // 这样,SetWidth 方法就能修改原始的 Rectangle 实例,因为接收者是指针类型。
    // func (r *Rectangle) SetWidth(width int) {
    //  r.Width = width
    // }

    //引用接收方法,可以修改tr的值
    tr.SetWidth(30)
    fmt.Println(tr)

    // 方法的传递方式
    // Go 方法有两个主要传递方式:值传递 和 指针传递。在方法定义时,选择值接收者还是指针接收者,决定了传递方式。
    // 值接收者:每次调用方法时,都会传递该类型的副本(拷贝),适用于方法不修改接收者的情况。
    // 指针接收者:每次调用方法时,都会传递该类型的指针,适用于方法可能修改接收者的情况,且能避免拷贝开销。

    // 方法与接口
    // 在 Go 中,方法是与接口的实现紧密相关的。一个类型如果实现了接口中的所有方法,则该类型就实现了该接口,而不需要显式声明。Go 的接口是隐式实现的

    // 示例
    // // 定义一个接口
    // type Shape interface {
    //  Area() int
    // }

    // // 定义一个结构体
    // type Rectangle struct {
    //  Width, Height int
    // }

    // // 实现 Shape 接口的 Area 方法
    // func (r Rectangle) Area() int {
    //  return r.Width * r.Height
    // }

    // func main() {
    //  var s Shape
    //  rect := Rectangle{Width: 5, Height: 4}

    //  s = rect // rect 实现了 Shape 接口
    //  fmt.Println("Area of Shape:", s.Area())  // 输出: Area of Shape: 20
    // }

    // 方法与匿名字段(嵌入式结构体)
    // Go 允许通过匿名字段(embedding)实现方法的继承。通过将一个结构体嵌入到另一个结构体中,嵌入的结构体的方法可以被外部结构体调用

    //示例
    // 定义一个基础结构体
    // type Shape struct {
    //  Width, Height int
    // }

    // func (s Shape) Area() int {
    //  return s.Width * s.Height
    // }

    // // 定义一个继承 Shape 的结构体
    // type Rectangle struct {
    //  Shape // 匿名字段,继承了Shape的方法
    // }

    // func main() {
    //  rect := Rectangle{Shape: Shape{Width: 5, Height: 4}}
    //  fmt.Println("Area of Rectangle:", rect.Area())  // 输出: Area of Rectangle: 20
    // }
    // 在这个例子中,Rectangle 嵌入了 Shape 结构体,从而继承了 Shape 的 Area 方法。这样可以避免重复实现相同的方法

    // 方法的组合与封装
    //方法也可以作为组合的一部分来封装其他类型的行为。这种方式通常用于实现更复杂的类型,封装细节,暴露公共接口

    // 示例
    // 定义一个基础类型
    // type Circle struct {
    //  Radius int
    // }

    // func (c Circle) Area() int {
    //  return 3 * c.Radius * c.Radius
    // }

    // // 定义一个更复杂的类型,组合了 Circle
    // type ColoredCircle struct {
    //  Circle // 组合 Circle
    //  Color  string
    // }

    // func main() {
    //  coloredCircle := ColoredCircle{
    //      Circle: Circle{Radius: 5},
    //      Color:  "Red",
    //  }
    //  fmt.Println("Area of Colored Circle:", coloredCircle.Area()) // 输出: Area of Colored Circle: 75
    //  fmt.Println("Color:", coloredCircle.Color)  // 输出: Color: Red
    // }

    // 方法与 Goroutine
    // Go 中的并发编程依赖于 goroutine 和通道,方法也可以用来在 goroutine 中执行某些操作。

    // type Counter struct {
    //  Count int
    // }

    // // 方法:计数
    // func (c *Counter) Increment() {
    //  c.Count++
    // }

    // // 启动多个 goroutine
    // func main() {
    //  counter := Counter{Count: 0}
    //  for i := 0; i < 5; i++ {
    //      go func() {
    //          counter.Increment()
    //      }()
    //  }

    //  // 等待 goroutines 完成
    //  time.Sleep(time.Second)
    //  fmt.Println("Counter value:", counter.Count) // 输出:Counter value: 5
    // }

    //总结
    // Go 中的方法与函数非常相似,但有一个关键区别:方法与特定类型(例如结构体)绑定。理解 Go 方法的以下特性对于编写高效且易于维护的 Go 代码至关重要
    // 接收者:方法需要一个接收者,接收者决定了方法属于哪个类型。
    // 值接收者与指针接收者:方法可以使用值接收者或指针接收者,选择哪种接收者取决于是否需要修改接收者的数据。
    // 方法与接口:Go 中的方法与接口息息相关,类型实现接口不需要显式声明,只要方法匹配即可。
    // 嵌入式结构体:通过嵌入其他结构体,可以轻松实现代码复用和组合。
    // 方法与并发:方法可以结合 goroutine 和通道来实现并发编程。

}

杨佳乐 发布于  2025-1-10 14:12 

Golang 函数 Golang

package main

import "fmt"

// 函数
func main(){

    // Go 语言的函数(Function)是构建程序的基本单元之一。函数封装了某些行为或操作,并且通过参数传递数据,最终通过返回值将结果返回给调用者。
    // Go 的函数具备简洁和灵活的特性,例如支持多返回值、匿名函数、闭包等

    // 定义函数基本语法
    // func FunctionName(parameterList) returnType {
    //  // 函数体
    //  return returnValue
    // }

    num1 := 10
    num2 := 20

    //无返回值函数
    tfunc(num1,num2)

    //单个返回值函数
    fmt.Println(add(num1,num2))

    //多返回值函数
    q,r := divide(num1,num2)
    fmt.Println("q,r",q,r)

    // 命名返回值
    // 如果函数的返回值具名,可以在函数声明中指定返回值的变量名,这样可以省略 return 中的变量名,直接返回值。
    // func divideTo(a, b int) (quotient, remainder int) {
    //  quotient = a / b
    //  remainder = a % b
    //  return
    // }

    // 参数传递
    //Go 中的参数传递默认是 值传递,即函数内对参数的修改不会影响外部变量。也就是说,函数接收到的是参数的副本,修改副本不会影响原始数据

    numa := 5
    modifyValue(numa) // 给参数 + 1
    fmt.Println("numa",numa) // 返回5 不会改变

    //引用传递 (要使用&传递内存地址)
    modifyPointer(&numa) // 参数 * 2
    fmt.Println("numa",numa) // 返回10 会修改numa数据

    //变长参数
    //Go 函数支持变长参数(variadic function),允许传递任意数量的参数。变长参数在函数定义中使用 ... 来表示,变长参数会被当作一个切片处理
    // 变长参数函数
    // func sum(nums ...int) int {
    //  total := 0
    //  for _, num := range nums {
    //      total += num
    //  }
    //  return total
    // }
    // fmt.Println(sum(1, 2, 3)) 
    fmt.Println(addsnum(1,2,3,4,5))

    // 匿名函数
    // 匿名函数(Lambdas)是没有名字的函数,通常用于传递给其他函数或者作为闭包使用。
    func(a, b int) {
        fmt.Println(a + b)
    }(5, 7) // 输出 12

    // 匿名函数可以赋值给变量,并且可以像普通函数一样调用
    oneaddfunc := func(a,b int) int {
        return a + b
    }
    fmt.Println(oneaddfunc(1,2)) // 输出 3

    // 闭包(Closure)
    // 闭包是一个函数,它“记住”并且可以访问它被创建时的作用域中的变量,即使外部函数已经返回。Go 的匿名函数通常是闭包
    // func adder() func(int) int {
    //  sum := 0
    //  return func(x int) int {
    //      sum += x
    //      return sum
    //  }
    // }
    // 函数 adder 返回了一个闭包,闭包可以访问 sum 变量并且修改它。
    // 即使 adder 函数已经返回,闭包仍然记住了 sum 的值。

    add := adder() // 返回一个闭包
    fmt.Println(add(5)) // 返回 5
    fmt.Println(add(10)) // 返回 15

    add2 := adder()
    fmt.Println(add2(5)) // 返回 5
    fmt.Println(add2(10)) // 返回 15

    // 递归函数
    // 递归是指函数直接或间接调用自身。Go 支持递归,递归的基本形式和其他编程语言一样
    // 递归函数,计算阶乘
    // func factorial(n int) int {
    //  if n == 0 {
    //      return 1
    //  }
    //  return n * factorial(n-1)
    // }
    fmt.Println(factorial(10))

    // defer 关键字
    // Go 语言中的 defer 语句用于确保在函数执行结束时(无论是正常返回还是异常终止)执行某些操作。defer 语句会被推迟到函数执行结束时才执行。

    deferfunc1 := func(){
        defer fmt.Println("最后执行这里")
        fmt.Println("优先执行这里")
    }

    deferfunc1()

    // 多个defer语句
    // 如果函数中有多个 defer 语句,它们会按 后进先出(LIFO) 的顺序执行。

    deferfunc2 := func(){
        defer fmt.Println("1")
        defer fmt.Println("2")
        defer fmt.Println("3")
    }

    deferfunc2()
    // 1
    // 2
    // 3

    // 总结
    // 多返回值:函数可以返回多个值,常用于返回多个计算结果。
    // 参数传递:Go 的函数采用值传递,可以通过传递指针实现引用传递。
    // 变长参数:可以传递任意数量的参数,参数在函数内部以切片形式处理。
    // 匿名函数:可以定义没有名字的函数,通常用于回调或闭包。
    // 闭包:函数可以捕获并记住外部变量的状态,即使外部函数已经返回。
    // 递归:函数可以调用自身,以解决一些递归问题。
    // defer 语句:用于延迟执行某些操作,通常用于资源清理工作。

}
// func:关键字,表示定义函数。
// FunctionName:函数的名称。
// parameterList:参数列表,多个参数用逗号分隔。
// returnType:返回类型,如果函数没有返回值,返回类型是 void。
// return:返回的值,返回值的类型要与声明的返回类型匹配。

// 一个简单的加法函数
func add(a int,b int) int {
    return a + b
}

//一个没有返回值的函数
func tfunc(a int,b int) {
    fmt.Println("a,b",a,b)
}

//多个返回值函数
func divide(a,b int)(int,int){
    quotient := a / b
    remainder := a % b
    return quotient, remainder
}

//值传递函数
func modifyValue(x int) {
    x += 1
}

// 引用传递(指针传递)
func modifyPointer(x *int) {
    *x = *x * 2
}

// 变长函数
// nums ...int 表示一个变长参数,它是一个切片
// 在函数体内,nums 可以像普通切片一样使用
func addsnum(nums ...int) int {
    total := 0

    for _,num := range nums{
        total += num
    }

    return total
}

// 闭包函数
func adder() func(int) int {
    num := 0
    return func(x int) int {
        num += x
        return num
    }
}

//递归函数
func factorial(n int) int {
    if n == 0 {
        return 1
    }
    return n * factorial(n-1)
}

杨佳乐 发布于  2025-1-10 11:24 

Golang 控制结构 Golang

package main

import "fmt"

// 控制结构
func main(){

    // if 语句
    // Go 中的 if 语句用于执行条件判断,语法与大多数编程语言类似,但 Go 的 if 语句没有括号,条件表达式直接放在 if 后面

    // if condition {
    //  // 条件为真时执行的代码
    // }
    x := 10

    if x == 10 {
        fmt.Println("x = 10")
    }

    // if else 语句
    // if condition {
    //  // 条件为真时执行的代码
    // } else {
    //  // 条件为假时执行的代码
    // }
    if x > 10 {
        fmt.Println("x > 10")
    } else {
        fmt.Println("x <= 10")
    }

    // if-else if-else 语句
    // 可以使用多个 else if 来处理多条件判断。
    if x == 1 {
        fmt.Println("x = 1")
    } else if x == 2 {
        fmt.Println("x = 2")
    } else if x == 3 {
        fmt.Println("x = 3")
    } else {
        fmt.Println("x != 1 && x != 2 && x != 3")
    }

    // if条件语句中的短声明
    // Go 的 if 语句支持在条件中声明和初始化变量,这在条件语句中很常见

    if testa := 10; testa > 0 {
        fmt.Println("testa > 0",testa)
    }
    //这里的 testa 只在 if 语句块内有效。
    //fmt.Println("testa",testa) // 此处使用testa将会报错

    // for循环
    // Go 中唯一的循环结构是 for。它可以用来实现各种类型的循环(类似于 C 语言的 for、while、do-while)

    // for initialization; condition; post {
    //  // 循环体
    // }
    // initialization:在循环开始时执行的代码,通常用于初始化循环变量。
    // condition:每次循环前检查的条件,如果为真则执行循环体。
    // post:每次循环结束时执行的代码,通常用于更新循环变量。
    for ia := 0;ia < 10; ia++ {
        fmt.Println("ia:",ia)
    }

    // 模拟while循环
    ib := 0
    for ib < 5 {
        fmt.Println("ib:",ib)
        ib++
    }

    // 无限循环
    // Go 的 for 语句也可以作为无限循环使用,只需要省略条件部分
    // for {
    //  //无限循环
    //  fmt.Println("无限循环")
    // }

    // switch语句
    // 是另一种控制结构,用于在多个条件中选择一个执行。Go 中的 switch 更加简洁和灵活,支持自动“fallthrough”行为,也支持条件判断和多条件匹配。
    // switch value {
    // case condition1:
    //  // 执行代码
    // case condition2:
    //  // 执行代码
    // default:
    //  // 如果没有匹配的条件,执行 default 中的代码
    // }

    day := 4
    switch day {
    case 1:
        fmt.Println("day = 1")
    case 2:
        fmt.Println("day = 2")
    case 3:
        fmt.Println("day = 3")
    default:
        fmt.Println("day != 1 && day !=2 && day != 3")
    }

    // 没有条件的switch语句
    // 如果没有 value,switch 会自动判断每个 case 的条件,直到遇到第一个满足的条件为止。
    switch {
    case day < 10:
        fmt.Println("day is less than 10")
    case day == 10:
        fmt.Println("day is equal to 10")
    default:
        fmt.Println("day is greater than 10")
    }

    // fallthrough 关键字
    // 在 Go 中,switch 不会自动执行下一个 case,除非显式使用 fallthrough 关键字
    x = 1
    switch x {
    case 1:
        fmt.Println("One") // 会输出
        fallthrough
    case 2:
        fmt.Println("Two") // 会输出
        fallthrough
    case 3:
        fmt.Println("Three") // 会输出
    case 4:
        fmt.Println("Four") // 不会输出
    }

    var nx interface{}
    nx = 1
    // switch语句中的类型匹配
    // Go 支持在 switch 中进行类型匹配,可以根据类型来分支
    switch v := nx.(type) { // 类型断言
    case int:
        fmt.Println("nx is of type int:", v)
    case string:
        fmt.Println("nx is of type string:", v)
    default:
        fmt.Println("Unknown type")
    }

    // select 语句
    // select 语句用于处理多个通道的操作,它是 Go 中用于并发编程的一个关键控制结构。select 会等待多个通道中的操作完成,并选择一个进行处理

    // select {
    // case <-chan1:
    //  // chan1 收到数据时执行
    // case <-chan2:
    //  // chan2 收到数据时执行
    // default:
    //  // 如果没有通道准备好,则执行 default
    // }

    // package main

    // import "fmt"

    // func main() {
    //  ch1 := make(chan string)
    //  ch2 := make(chan string)

    //  go func() {
    //      ch1 <- "Hello from ch1"
    //  }()
    //  go func() {
    //      ch2 <- "Hello from ch2"
    //  }()

    //  select {
    //  case msg1 := <-ch1:
    //      fmt.Println(msg1)
    //  case msg2 := <-ch2:
    //      fmt.Println(msg2)
    //  }
    // }

    // select 语句中的default关键字
    // 如果所有的通道都没有准备好,default 分支将会执行

    //总结
    // if 语句:用于条件判断,支持 else 和 else if。
    // for 循环:唯一的循环结构,支持多种形式的循环,包括传统的 for 循环、while 循环和无限循环。
    // switch 语句:用于选择多个条件之一,支持多条件匹配、类型断言和 fallthrough。
    // select 语句:用于并发编程,处理多个通道的读取和写入操作。

}

杨佳乐 发布于  2025-1-10 10:36 

Golang 运算符 Golang

package main

import "fmt"

// 运算符
func main(){

    // 算术运算符 用于进行基本的数学计算

    // +    加法  a + b
    // -    减法  a - b
    // *    乘法  a * b
    // /    除法  a / b
    // %    取余(模运算) a % b

    a := 10
    b := 3

    fmt.Println("a + b",a + b)
    fmt.Println("a - b",a - b)
    fmt.Println("a * b",a * b)
    fmt.Println("a / b",a / b)
    fmt.Println("a `%` b",a % b)

    // 关系运算符 用于比较两个值,并返回一个布尔值(true 或 false)

    // ==   等于  a == b
    // !=   不等于 a != b
    // <    小于  a < b
    // >    大于  a > b
    // <=   小于或等于   a <= b
    // >=   大于或等于   a >= b

    fmt.Println("a == b",a == b) // false
    fmt.Println("a != b",a != b) // true
    fmt.Println("a < b",a < b) // false
    fmt.Println("a > b",a > b) // true
    fmt.Println("a <= b",a <= b) // false
    fmt.Println("a >= b",a >= b) // true

    // 逻辑运算符 用于处理布尔值的逻辑运算

    // &&   逻辑与(AND)    a && b
    // !    逻辑非(NOT)    !a
    // ||   逻辑或        a || b

    tt := true
    tf := false

    fmt.Println("tt && tf",tt && tf) // false
    fmt.Println("tt || tf",tt || tf) // true
    fmt.Println("!tt",!tt) // false 当tt 不是false的时候,返回false , tt = false的时候返回true

    // 赋值运算符 用于将右侧的值赋给左侧的变量
    // 运算符  描述  示例
    // =    赋值  a = b
    // +=   加并赋值    a += b
    // -=   减并赋值    a -= b
    // *=   乘并赋值    a *= b
    // /=   除并赋值    a /= b
    // %=   取余并赋值   a %= b
    // &=   按位与并赋值  a &= b
    // |=   按位或    a |= b
    // ^=   按位异或并赋值 a ^= b
    // <<=  左移并赋值   a <<= b
    // >>=  右移并赋值   a >>= b

    c := 10
    d := 5

    c += d // c = c + d
    fmt.Println("c",c)

    c -= d // c = c - d
    fmt.Println("c",c)

    c *= d // c = c * d
    fmt.Println("c",c)

    c /= d // c = c / d
    fmt.Println("c",c)

    c |= d // c = c | d
    fmt.Println("c",c)

    // 自增自减运算
    c++ // c = c + 1
    c-- // c = c - 1
    fmt.Println("c",c)

    // 位运算符
    // &    按位与(AND)    a & b
    // |    按位或(OR)    a | b
    // ^    按位异或(XOR)   a ^ b
    // &^   清空对应位   a &^ b
    // <<   左移  a << b
    // >>   右移  a >> b

}

杨佳乐 发布于  2025-1-10 09:55