Golang 函数

1. 函数介绍

  • 定义:有输入、有输出,用来执行一个指定任务的代码块,增加代码的可重用性
  • 函数声明格式:
func 函数名( [参数列表] ) [返回值列表] {
   函数体
}

2. 自定义函数

无参数无返回值

package main

import "fmt"

func func001() {
	fmt.Println("无参无返回值")
}

func main() {
	func001()
}

有参数无返回值

package main

import "fmt"

func func001() {
	fmt.Println("无参无返回值")
}

func main() {
	func001()
}

有不定参数无返回值

package main

import "fmt"

func func003(n ...int) {
	fmt.Println("有不定参无返回值")
	for _, v := range n {
		fmt.Println(v)
	}
}

func main() {
	func003(10)
	func003(1,2,3,4)
}

无参数有返回值

package main

import "fmt"

// 无参有一个返回值
func func004() int {
	n := 10
	return n
}

// 无参数有多个返回值
func func005() (a, b int) {
	// 这里的a,b已经在声明函数返回值的时候已经声明,下面可以直接使用
	a = 10
	b = 20
	return
}

func main() {
	n := func004()
	fmt.Println(n)

	a, b := func005()
	fmt.Println(a, b)
}

有参数有返回值

package main

import "fmt"

// 有参有返回值
func calc(a, b int) (add, sub int) {
	add = a + b
	sub = a - b
	return
}

func main() {
	add, sub := calc(15, 10)
	fmt.Println(add, sub)
}

练习

分别通过循环和递归函数,计算 1+2+3……+100

package main

import "fmt"

func test01(num int) int {
	if num == 1 {
		return 1
	}
	return num + test01(num-1)
}

func test02(num int) int {
	if num == 100 {
		return 100
	}

	return num + test02(num+1)
}

func main() {
	fmt.Println(test01(100))
	fmt.Println(test02(1))
}

3.defer 关键字

defer 关键字的作用

  • defer ⽤于延迟一个函数或者方法的执行
  • defer 语句经常被用于处理成对的操作,如打开、关闭、连接、断开连接、加锁、释放锁
  • 通过 defer 机制,不论函数逻辑多复杂,都能保证在任何执行路径下,资源被释放
  • 释放资源的 defer 应该直接跟在请求资源的语句后,以免忘记释放资源
package main

import "fmt"

func main() {
	defer fmt.Println("defer语句")

	fmt.Println("普通语句")
}

// 输出结果
// 普通语句
// defer语句

4. 多个 defer 执行顺序

后声明先执行

package main

import "fmt"

func main() {
	defer fmt.Println("aaaaaa")
	defer fmt.Println("bbbbbb")
	fmt.Println("123132")
	defer fmt.Println("cccccc")
}

// 输出结果
//123132
//cccccc
//bbbbbb
//aaaaaa

defer 不管程序是否异常都会执行(为了关闭资源)

package main

import "fmt"

func test(n int) int{
	return 10/n
}

func main() {
	defer fmt.Println("aaaaaa")
	fmt.Println("123132")
	// 使用0除一个数来模拟异常
	fmt.Println(test(0))
	// 异常语句之后的代码快不执行,但是在次之前定义的语句会执行
	fmt.Println("456456")
	defer fmt.Println("cccccc")
}

defer 使用变量

package main

import "fmt"

func main() {
	i := 10
	// 此时的defer已经为10, 只是不执行而已
	defer fmt.Printf("defer语句: i=%d\n", i)

	i = 100
	fmt.Println(i)
}

// 输出结果
// 100
// defer语句: i=10
package main

import "fmt"

func main() {
	i := 10
	// 此时defer 中是使用的i是传入的当前参数i=10的值,只是暂时不执行
	defer func(i int) {
		fmt.Printf("defer语句: i=%d\n", i)
	}(i)

	i = 100
	fmt.Println(i)
}

// 输出结果
// 100
// defer语句: i=10
package main

import "fmt"

func main() {
	i := 10
	// 此时defer中引用的是外部i变量的地址,到最后执行defer语句时会取读取变量i的值
	defer func() {
		fmt.Printf("defer语句: i=%d\n", i)
	}()

	i = 100
	fmt.Println(i)
}

