Fork me on GitHub

Golang之旅30-函数与闭包

Golang知识点-函数和闭包

函数

在调用函数的时候,会给该函数分配一个新的栈区。基本数据类型一般放在栈区,引用类型放在堆区。

  • Go 语言总支持多个返回值
  • 希望忽略某个返回值,使用下划线_,表示占位符
  • 返回值如果只有一个,则可以不写
  • 基本数据类型和数组都是值传递,即进行值拷贝,在函数内修改,不会影响原来的值
  • Go语言不支持传统的函数重载(函数名相同,变量不同)
  • 函数也是一种数据类型,可以赋值给一个变量,该变量成为函数类型的变量。
  • 函数可以作为形参,进行调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func 函数名 (参数列表) (返回值列表){
函数体部分
return 返回值列表
}
// --------

package main
import "fmt"

// 函数也是种数据类型
func test(a,b int){
return a + b
}

func main(){
x := test
res := x(10, 20)
fmt.Println(res)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main
import "fmt"

func calc(a int, b int) res int{
res = a + b
return res // res可以省略
}

func test1(n1 int){
n1 = n1 + 10
fmt.Println("n1=", n1) // 20
}

func main(){
n1 := 20
test1(n1)
fmt.Println("n1=", n1) // 20
}

  • go语言中的每个文件都是一个包
  • 区分相同名字的函数、变量等标识符
  • 控制函数、变量等访问范围,即作用域
  • 为了外部访问,将函数名首字母大写
  • 文件的包名通常和所在的文件夹名相同
  • 引入包的时候,路径是从GOPATH开始的,从src下开始,不用带上src
  • 如果给包取了别名,则需要使用别名来进行访问

递归调用

在函数被调用的时候会分配一个新的栈区。栈是先进后出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main
improt "fmt"

func test(n int){
if n > 2{
n--
test(n)
}
fmt.Println(n)
}

func main(){
test(4)
}

// 2 2 3
// n=4执行if,n变成3,还有fmt;n=3执行if,n变成2,还有fmt;n=2不执行if,直接输出fmt。
// 调用输出顺序:先进后出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main
improt "fmt"

func test(n int){
if n > 2{
n--
test(n)
}else{
fmt.Println(n)
}
}

func main(){
test(4)
}

// 结果是 2
// n=3,4满足条件,根本不会执行else语句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 斐波那契数列实现
package main
import "fmt"

func fibonacii(n int) int {
if n == 1 || n == 2{
return 1
} else{
return fibonacii(n - 1) + fibonacii(n - 2)
}
}

func main(){
res := fibonacii(5)
fmt.Println(res)
}

值传递和引用传递

  1. 值类型:变量直接存储值,内存通常在中进行分配。传递效率取决于数据量的大小。

    • int\float

    • bool\string

    • 数组array

    • 结构体struct

  2. 引用类型:变量存储的是地址,地址的空间才是真正存储的数据(值),内存通常在上进行分配。如果没有任何变量引用这个地址,由GC进行垃圾回收。传递效率高

    • 指针pointer
    • 切片slice
    • 映射map
    • 管道channel
    • 接口interface

KjoVZF.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main
import "fmt"

// 基本数据类型:值传递
func test1(n1 int){
n1 = n1 + 10
fmt.Println("n1=", n1) // 30
}

// 引用传递
func test2(n1 *int){
*n1 = *n1 + 10
fmt.Println("n1=", *n1)
}

func main(){
n1 := 20
test1(n1)
fmt.Println("n1=", n1) // 20

test2(&n1)
fmt.Println("n1=", n1) // 30
}

自定义数据类型

1
2
3
4
5
6
7
8
type myInt int   // 相当于是别名;myInt和int不是同一个类型
var num1 myInt
var num2 int
num1 = 40
num2 = int(num1) // 需要强制转换
fmt.Println(num1, num2)

type mySum func(int, int)int // mySum等价于函数类型

Go语言支持可变参数

  • args是切片类型
  • 通过args[index]可以进行访问
  • 可变参数必须放在最后
1
2
3
4
5
6
7
8
func sum(args... int) sum int{
//函数体部分
}

// 1到多个参数
func sum(1 int, args...int)sum int{
//函数体部分
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main
import "fmt"

// 可变参数必须放在最后
func sum(n1 int, args...int)int{
sum := n1
for i := 0;i<len(args);i++{
sum += args[i]
}
return sum
}

func main(){
res := sum(10,20,20,30,91)
fmt.Println(res)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 两个变量的值交换

package main
import "fmt"

func swap(n1 *int, n2 *int){
t = *n1 // 定义临时变量
*n1 = *n2
*n2 = t
}

func main(){
a := 10
b := 20
swap(&a, &b) // 传入的是地址
fmt.Println(a, b)
}

init函数

每个原文件中都有个init函数;在main函数执行之前,先被调用,完成初始化工作。

  • 变量定义----init----main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main
import "fmt"

var age = test()
func test()int{
fmt.Println("test()...") // 1
}

func init(){
fmt.Println("init()...") // 2
}

func main(){
fmt.Println("main()....age=", age) // 3
}
  • 如果main中引入了另一个文件,执行顺序为:

M9pn0O.png

匿名函数

匿名函数可以多次使用。

  • 在定义的时候直接调用,只能调用一次
  • 将匿名函数赋值给变量
  • 全局匿名函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main
import "fmt"

var (
// Fun是全局匿名函数
Fun = func(n1,n2 int)int{
return n1 * n2
}
)

func main(){
// 定义的时候直接调用
res1 := func(n1, n2 int)int{
return n1 + n2
}(10,20) // 直接赋值调用
fmt.Println(res1)

// 将匿名函数赋值给变量
res2 := func(n1, n2 int)int{ // 定义匿名函数
return n1 - n2
}
a := res2(80,20)
fmt.Println(a)

// 调用全局匿名函数
res3 := Fun(1,3)
fmt.Println(rew3)
}

必包

必包=函数+外部引用环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main
import "fmt"

fun addNumber() func(int) int{ // 返回值是一个函数
var n int = 10
var str = "hello"
return func(x int)int{
n = n + x
str += string(36) // 36对应的是'$'
fmt.Println("str=", str) // str="hello$" str="hello$$" str="hello$$$"
return n
}
}

func main(){
// 实现累加功能
f := addNumber() // 调用函数
fmt.Printn(f(1)) // 11
fmt.Printn(f(2)) // 13
fmt.Printn(f(3)) // 16

}

defer

延时机制。在函数执行完毕之后及时释放资源。

  • 逆序执行,类似栈的结构
  • defer将语句压入栈中,同时也会将值拷贝压入栈中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main
import "fmt"

func sum(n1, n2 int)int{
// defer:压入栈中
defer fmt.Println("ok1 n1=", n1) // 3 n1=10 这两个值不会改变
defer fmt.Println("ok2 n2=", n2) // 2 n2=20
// 增加部分,不会改变n1和n2的原来的值
n1++
n2++
res := n1 + n2
fmt.Println("ok3 res=", res) // 1 res=32
return res
}

func main(){
res := sum(10,20)
fmt.Println("res=", res) // 4 res=32
}
1
2
3
4
5
6
7
8
9
10
11
12
13
func test(){
// 关闭文件
file = openfile(文件名)
defer file.close()
// 其他代码
}

func test(){
// 释放数据库资源
connect = openDatabase()
defer connect.close()
// 其他代码
}

变量作用域

  • 函数内部声明的变量称为局部变量,作用域仅在函数内部
  • 函数外部声明的变量叫全局变量,作用域在整个包内有效;如果首字母大写,则在整个程序内有效
  • 如果变量在一个代码块之内入for / if之内,变量的作用域在代码块之内
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main
import "fmt"

var name = "tom" // 全局变量
func test1(){
fmt.Println(name)
}

func test2(){
name := "jack" // 局部变量
fmt.Println(name)
}

func main(){
fmt.Println(name) // tom
test1() // tom
test2() // jack
test1() // tom
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main
import "fmt"

var name = "tom" // 全局变量
func test1(){
fmt.Println(name)
}

func test2(){
name = "jack" // 相当于是修改全局变量
fmt.Println(name)
}

func main(){
fmt.Println(name) // tom
test1() // tom
test2() // jack
test1() // jack
}

函数练习题

打印金字塔
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package main
import "fmt"



func printPyramid(totalLevel int){

for i:=1; i <= totalLevel;i++{ // i表示层数
// 打印空格
for k := 1;k <= totalLevel - i;k++{
fmt.Print(" ")
}

for j :=1;j <= 2 * i - 1;j++{ // j表示每层打印多少个*
// 打印空金字塔
if j == 1 || j == 2 * i-1 || i == totalLevel {
fmt.Print("*")
} else{
fmt.Print(" ")
}

}
fmt.Println() // 换行操作
}
}

func main(){
// 调用函数
var n int
fmt.Println("请输入层数")
fmt.Scanln(&n)
printPyramid(n)
}

字符串中常用的函数

  • len()

  • r := []rune(str),字符遍历,解决中文的问题

  • str = strong.Itoa(1234)

  • var bytes = []byte(“hello go”)

  • str = str([]byte{98,97,99})

本文标题:Golang之旅30-函数与闭包

发布时间:2019年11月05日 - 22:11

原始链接:http://www.renpeter.cn/2019/11/05/Golang%E4%B9%8B%E6%97%8530-%E5%87%BD%E6%95%B0%E4%B8%8E%E9%97%AD%E5%8C%85.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

Coffee or Tea