路漫漫其修远兮
吾将上下而求索

第6章总结:函数

6.1

==========重要=========
Go 里面有三种类型的函数:
普通的带有名字的函数
匿名函数或者lambda函数(参考 第 6.8 节)
方法(Methods,参考 第 10.6 节)

函数被调用的基本格式如下:
pack1.Function(arg1, arg2, …, argn)

Function 是 pack1 包里面的一个函数,括号里的是被调用函数的实参(argument):这些值被传递给被调用函数的形参(parameter,参考 第 6.2 节)。函数被调用的时候,
这些实参将被复制(简单而言)然后传递给被调用函数。函数一般是在其他函数里面被调用的,这个其他函数被称为调用函数(calling function)。函数能多次调用其他函数,
这些被调用函数按顺序(简单而言)执行,理论上,函数调用其他函数的次数是无穷的(直到函数调用栈被耗尽)。

假设 f1 需要 3 个参数 f1(a, b, c int),同时 f2 返回 3 个参数 f2(a, b int) (int, int, int),就可以这样调用 f1:f1(f2(a, b))。

函数是一等值(first-class value):它们可以赋值给变量,就像 add := binOp 一样。
这个变量知道自己指向的函数的签名,所以给它赋一个具有不同签名的函数值是不可能的。

6.2
在函数块里面,return 之后的语句都不会执行。如果一个函数需要返回值,那么这个函数里面的每一个代码分支(code-path)都要有 return 语句。

Go 默认使用按值传递来传递参数,也就是传递参数的副本。函数接收参数副本之后,在使用变量的过程中可能对副本的值进行更改,但不会影响到原来的变量,比如 Function(arg1)。

如果你希望函数可以直接修改参数的值,而不是对参数的副本进行操作,你需要将参数的地址(变量名前面添加&符号,比如 &variable)传递给函数,这就是按引用传递,比如
 Function(&arg1),此时传递给函数的是一个指针。 

在函数调用时,像切片(slice)、字典(map)、接口(interface)、通道(channel)这样的引用类型都是默认使用引用传递(即使没有显式的指出指针)。

当需要返回多个非命名返回值时,需要使用 () 把它们括起来,比如 (int, int)。
命名返回值作为结果形参(result parameters)被初始化为相应类型的零值,当需要返回的时候,我们只需要一条简单的不带参数的return语句。需要注意的是,即使只有一
个命名返回值,也需要使用 () 括起来

下面的函数具有典型性
func main() {
    numx2, numx3 = getX2AndX3(num)
    PrintValues()
    numx2, numx3 = getX2AndX3_2(num)
    PrintValues()
}

func PrintValues() {
    fmt.Printf("num = %d, 2x num = %d, 3x num = %d\n", num, numx2, numx3)
}

func getX2AndX3(input int) (int, int) {
    return 2 * input, 3 * input
}

#下面的函数是命名返回值
func getX2AndX3_2(input int) (x2 int, x3 int) {   #这里的变量不用在函数里面声明,可直接使用
    x2 = 2 * input
    x3 = 3 * input
    // return x2, x3
    return
}

尽量使用命名返回值:会使代码更清晰、更简短,同时更加容易读懂。

6.2.2中的例程第一次使用了error,并new了一个error

如果函数的某个返回值不需要,可以使用空白符用来匹配一些不需要的值,然后丢弃掉

传递指针给函数不但可以节省内存(因为没有复制变量的值),而且赋予了函数直接修改外部变量的能力,所以被修改的变量不再需要使用 return 返回。当是比较大的变量
的时候,性能优势会非常明显

6.3 
如果函数的最后一个参数是采用 ...type 的形式,那么这个函数就可以处理一个变长的参数,这个长度可以为 0,这样的函数称为变参函数。
func myFunc(a, b, arg ...int) {}
func Greeting(prefix string, who ...string)
Greeting("hello:", "Joe", "Anna", "Eileen")
在 Greeting 函数中,变量 who 的值为 []string{"Joe", "Anna", "Eileen"}。  #重要,这里是字符串数组
如果参数被存储在一个 slice 类型的变量 slice 中,则可以通过 slice... 的形式来传递参数,调用变参函数。