// 输出结果
// 100
// defer语句: i=100

5. 变量的作用域

全局变量

package main

import "fmt"

// 全局变量,先与main()前声明
var a int = 10

func test(){
	fmt.Println(a)
}

func main() {
	fmt.Println(a)
	test()
}

局部变量

package main

import "fmt"

func test(){
	// 局部变量
	var a int = 10
	for a > 0 {
		c := 3
		fmt.Println(c)
	}

	// 无法使用变量c,编译报错
	//fmt.Println(c)
}

func main() {
	test()
	// 无法使用变量c,编译报错
	//fmt.Println(c)
}

6. 变量可见性

包内任何变量和函数都是能访问的,包外需要名字首字母大写才可以访问

.
├── calc
│   └── calc.go
└── tt.go

// tt.go

package main

import (
	"fmt"
	"go_study/calc"
)

func main() {
	// 引用外部包的变量和函数
	fmt.Println(calc.Add(1,2))
	fmt.Println(calc.V)
	fmt.Println(calc.Sub(10,4))
}

// calc.go

package calc

// 外部包可见,变量或函数或其他首写字母需要大写
var V int = 10

func Add(a, b int) int {
	return a + b
}

func Sub(a, b int) int {
	return a - b
}

7. 匿名函数

函数也是一种类型,因此可以定义一个函数类型的变量

package main

import "fmt"

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

//1. 定义函数类型的变量
func test01() {
	f1 := add
	fmt.Printf("f1函数的类型: %T\n", f1)
	// 函数类型的变量调用函数
	sum := f1(1, 3)
	fmt.Println(sum)
}

// 2. 定义和赋值写在一起
func test02() {
	// 这就是匿名函数
	f1 := func(a, b int) int {
		return a + b
	}
	fmt.Printf("f1函数的类型: %T\n", f1)
	// 函数类型的变量调用函数
	sum := f1(1, 3)
	fmt.Println(sum)
}

// 3. defer后定义匿名函数

func test03() {
	var i int = 10
	defer func(a, b int) {
		fmt.Printf("a+b=%d\n", a+b)
	}(3,1)
	i = 100
	fmt.Println(i)
}

func main() {
	//test01()
	//test02()
	test03()
}

8. 函数类型作为参数

package main

import "fmt"

// 定义一个加法函数
func add(a, b int) int {
	return a + b
}

// 定义一个计算函数
func calc(a, b int, op func(int, int) int) int {
	return op(a,b)
}

func main() {
	// 通过调用calc函数来使用add函数计算两数相加结果
	reslut := calc(10, 15, add)
	fmt.Println(reslut)
}

9. 闭包

闭包是一个函数和与其相关的引用环境组合而成的实体(很抽象,难理解)

先看一段代码吧

package main

import "fmt"

// 定义一个返回类型为函数的方法
func add() func(int) int{
	// 局部变量n此时被匿名函数引用
	// 也就是n与匿名函数组成了一个实体
	// 所以下面函数若存活,则n会伴其左右

	var n int

	return func(i int) int {
		n +=i
		return n
	}
}

上面这段代码,定义了一个闭包函数,这个函数返回一个函数的变量类型。

我们可以看到定义了一个变量 n ,以及 return 返回一个匿名函数。我们可以看到匿名函数引用了外部的变量 n , 可以把这个 n 变量就做自由变量。

换句话说,这个函数和这个自由变量 n 组成一个整体,只要是在这个整体的声明周期内,这个 n 都是有效的。

下面我们看一下这个 add 函数的调用

func main() {
	f := add()
	fmt.Println(f(1))
	fmt.Println(f(10))
	fmt.Println(f(100))
}

// 输出结果
// 1
// 11
// 111

看调用的执行结果,正如上面所提到的,这个只要 add 函数也就是 f 这个对象没有消亡,那么 f 中的这个 n 就始终存在,也就是为什么第二次和第三次是在上自己调用后值的基础上做相加的原因了。

外部引用函数参数局部变量

package main

import "fmt"

