在Go语言中,函数是一等公民,函数类型也是一等的数据类型。简单来讲,这意味着:函数不但能够用于封装代码,分割功能,解耦逻辑,还能够化身为普通的值,在其余函数间传递、赋予变量、作类型判断和转换等等。编程
package main import "fmt" type Printer func(contents string) (n int, err error) func printToStd(contents string) (bytesNum int, err error) { return fmt.Println(contents) } func main() { var p Printer p = printToStd p("something") }
函数的什么使用type
关键字。type后面是函数类型名称,紧接着是func关键字。func右边的就是这个函数类型的参数列表和结果列表。数组
函数的签名就是函数的参数列表和结果列表的统称。各个参数和结果的名称不能算作函数签名的一部分,甚至结果列表能够没有名称。严格来讲,函数的名称也不能算做函数签名的一部分,它只是咱们在调用函数时须要给定的标识符而已。闭包
只要两个函数的参数列表和结果列表中的元素顺序及其类型是一致的,咱们就能够说它们是同样的函数,或者说它们是实现了同一个函数类型的函数。函数式编程
上面的代码中printToStd的签名和Printer的是一致的,所以前者是后者的一个实现。函数
Go语言在语言层面支持了函数式编程。指针
知足下面的两个条件的任意一个的函数就是高阶函数:code
type operate func(x, y int) int func add(x,y int)int { return x + y } func calculate(x int, y int, op operate) (int, error) { if op == nil { return 0, errors.New("invalid operation") } return op(x, y), nil }
上面的calculate函数就是一个高阶函数。编译器
在一个函数中,有一个外来标识符,它既不是函数的参数,也不是函数的结果,更不是函数内部声明的。它是直接从外边拿过来的。专业属于称呼为“自由变量”string
闭包函数就是由于引用了自由变量,而呈现出一种“不肯定”的状态,也叫“开放”状态。也就是说闭包的内部逻辑并非完整的,有一部分逻辑须要这个自由变量参与完成,而自由变量到底表明了什么在闭包函数被定义的时候是未知的。在Go语言定义闭包函数的时候最多知道自由变量的类型。io
type operate func(x, y int) int type calculateFunc func(x, y int) (int, error) func genCalculator(op operate) calculateFunc { return func(x, y int) (i int, e error) { if op == nil { return 0, errors.New("invalid opration") } return op(x, y), nil } } func main(){ op := func(x, y int) int { return x + y } add := genCalculator(op) result, err := add(3, 4) fmt.Printf("the result: %d (error: %v)\n",result,err) }
genCalculator函数内部就实现了闭包:那就是定义一个匿名的calculateFunc类型的函数并把它做为结果值返回。这个匿名的函数就是一个闭包函数。由于这个匿名函数里使用的变量op既不是它的任何参数或结果,也不是它本身声明的,而是定义它的genCalculator函数的参数,因此op在匿名函数中是一个自由变量。
这个自由变量究竟表明了什么,这一点并非在定义这个闭包函数的时候肯定的,而是在genCalculator函数被调用的时候肯定的。只有给定了该参数op,咱们才直到它返回给咱们的闭包函数能够用于什么计算。
if op == nil
Go语言编译器读到这里的时候会试图去寻找op锁表明的东西,它会发现op表明的是genCalculator函数的参数,而后,编译器就会把二者联系起来,这时能够说:自由变量op被“捕获”了。当程序运行到这里,op就是genCalculator的参数值了,这时,闭包函数的状态就由“不肯定”变为了“肯定”,或者说转到了“闭合”状态,至此,也就真正造成了一个闭包。
表面上看,咱们只是延迟实现了一部分程序逻辑或功能而已。
实际上,咱们是在动态地生成那部分逻辑功能。
package main import "fmt" func main() { array1 := [3]string{"a", "b", "c"} fmt.Printf("The array: %v\n", array1) array2 := modifyArray(array1) fmt.Printf("The modified array: %v\n", array2) fmt.Printf("The original array: %v\n", array1) } func modifyArray(a [3]string) [3]string { a[1] = "x" return a }
原数组不变。
缘由是全部传递给函数的参数值都会被复制,函数在其内部使用的并非参数值的原值,而是它的副本。
注意,对于引用类型,切片,字典,通道,复制的是它们自己的值,不会复制它们引用的底层数据,也就是说,这只是浅表复制,不是深层复制。Go语言没有深层复制。
对于值类型的数组,若是它的元素类型是引用类型,当这种数组被传入函数的话,函数中对该参数值的修改会影响到参数吗?例如[3][]string
若是修改数组中的切片的某个元素,会影响原数组;若是修改数组的某个元素就不会影响原数组。
当函数返回指针类型时,不会发生拷贝。当函数返回非指针类型并把结果赋值给其余变量时,会发生拷贝。