可以这样理解,函数接收的是多个同类型的值,但是函数内部会自动转换为数组?
一个接受变长参数的函数可以将这个参数作为其它函数的参数进行传递:
func F1(s ...string) {
	F2(s...)
	F3(s)
}

func F2(s ...string) { }
func F3(s []string) { }
变长参数可以作为对应类型的 slice 进行二次传递。

6.4
关键字 defer 允许我们推迟到函数返回之前(或任意位置执行 return 语句之后)一刻才执行某个语句或函数
(为什么要在返回之后才执行这些语句?因为 return 语句同样可以包含一些操作,而不是单纯地返回某个值)

func f() {
	for i := 0; i < 5; i++ {
		defer fmt.Printf("%d ", i)
	}
}
上面的代码将会输出:4 3 2 1 0。

关键字 defer 允许我们进行一些函数执行完成后的收尾工作,例如:关闭文件流
// open a file  
defer file.Close()

此处第一次出现匿名函数。是将返回值赋给n以后,才执行的func匿名函数,将n变量打印出来
func func1(s string) (n int, err error) {
	defer func() {
		log.Printf("func1(%q) = %d, %v", s, n, err)
	}()
	return 7, io.EOF
}

func main() {
	func1("Go")
}


6.5 
内置函数,不用导入包即可使用
close  len、cap  new、make  copy、append  panic、recover

6.6 

斐波那契
package main

import "fmt"

func main() {
	result := 0
	for i := 0; i <= 10; i++ {
		result = fibonacci(i)
		fmt.Printf("fibonacci(%d) is: %d\n", i, result)
	}
}

func fibonacci(n int) (res int) {
	if n <= 1 {
		res = 1
	} else {
		res = fibonacci(n-1) + fibonacci(n-2)
	}
	return
}

6.7 

函数可以作为其它函数的参数进行传递,然后在其它函数内调用执行,一般称之为回调。
func main(){
	callback(1,Add)
}

func Add(a, b int){
	fmt.Printf("the sum %d and %d is %d\n", a, b, a+b)
}

func callback(y int, f func(int, int)){
	f(y, 2)
}

6.8 

当不希望给函数起名字的时候,可以使用匿名函数,也叫闭包
例如:func(x, y int) int { return x + y }。这样的一个函数不能够独立存在(编译器会返回错误:non-declaration statement outside function body),但可以
被赋值于某个变量,即保存函数的地址到变量中:fplus := func(x, y int) int { return x + y },然后通过变量名对函数进行调用:fplus(3,4)。
当然,您也可以直接对匿名函数进行调用:func(x, y int) int { return x + y } (3, 4)。
表示参数列表的第一对括号必须紧挨着关键字 func,因为匿名函数没有名称。花括号 {} 涵盖着函数体,最后的一对括号表示对该匿名函数的调用。
麻痹,终于知道什么意思了

def和匿名函数结合使用
func f() (ret int) {
	defer func() {
		ret++
	}()
	return 1
}
func main() {
	fmt.Println(f())
}
变量 ret 的值为 2,因为 ret++ 是在执行 return 1 语句后发生的。


6.9 

func main() {
	var f = Adder()
	fmt.Print(f(1), " - ")
	fmt.Print(f(20), " - ")
	fmt.Print(f(300))
}

func Adder() func(int) int {
	var x int
	return func(delta int) int {
		x += delta
		return x
	}
}
函数 Adder() 现在被赋值到变量 f 中(类型为 func(int) int)。

go func(i int) {
	s := 0
	for j := 0; j < i; j++ { s += j }
	g = s
}(1000) // Passes argument 1000 to the function literal.

6.10 

包 runtime 中的函数 Caller() 提供了相应的调试信息
where := func() {
	_, file, line, _ := runtime.Caller(1)
	log.Printf("%s:%d", file, line)
}
where()
// some code
where()
// some more code
where()

6.11 
计算函数执行时间
start := time.Now()
longCalculation()
end := time.Now()
delta := end.Sub(start)
fmt.Printf("longCalculation took this amount of time: %s\n", delta)

未经允许不得转载:江哥架构师笔记 » 第6章总结:函数

分享到:更多 ()

评论 抢沙发

评论前必须登录!