函数

一、什么是函数

  • 简单来说就是一个特定的方法,通过这个方法产生预期的结果。
  • 函数是一块执行特定任务的代码。一个函数是在输入源基础上,通过执行一系列的算法,生成预期的输出

二、函数的定义

1. 定义函数的语法

func 函数名(参数1 参数1类型,参数2 参数2类型) 返回值类型 {
    // 函数体(代码块)
}

1.1 若参数类型一致或返回值类型一致,则可简写

func 函数名(参数1,参数2 参数类型) (返回值1,返回值2 返回值类型) {
    // 函数体(代码块)
}

1.2 示例,定义一个计算总价的函数
func calculateBill(price int, no int) int {  
    var totalPrice = price * no // 商品总价 = 商品单价 * 数量
    return totalPrice // 返回总价
}

2. *****************函数中的参数列表和返回值并非是必须的,所以下面这个函数的声明也是有效的***************
func test1() {  
    // 译注: 表示这个函数不需要输入参数,且没有返回值
}

3. 若参数或者返回值都为同一个类型的,则可以简写
func 函数名(参数1,参数2 参数类型...) 返回值类型 {
    // 函数体(代码块)
}

三、函数的参数

  • 注意:函数的参数传递都是拷贝传递,相当于将参数的值拷贝一份,传入函数,所以要想在函数内部同步修改参数本身,就要用到指针

  • 和python不同的是,go中没有位置参数和关键字参数的概念,go中都是按照位置传参

  • 只需要熟悉 可变长参数函数

  • 函数作为参数

    • func add(x, y int) int {
      	return x + y
      }
      func calc(x, y int, op func(int, int) int) int {
      	return op(x, y)
      }
      func main() {
      	ret2 := calc(10, 20, add)
      	fmt.Println(ret2) //30
      }
      

四、函数的返回值

  • 和python不同的是,GO中对函数的返回值的类型也必须提前写死
  • 想接收一个函数的返回值,该函数有几个返回值必须要声明几个变量来接收,也可以用匿名变量(或者叫空白符) _ 来接收某个返回值

1. 多个返回值

  • go中是支持多个返回值的,返回值类型用括号括起来即可
1. 语法是

func 函数名(参数1 参数1类型,参数2 参数2类型) (返回值1类型,返回值2类型) {
    // 函数体(代码块)
    return 值1,值2...
}

1.1 示例,定义一个计算矩形的周长和面积的函数

package main

import (  
    "fmt"
)

func rectProps(length, width float64)(float64, float64) {  
    var area = length * width
    var perimeter = (length + width) * 2
    return area, perimeter
}

func main() {  
    area, perimeter := rectProps(10.8, 5.6)
    fmt.Printf("Area %f Perimeter %f", area, perimeter) 
}

2. 命名返回值

  • 在定义函数时,若指定了返回值的变量名,则在函数体中,也不用重新声明该返回的变量,不需要再明确指定返回值
1. 若返回值都为同一个类型的,则可以简写
func 函数名(参数1,参数2 参数类型) (返回值1,返回值2 返回值类型) {
    // 函数体(代码块)
}

1.1 如下面的rectProps方法
func rectProps(length float64, width float64)(area float64, perimeter float64) {  
    area = length * width
    perimeter = (length + width) * 2
    return // 不需要明确指定返回值,默认返回 area, perimeter 的值
}

1.1.1 可以简写为:
func rectProps(length, width float64)(area, perimeter float64) {  
    area = length * width  // 直接赋值,不用声明
    perimeter = (length + width) * 2  // 直接赋值,不用声明
    return // 不需要明确指定返回值,默认返回 area, perimeter 的值
}

3. 返回值补充

(1)nil作为返回值时

  • 当我们的一个函数返回值类型为切片时,nil可以看做是一个有效的,没必要显示返回一个长度为0的切片

    • 其他空值也为nil的如 map 、指针等,也同样适用上述规则
    func someFunc(x string) []int {
    	if x == "" {
    		return nil // 没必要返回[]int{}
    	}
    	...
    }
    

(2)函数作为返回值

func do(s string) (func(int, int) int, error) {
	switch s {
	case "+":
		return add, nil
	case "-":
		return sub, nil
	default:
		err := errors.New("无法识别的操作符")
		return nil, err
	}
}

五、匿名函数

  • 和python中的匿名函数,差别很大,Go中的匿名函数没有关键字,相当于用变量去存储一个没名字的正常函数,代码格式类似python中的闭包函数
  • 同普通函数一样,匿名函数的参数类型和返回值的类型都是该匿名函数类型的一部分
  • 匿名函数因为没有函数名,所以没办法像普通函数那样调用,所以匿名函数需要保存到某个变量或者作为立即执行函数

