一、函数
在Go语言中,函数是执行特定任务的自包含代码块。
1.函数的定义
函数通过func
关键字定义,格式如下:
func 函数名(形参 形参类型, 形参 形参类型) 返回值类型 { 函数体 return 返回值 }
2.基础函数类型
- 无参数无返回值
- 单个参数,无返回值
- 多个相同类型参数,无返回值
- 多个不同类型参数,无返回值
- 多个参数,一个返回值
- 多个参数,多个返回值
package main import "fmt" func main() { demo01() //demo02("d") // 类型不匹配 demo02(2) demo03(1, 2) demo04(1, "A") var res = demo05(1, 2) fmt.Println("a+b:", res) var res1, res2 = demo06(1, 1) fmt.Println(res1, res2) } // 无参数无返回值 func demo01() { fmt.Println("hello world") } // 单个参数,无返回值 func demo02(a int) { fmt.Println("a:", a) } // 多个相同类型参数,无返回值 func demo03(a, b int) { fmt.Println("a+b:", a+b) } // 多个不同类型参数,无返回值 func demo04(a int, b string) { fmt.Println("a:", a) fmt.Println("b:", b) } // 多个参数,一个返回值 func demo05(a, b int) int { return a + b } // 多个参数,多个返回值 func demo06(a, b int) (int, bool) { if a+b > 3 { return a + b, true } else { return a * b, false } }
3.特殊函数类型
-
匿名函数:
test06
展示了如何在Go中定义匿名函数,即没有函数名的函数。 -
函数作为一等公民:
f
变量可以存储函数,test11
返回一个函数作为其结果。 -
命名返回值:
test10
展示了如何使用命名返回值,这有助于提高代码的可读性。 -
闭包:
test12
是一个闭包示例,它捕获并使用外部作用域的变量。 -
函数作为参数:
test14
接受一个函数作为参数,并在函数体内调用它。 -
类型重命名:
MyFunc
是func(string) (int, int)
类型的别名。
package main import "fmt" func main() { demo01() // 函数作为一等公民 var demo02 = func(a, b int) int { return a + b } var res = demo02(15, 16) fmt.Println(res) var res1, res2 = demo03(16, 17) fmt.Println(res1, res2) // 闭包 var ff = demo04(15) ff() // 函数作为参数 var f1 = func(a int) { fmt.Println(a) } demo05(15, f1) // 类型重命名 type StringProcessor func(string) string processor := StringProcessor(func(input string) string { return "Processed: " + input }) result := processor("Input") fmt.Println(result) } // 匿名函数 func demo01() { fmt.Println("demo01--start") // 定义匿名函数并立即执行 func() { fmt.Println("demo01--inner") }() fmt.Println("demo01--end") } // 命名返回值 func demo03(a, b int) (sum int, flag bool) { sum = a + b flag = true //return sum, flag // 可以指定 return // 也可以不在指定 } // 闭包 func demo04(x int) func() { x = 666 f := func() { fmt.Println("我是内部函数", x) } return f } // 函数作为参数 func demo05(a int, f func(i int)) { f(a) }
4.装饰器函数
-
Go语言虽然没有像一些其他语言(例如Python)那样的装饰器语法,但是可以通过闭包来实现类似的功能。
-
装饰器通常用于在不修改原始函数代码的情况下增加函数的功能。
-
示例
package main import ( "fmt" "time" ) // 普通函数 func sayHello(name string) string { time.Sleep(time.Second) // 延迟1秒 return "Hello " + name } // 自定义装饰器,计时功能 func timeTrack(fn func(string) string) func(string) string { return func(name string) string { startTime := time.Now() // 记录开始时间 result := fn(name) // 调用原始函数 elapsedTime := time.Since(startTime) // 计算经过时间 fmt.Printf("耗时: %s \n", elapsedTime) return result } } func main() { // 使用装饰器增强原始函数 enhanceSayHello := timeTrack(sayHello) helloMessage := enhanceSayHello("world") fmt.Println(helloMessage) }
二、包
在Go语言中,包(package)是组织源代码的基本单位,它有助于代码的可重用性和可读性
1.包规则
- 导出规则:如果一个函数、变量或类型的名称以大写字母开头,它就是导出的(exported),这意味着它可以在包外部被访问和使用。如果以小写字母开头,则它在包外部是不可见的。
- 包名一致性:在同一个文件夹下的所有Go源文件应该属于同一个包,并且包名通常建议与文件夹名相同。这是为了保持代码的清晰和易于理解。
- 包内部可见性:在同一个包内的所有源文件共享相同的包级作用域,这意味着这些文件中的所有变量、函数和类型都像是在同一个文件中一样可见。
- 使用包:要使用其他包中的导出成员,需要导入对应的包,并通过包名来引用它们。
-
导入包:使用
import
关键字来导入包。导入时,通常使用包的全路径名,例如import "demo/common"
。 -
导入并重命名包:如果包名冲突或者为了方便引用,可以在导入时为包指定一个新的名称,例如
import cv1 "demo/common/v1"
。 - 一次导入多个包:可以通过一对花括号来一次导入多个包,这样可以保持代码的整洁。
2.示例
-
目录结构
-
demo/ ├── common/ │ ├── v1/ # v1 是 common 包的一个子目录,表示版本1 │ │ └── demo01.go # demo01.go 是 common 包 v1 版本的实现文件 │ ├── demo02.go # demo02.go 是 common 包的一部分,不在任何子目录中 │ └── demo01.go # 这是 common 包的另一个实现文件,可能与 v1 版本不同 └── main.go # main.go 是 main 包的入口文件,通常包含 main 函数 └── go.mod # go.mod 是 Go 模块的配置文件,记录模块的依赖关系
-
-
文件内容
-
// main.go package main // 导入标准库中的fmt包 import "fmt" // 一次导入多个包 import ( "demo/common" cv1 "demo/common/v1" // // 导入包并重命名 ) func main() { // 使用fmt包的Println函数 fmt.Println("Hello, world!") res1 := common.Add(1, 2) fmt.Println(res1) res2 := cv1.Mul(4, 5) // 使用重命名的包 fmt.Println(res2) }
-
// common/demo01.go package common func Add(a, b int) int { return a + b }
-
// common/demo02.go package common func Addd(a, b int) int { return Add(a, b) + b }
-
// common/v1/demo01.go package v1 func Mul(a, b int) int { return a * b }
-