go语言学习笔记(十一)——函数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sinat_32023305/article/details/82688932
  • 前言

       在Go语言中,函数是一等的公民,函数类型也是一等的数据类型。

       函数不但可以用于封装代码、分割功能、解耦逻辑,还可以作为参数值在函数间传递、赋给变量、做类型判断和转换等,

       函数值可以由此成为能够被随意传播的独立逻辑组件

  • 函数类型

函数类型是一种对一组输入、输出进行模板化的工具,使得函数值变成了可被热替换的逻辑组件,如例

package main
 
import "fmt"
 
type Printer func(content string) (n int, err error)
 
func printToStd(content string) (bytesNum int, err error){
    return fmt.Println(content)
}
 
func main(){
    var p Printer
    p = printToStd
    p("something")
}

type XXX func()()声明一个函数类型,这里函数名称和func互换了一下位置而已。

函数签名是函数的参数列表和结果列表的统称,它定义的可用来鉴别不同函数的那些特征,只要两个函数的参数列表和结果列表的元素顺序及其类型是一致的,那么他们就是一样的函数或者说是实现了同一个函数类型的函数

在上例中函数printToStd的签名和Printer的是一致的,因此printToSad其实是Printer的一个实现,把printToSad函数赋给了Printer类型的变量p,并且成功的调用了它。

函数类型是引用类型,它的值可以为nil,零值就是nil。

  • 高阶函数

什么是高阶函数?

1,接受其他的函数作为参数返回

2,把其他的函数作为结果返回

只要满足这两个中的一个,这个函数就是一个高阶函数。

如何编写高阶函数?

(1)接受其他函数作为参数返回的用法

比如写出一个函数代码,通过calculate函数实现两个整数间的加减乘除运算,但是希望两个整数和具体的操作都由该函数的调用方给出。

首先,声明一个函数类型:

type operate func(x, y int) int

然后,编写calculate函数的签名部分(函数名,输入、输出参数),输入参数中接受函数类型。

func calculate(x int, y int, op operate) (int, error){
    if op == nil {
        return 0, errors.New("Invalid operation")
    }
    return op(x, y), nil
}

我们把函数作为参数在其他函数间传递,如op就是一个operate类型的参数。

也可以编写匿名函数,如:

    op := func(x,y int) int {
        return x+y
    }

 (2)把其他的函数作为结果返回

package main

import (
	"errors"
	"fmt"
)

type operate func(x, y int) int

// 方案1。
func calculate(x int, y int, op operate) (int, error) {
	if op == nil {
		return 0, errors.New("invalid operation")
	}
	return op(x, y), nil
}

// 方案2。
type calculateFunc func(x int, y int) (int, error)

func genCalculator(op operate) calculateFunc {
	return func(x int, y int) (int, error) {
		if op == nil {
			return 0, errors.New("invalid operation")
		}
		return op(x, y), nil
	}
}

func main() {
	// 方案1。
	x, y := 12, 23
	op := func(x, y int) int {
		return x + y
	}
	result, err := calculate(x, y, op)
	fmt.Printf("The result: %d (error: %v)\n",
		result, err)
	result, err = calculate(x, y, nil)
	fmt.Printf("The result: %d (error: %v)\n",
		result, err)

	// 方案2。
	x, y = 56, 78
	add := genCalculator(op)
	result, err = add(x, y)
	fmt.Printf("The result: %d (error: %v)\n",
		result, err)
}
  • 闭包实现

       闭包是在一个函数中存在对外来标识符的引用。外来标识符即不代表当前函数的任何参数或结果的变量(又称自由变量),是直接从外边拿过来的。表达式:func () (func())

    func genCalculator(op operate) calculateFunc {
        return func(x int, y int) (int, error) {
            if op == nil {
                return 0, errors.New("Invalid operation")
            }
            return op(x, y), nil
        }
    }

上述示例中genCalculator函数内部,就实现了一个闭包,而其genCalculator函数本身就是一个高阶函数。其内部的外来标识符有x,y,op,其中op是一个自由变量,只有在genCalculator函数被调用的时候,op参数才能知道代表什么。当执行到if op == nil这行时,go语言编译器读到这里会试图去寻找op所代表的东西,它会发现op代表的是genCalculator函数的参数,如此一来这个闭包函数的状态就由“不确定”变成了“确定”,或者说转到了“闭合”状态,由此也就真正形成了一个闭包。

实现闭包的意义

表面上看,我们只是延迟实现了一部分的程序逻辑或功能而已,但实际上,我们是在动态的生成那部分的程序逻辑。我们可以使用闭包在程序运行的过程中,根据需要生成功能不同的函数,并影响后续的程序行为。类似GoF设计模式中的模板方法。

  • 传参

package main
 
import (
	"fmt"
)
 
func modifyArray(a [3]string) [3]string {
	a[1] = "x"
	return a
}
 
func main() {
	a1 := [3]string{"a", "b", "c"}
	fmt.Printf("The array a1 is: %v\n", a1)
	a2 := modifyArray(a1)
	fmt.Printf("The modified array of a2 is: %v\n", a2)
	fmt.Printf("The original a1 is: %v\n", a1)
}

该示例中,所有传给函数的参数值都会被复制,函数在其内部使用的并不是参数值的原值,而是它的副本。

在本例中,修改的知识原数组的副本而已,不会对原数组造成影响。

打印结果为

The array: [a b c]
The modified array: [a x c]
The original array: [a b c]

对于引用类型,如切片、字典、通道(chan),都是浅表复制,只会拷贝它们本身,而不会拷贝底层数组。

对于值类型的参数值,有些情况会被改变。如下例

package main
 
import (
	"fmt"
)
 
func modifyComplexArray(a [3][]string) [3][]string {
	a[1][1] = "s"
	a[2] = []string{"o", "p", "q"}
	return a
}
 
func main() {
    complexArray1 := [3][]string{
		[]string{"d", "e", "f"},
		[]string{"g", "h", "i"},
		[]string{"j", "k", "l"},
	}
    fmt.Printf("The complex array: %v\n", complexArray1)
    complexArray2 := modifyComplexArray(complexArray1)
    fmt.Printf("The modified complex array: %v\n", complexArray2)
    fmt.Printf("The original complex array: %v\n", complexArray1)
}

打印结果为

The complex array: [[d e f] [g h i] [j k l]]
The modified complex array: [[d e f] [g s i] [o p q]]
The original complex array: [[d e f] [g s i] [j k l]]
  • 思考题

1,函数真正拿到的参数值只是它们的副本,那么函数返回给调用方的结果值也会被复制吗?

答:是复制的,不是原值。可以从引用地址看出来

猜你喜欢

转载自blog.csdn.net/sinat_32023305/article/details/82688932