1. 匿名函数的定义

  • 匿名函数因为没有函数名,所以没办法像普通函数那样调用,所以匿名函数需要保存到某个变量或者作为立即执行函数

  • 加括号可以直接调用

  • 下面是没有返回值的闭包函数

语法: 变量名 := func(参数 类型) 返回值类型{函数体}

// 示例:

package main

import (
	"fmt"
)

func main() {
	// 将匿名函数保存到变量
	add := func(x, y int) {
		fmt.Println(x + y)
	}
	add(10, 20) // 通过变量调用匿名函数
 
	//自执行函数:匿名函数定义完加()直接执行
	func(x, y int) {
		fmt.Println(x + y)
	}(10, 20)
}

2. 闭包函数

  • 闭包函数指的是一个函数和它引用环境组合在一起,形成的一个组合体,该组合体称为闭包函数。简而言之,闭包函数=函数+引用环境

  • 闭包一定是嵌套函数,但嵌套函数不一定是闭包函数

  • 闭包函数的概念就使得其有个特点,即在闭包函数的内部,对外层的作用域有引用。意思就是闭包函数内部使用外层的变量都是使用的地址,而非拷贝的值

package main

import (
	"fmt"
)

func test3(a, b int) func(a, b string) (int, int, int) {
	fmt.Println("test3 a,b:", a, b)
	fmt.Printf("test3 a,b的地址: %p %p\n", &a, &b) // %p 的作用是打印地址

	f := func(x, y string) (int, int, int) {
		fmt.Println("x,y:", x, y)
		fmt.Printf("匿名函数内部 a,b的地址: %p %p\n", &a, &b)
		return 3, 4, 5
	}

	return f
}


func main() {
    a, b := 1, 2
	println("外部a,b地址:", &a, &b)
	f := test3(a, b)
	fmt.Println("f:", f)
	f("xxx", "yyy")
}

/*
打印结果:

外部a,b地址: 0xc000107f10 0xc000107f08         
test3 a,b: 1 2                                   
test3 a,b的地址: 0xc00000a0d8 0xc00000a0f0       
f: 0xafb1a0                                      
x,y: xxx yyy                                     
匿名函数内部 a,b的地址: 0xc00000a0d8 0xc00000a0f0


可以看出,test3 a,b的地址 和 匿名函数内部 a,b的地址 是一样的,但它们和test3外部的x,y的地址是不同的
*/

(1)闭包函数简单示例

package main

import (
	"fmt"
)

func adder() func(int) int {
	var x int
	return func(y int) int {
		x += y
		return x
	}
}
func main() {
	var f = adder()
	fmt.Println(f(10)) //10
	fmt.Println(f(20)) //30
	fmt.Println(f(30)) //60
 
	f1 := adder()
	fmt.Println(f1(40)) //40
	fmt.Println(f1(50)) //90
}

// 注意每次的打印结果

(2)闭包函数进阶示例

i. 示例1

package main

import (
	"fmt"
)

func adder2(x int) func(int) int {
	return func(y int) int {
		x += y
		return x
	}
}
func main() {
	var f = adder2(10)
	fmt.Println(f(10)) //20
	fmt.Println(f(20)) //40
	fmt.Println(f(30)) //70
 
	f1 := adder2(20)
	fmt.Println(f1(40)) //60
	fmt.Println(f1(50)) //110
}

// 注意每次的打印结果

ii. 示例2

package main

import (
	"fmt"
)

func makeSuffixFunc(suffix string) func(string) string {
	return func(name string) string {
		if !strings.HasSuffix(name, suffix) {
			return name + suffix
		}
		return name
	}
}
 
func main() {
	jpgFunc := makeSuffixFunc(".jpg")
	txtFunc := makeSuffixFunc(".txt")
	fmt.Println(jpgFunc("test")) //test.jpg
	fmt.Println(txtFunc("test")) //test.txt
}

// 注意每次的打印结果

iii. 示例3

package main

import (
	"fmt"
)

func calc(base int) (func(int) int, func(int) int) {
	add := func(i int) int {
		base += i
		return base
	}
 
	sub := func(i int) int {
		base -= i
		return base
	}
	return add, sub
}
 
func main() {
	f1, f2 := calc(10)
	fmt.Println(f1(1), f2(2)) //11 9
	fmt.Println(f1(3), f2(4)) //12 8
	fmt.Println(f1(5), f2(6)) //13 7
}

// 注意每次的打印结果
posted @ 2024-02-21 17:04  BigSun丶  阅读(19)  评论(0)    收藏  举报