Files
the-way-to-go_ZH_CN/eBook/06.1.md

5.1 KiB
Raw Blame History

#6.1 函数介绍 每一个程序都包含很多的函数:函数是基本的代码块。

Go是编译型语言所以函数编写的顺序是无关紧要的鉴于可读性的需求最好把 main() 函数写在文件的前面,其他函数按照一定逻辑顺序进行编写(例如函数被调用的顺序)。

编写多个函数的主要目的是将一个需要很多行代码的复杂问题分解为一系列简单的任务(那就是函数)来解决。而且,同一个任务(函数)可以被调用多次,有助于代码重用。

事实上好的程序是非常注意DRY原则的即不要重复你自己Don't Repeat Yourself意思是执行特定任务的代码只能在程序里面出现一次。

当函数执行到代码块最后一行(}之前)或者return语句的时候会退出,其中return语句可以带有零个或多个参数;这些参数将作为返回值(参考6.2节)供调用者使用。简单的return语句也可以用来结束for死循环或者结束一个协程goroutine

Go里面拥三种类型的函数

  • 普通的带有名字的函数
  • 匿名函数或者lambda函数参考6.8节
  • 方法Methods参考10.6节

所有类型的函数都可以有参数与返回值。函数参数、返回值以及它们的类型被统称为函数签名。

作为提醒,提前介绍一个语法:

这样是不正确的Go代码

func g()
{
}

它必须是这样的:

func g() {
}

函数被调用的基本格式如下:

pack1.Function(arg1, arg2, …, argn)

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

一个简单的函数调用其他函数的例子:

示例 6.1 greeting.go

package main

func main() {
    println("In main before calling greeting")
    greeting()
    println("In main after calling greeting")
}

func greeting() {
    println("In greeting: Hi!!!!!")
}

代码输出:

In main before calling greeting
In greeting: Hi!!!!!
In main after calling greeting

函数可以将其他函数调用作为它的参数,只要这个被调用函数的返回值个数、返回值类型和返回值的顺序与调用函数所需求的实参是一致的,例如:

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

函数重载function overloading指的是可以编写多个同名函数只要它们拥有不同的形参与/或者不同的返回值在Go里面函数重载是不被允许的。这将导致一个编译错误

funcName redeclared in this book, previous declaration at lineno

Go语言不支持这项特性的主要原因是函数重载需要进行多余的类型匹配影响性能没有重载意味着只是一个简单的函数调度。所以你需要给不同的函数使用不同的名字我们通常会根据函数的特征对函数进行命名参考11.12.5节)。

如果需要申明一个在外部定义的函数,你只需要给出函数名与函数签名,不需要给出函数体:

func flushICache(begin, end uintptr) // implemented externally

函数也可以以申明的方式被使用,作为一个函数类型,就像:

type binOp func(int, int) int

在这里,不需要函数体{}

函数是一等值first-class value它们可以赋值给变量就像add := binOp一样。

这个变量知道自己指向的函数的签名,所以给它赋一个具有不同签名的函数值是不可能的。

函数值functions value之间可以相互比较如果它们引用的是相同的函数或者都是nil的话则认为它们是相同的函数。函数不能在其它函数里面声明不能嵌套不过我们可以通过使用匿名函数参考6.8节)来破除这个限制。

目前Go没有泛型generic的概念也就是说它不支持那种支持多种类型的函数。不过在大部分情况下可以通过接口interface特别是空接口与类型选择type switch参考11.12节)与/或者通过使用反射reflection参考6.8节)来实现相似的功能。使用这些技术将导致代码更为复杂、性能更为地下,所以在非常注意性能的的场合,最好是为每一个类型单独创建一个函数,而且代码可读性更强。

##链接