// 外部引用函数参数局部n变量
func add(n int) func(int) int{

	return func(i int) int {
		n +=in
		return n
	}
}

func main() {
	f1 := add(10)
	fmt.Println(f1(1))
	fmt.Println(f1(10))

	// 此时f1 和 f2 不是一个实体
	f2 := add(1)
	fmt.Println(f2(100))
}

// 输出结果
// 11
// 21
// 101

这个了例子中参数 n 是由调用 add 函数传递的,和上一个例子中在外部函数内部声明变量 n 的效果是一样的。

例子:判断传入字符串,是否以某子串作为结尾

package main

import (
	"fmt"
	"strings"
)

func checkSuffix(suffix string) func(string) string {
	return func(filename string) string {
		if !strings.HasSuffix(filename, suffix) {
			return filename + suffix
		}
		return filename
	}
}

func main() {
	check := checkSuffix(".txt")
	fmt.Println(check("test"))
	fmt.Println(check("hello.txt"))
}

返回 2 个闭包

package main

import "fmt"

func test01(base int) (func(int) int, func(int) int) {
	add := func(n int) int {
		base = base + n
		return base
	}

	sub := func(n int) int {
		base = base - n
		return base
	}
	return add, sub
}

func main() {
	add, sub := test01(10)
	fmt.Println(add(1))
	fmt.Println(sub(3))
}

// 输出结果
// 11
// 8

这个例子中返回两个闭包函数,且这 2 个闭包函数都对一个外部传递的 base 值进行操作,每次调用其中的一个闭包函数都会在上一次调用后的 base 值结果上进行操作。

闭包的副作用

package main

import (
	"fmt"
	"time"
)

func main() {
	for i := 0; i < 5; i++ {
		go func() {
			// 导致异步输出的值重复
			fmt.Println(i)
		}()
	}
	time.Sleep(time.Second)
}

// 预期输出结果
// 0 1 2 3 4 5
// 实际输出结果
// 5 4 2 5 5 	// 每次结果输出不同

看上面的例子可以看到异步的闭包输出的不是我们预期的,所以在异步输出中值会出现重复

10. 函数练习

打印 1 到 100 所有的质数(大于 1 的自然数,除了 1 和它本身,不能被任何数整除)

package main

import "fmt"

// 判断是否为质数
func is_primeNumber(n int) bool {
	for j := 2; j < n; j++ {
		if n%j == 0 {
			return false
		}
	}
	return true
}

func main() {
	for i := 1; i <= 100; i++ {
		f := is_primeNumber(i)
		if f {
			fmt.Println(i)
		}

	}

}

打印 100 到 1000 所有的水仙花数(指一个数字,各位数字立方和等于该数本身,例如 153 是一个水仙花数,13+53+3^3=153)

package main

import "fmt"

// 判断是否为水仙花数
func is_narcissisticNumber(n int) bool {
	a := n / 100        // 百位
	b := (n % 100) / 10 // 十位
	c := n % 10	// 个位

	if (a*a*a + b*b*b + c*c*c) == n {
		return true
	}
	return false
}

func main() {

	for i := 100; i < 1000; i++ {
		f := is_narcissisticNumber(i)
		if f {
			fmt.Println(i)
		}
	}

}

传入一行字符,分别统计出其中英文字母、空格、数字和其他字符的个数

package main

import "fmt"

func count(str string) (e int, s int, n int, o int) {
	utfChar := []rune(str)
	for i := 0; i < len(utfChar); i++ {
		if (utfChar[i] >= 'a' && utfChar[i] <= 'z') || (utfChar[i] >= 'A' && utfChar[i] <= 'Z') {
			e++
			continue
		}
		if utfChar[i] >= '0' && utfChar[i] <= '9' {
			n++
			continue
		}
		if utfChar[i] == ' ' {
			s++
			continue
		}
		o++
	}
	return
}

func main() {
	e, s, n, o := count("Hello world 你好 123123 !!!")
	fmt.Printf("英文字符: %d, 空格: %d, 数字: %d, 其他字符: %d", e, s, n, o)
}
posted @ 2020-03-17 11:56  ZhiChao&  阅读(387)  评论(0编辑  收藏  举报