1.golang函数
1.1 golang函数简介
函数的go语言中的 一级公民
,我们吧所有的功能单元都定义在函数中,可以重复使用。函数包含函数的名称、参数列表和返回值类型,这些构成了函数的签名(signature)
go语言中函数特性
1.go语言中有3中函数:普通函数、匿名函数、匿名函数(没有名称的函数)、方法(定义在struct上的函数)
2.go语言中不允许函数重载(overload),即不允许函数同名。
3.go语言中的函数不能嵌套函数,但可以嵌套匿名函数。
4.函数是一个值,可以将函数赋值给变量,使得这个变量也称为函数。
5.函数可以作为参数传递给另一个函数。
6.函数的返回值可以是一个函数
7.函数调用的时候,如果有参数传递给函数,则先拷贝参数的副本,再将副本传递给函数。
8.函数参数可以没有名称。
1.2 函数的定义和调用
函数在使用之前必须先定义,可以调用函数来完成某个任务。函数可以重复调用,从而达到代码重用。
函数定义语法
func function_name([parameter list]) [return_types]
{
|函数体
}
func function_name([parameter list]) [return_types]
{
|函数体
}
- 解析
func:函数由 func
开始声明
function_name:函数名称,函数名和参数列表一起构成了函数签名。
[parameter list]
:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
return_types:
返回类型,函数返回一列值。return_types
是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types
不是必须的。
函数体:函数定义的代码集合。
常见函数写法
// func 函数名称(接收的参数 参数的类型,参数2 类型2)
// func函数名称(参数1,参数2,参数3类型)(返回值1,返回值2 类型){代码块/函数体}
// func函数名称(参数1,参数2,参数3类型){代码块/函数体}
// func函数名称(参数1,参数2,参数3类型)类型{代码块/函数体}
// func 函数名称(接收的参数 参数的类型,参数2 类型2)
// func函数名称(参数1,参数2,参数3类型)(返回值1,返回值2 类型){代码块/函数体}
// func函数名称(参数1,参数2,参数3类型){代码块/函数体}
// func函数名称(参数1,参数2,参数3类型)类型{代码块/函数体}
go函数调用
当我们要完成某个任务时,可以调用函数来完成。调用函数传递参数,如何有返回值可以获得返回值。
func main(){
w := comp(4, 5)
fmt.Printf("w: %v\n", w)
}
func main(){
w := comp(4, 5)
fmt.Printf("w: %v\n", w)
}
实例
求和函数
func sumFunc(a int, b int) (ret int) {
ret = a + b
return ret
}
func main() {
r := sumFunc(1, 2)
fmt.Printf("r: %v\n", r)
}
func sumFunc(a int, b int) (ret int) {
ret = a + b
return ret
}
func main() {
r := sumFunc(1, 2)
fmt.Printf("r: %v\n", r)
}
比较函数
func comp(a int, b int) (max int) {
if a > b {
max = a
} else {
max = b
}
return max
}
func main() {
w := comp(4, 5)
fmt.Printf("w: %v\n", w)
}
func comp(a int, b int) (max int) {
if a > b {
max = a
} else {
max = b
}
return max
}
func main() {
w := comp(4, 5)
fmt.Printf("w: %v\n", w)
}
2.golang函数的返回值
函数可以有0或多个返回值,返回值需要指定数据类型,返回值通过 return
关键字来指定。
return
可以有参数,也可以没有参数,这些返回值可以有名称,也可以没有名称。go中的函数可以有多个返回值。
1.return
关键字中指定了参数时,返回值可以不用名称。如果 return
省略参数,则返回值部分必须带名称
2.当返回值有名称时,必须使用小括号包围,逗号分割,即使只有一个返回值
3.但即使返回值命名了,return
中也可以强制指定其它返回值的名称,也就是说 return
的优先级更高
4.命名的返回值是预先声明好的,在函数内部可以直接使用,无需再次声明。命名返回值的名称不能和函数参数名称相同,否则报错提示变量重复定义。
5.return
中可以有表达式,但不能出现赋值表达式,这和其它语言可能有所不同。列如 return a+b
是正确的。但 return c=a+b
是错误的
返回值实例
没有返回值
func fm1() {
fmt.Printf("我没有返回值")
}
func main(){
fm1()
}
func fm1() {
fmt.Printf("我没有返回值")
}
func main(){
fm1()
}
有一个返回值
func one(a int, b int) (ret int) {
ret = a + b
return ret
}
//或者
func one1(a int, b int) int {
//ret := a + b
//或者, 直接 return a+b
return a + b
}
func main() {
r := 1 + 2
fmt.Printf("r: %v\n", r)
}
func one(a int, b int) (ret int) {
ret = a + b
return ret
}
//或者
func one1(a int, b int) int {
//ret := a + b
//或者, 直接 return a+b
return a + b
}
func main() {
r := 1 + 2
fmt.Printf("r: %v\n", r)
}
多个返回值,且在return中指定返回的内容
func two() (name string, age int) {
name = "hell"
age = 20
return name, age
}
func main() {
name, age := two()
fmt.Printf("name: %v\n", name)
fmt.Printf("age: %v\n", age)
}
func two() (name string, age int) {
name = "hell"
age = 20
return name, age
}
func main() {
name, age := two()
fmt.Printf("name: %v\n", name)
fmt.Printf("age: %v\n", age)
}
多个返回值,返回值名称没有被使用
func f3() (name string, age int) {
name = "hell"
age = 20
return //等同于 return name, age
}
func main() {
name, age := two()
fmt.Printf("name: %v\n", name)
fmt.Printf("age: %v\n", age)
}
func f3() (name string, age int) {
name = "hell"
age = 20
return //等同于 return name, age
}
func main() {
name, age := two()
fmt.Printf("name: %v\n", name)
fmt.Printf("age: %v\n", age)
}
return覆盖命名返回值,返回值名称没有被使用
func two() (name string, age int) {
n := "ha"
a := 30
//如果是上面这样写,return必须写返回值
return n, a
}
func main() {
n, a := two()
fmt.Printf("name: %v\n", n)
fmt.Printf("age: %v\n", a)
}
func two() (name string, age int) {
n := "ha"
a := 30
//如果是上面这样写,return必须写返回值
return n, a
}
func main() {
n, a := two()
fmt.Printf("name: %v\n", n)
fmt.Printf("age: %v\n", a)
}
Go中经常会使用其中一个返回值作为函数是否执行成功、是否有错误信息的判断条件。例如:
value,exists
、return value,ok
、return value,err
等
当函数的返回值过多时,例如有4个以上的返回值,应该将这些返回值收集到容器中,然后以返回容器的方式去返回。例如,同类型的返回值可以放进slice中,不同类型的返回值可以放进map中。
但函数有多个返回值时,如果其中某个或某几个返回值不想使用,可以通过下划线
_
来丢弃这些返回值。例如下面的f1
函数两个返回值,调用该函数时,丢弃了第二个返回值b,只保留了第一个返回值a赋值给了变量a
func f1()(int ,int){
return 1,2
}
func main(){
_, x:=f1()
fmt.Printf(x)
}
func f1()(int ,int){
return 1,2
}
func main(){
_, x:=f1()
fmt.Printf(x)
}
3.golang函数的参数
- 无参无返回值函数
- 有一个参数的函数
- 有两个参数的函数
- 有一个返回值的函数
- 有多个返回值的函数
go函数可以有0或多个参数,参数需要指定 数据类型
声明函数时的参数参数列表叫做 形参
,调用时传递的参数叫做 实参
。
go语言是通过传值的方式传参的,意味着传递给函数的是拷贝后的副本,所以函数内部访问、修改的也是这个副本。
go语言可以使用 变长参数
,有时候并不能确定参数的个数,可以使用变长参数,可以在函数定义语句的参数部分使用 ARGS...TYPE
的方式。这时会将 ...
代表的参数全部保存到一个名为ARGS的slice中,注意这些参数的数据类型都是TYPE。
3.1实例
形参,实参
// 形参
func one1(a int, b int) int {
//ret := a + b
//或者, 直接 return a+b
return a + b
}
func main(){
// 实参
r = one1(2, 4)
fmt.Printf("r: %v\n", r)
}
// 形参
func one1(a int, b int) int {
//ret := a + b
//或者, 直接 return a+b
return a + b
}
func main(){
// 实参
r = one1(2, 4)
fmt.Printf("r: %v\n", r)
}
按值传递
//copy
func f1copy(a int) {
a = 100
fmt.Printf("a: %v\n", a)
}
func main() {
x := 200
f1copy(x)
fmt.Printf("x: %v\n", x)
}
//copy
func f1copy(a int) {
a = 100
fmt.Printf("a: %v\n", a)
}
func main() {
x := 200
f1copy(x)
fmt.Printf("x: %v\n", x)
}
从运行结果可以看到,调用函数f1copy后, a的值并没有被改变,说明参数传递是拷贝了一个副本,也就是拷贝了一份新的内容进行运算
map
、slice
、interface
、channel
这些数据类型本身就是指针类型的,所以计算是拷贝传值也是拷贝的指针,拷贝后的参数仍然指向底层数据结构,所以修改它们可能
会影响外部数据结构的值。
//map
func f1map(s []int) {
s[0] = 1000
}
func main() {
s := []int{1, 2, 3}
f1map(s)
fmt.Printf("s: %v\n", s)
}
//运行结果,调用函数后,slice内容被改变了
s: [1000 2 3]
//map
func f1map(s []int) {
s[0] = 1000
}
func main() {
s := []int{1, 2, 3}
f1map(s)
fmt.Printf("s: %v\n", s)
}
//运行结果,调用函数后,slice内容被改变了
s: [1000 2 3]
变长参数
func argFunc(args ...int) {
for _, v := range args {
fmt.Printf("v: %v\n", v)
}
}
func main() {
argFunc(1, 2, 3, 4)
argFunc(7, 8, 9)
}
func argFunc(args ...int) {
for _, v := range args {
fmt.Printf("v: %v\n", v)
}
}
func main() {
argFunc(1, 2, 3, 4)
argFunc(7, 8, 9)
}
// 传过来参数长度不定
func sum(args ...string) string {
// 接收到的是切片
fmt.Println("args:", args)
fmt.Printf("接收到的数据类型args type:%T\n", args)
msg := strings.Join(args, "--")
fmt.Println("拼接之后的字符串:", msg)
return msg
}
func main() {
fmt.Println("具有函数名字")
res := qiuhe(10, 20)
fmt.Println("res:", res)
sum("hello", "world", "go")
}
// 传过来参数长度不定
func sum(args ...string) string {
// 接收到的是切片
fmt.Println("args:", args)
fmt.Printf("接收到的数据类型args type:%T\n", args)
msg := strings.Join(args, "--")
fmt.Println("拼接之后的字符串:", msg)
return msg
}
func main() {
fmt.Println("具有函数名字")
res := qiuhe(10, 20)
fmt.Println("res:", res)
sum("hello", "world", "go")
}
4.函数类型和函数变量
可以使用 type
关键字来定义一个函数类型,语法:
type fun func(int,int)int
fun --->起名字
type fun func(int,int)int
fun --->起名字
上面语句定义了个 fun
函数类型,它是一种函数类型,这种函数接收连个 int
类型的参数并且返回一个 int
类型的返回值
func sum(a int, b int)int{
return a + b
}
func max(a int, b int) int{
if a > b {
return a
}else{
return b
}
}
func sum(a int, b int)int{
return a + b
}
func max(a int, b int) int{
if a > b {
return a
}else{
return b
}
}
下面定义一个 fun
函数类型,吧 sum
和 max
复制给它
func Sum(a int, b int) int {
return a + b
}
func Max(a int, b int) int {
if a > b {
return a
} else {
return b
}
}
func main() {
type f11 func(int, int) int
var ff f11
ff = Sum
r := ff(1, 3)
fmt.Printf("r: %v\n", r)
ff = Max
r = Max(1, 3)
fmt.Printf("r: %v\n", r)
}
func Sum(a int, b int) int {
return a + b
}
func Max(a int, b int) int {
if a > b {
return a
} else {
return b
}
}
func main() {
type f11 func(int, int) int
var ff f11
ff = Sum
r := ff(1, 3)
fmt.Printf("r: %v\n", r)
ff = Max
r = Max(1, 3)
fmt.Printf("r: %v\n", r)
}
5.高阶函数
go语言的函数,可以作为函数的参数,传递给另外一个函数,作为另外一个函数的返回值返回。
5.1函数作为参数
func sayHello(name string) {
fmt.Printf("hello,%s", name)
}
func test(name string, f func(string)) {
f(name)
}
func main() {
test("tom", sayHello)
}
func sayHello(name string) {
fmt.Printf("hello,%s", name)
}
func test(name string, f func(string)) {
f(name)
}
func main() {
test("tom", sayHello)
}
5.2go语言函数作为返回值
func add(x, y int) int {
return x + y
}
func sub(x, y int) int {
return x - y
}
func cal(oper string) func(int, int) int {
switch oper {
case "+":
return add
case "-":
return sub
default:
return nil
}
}
func main() {
f := cal("+")
r := f(1, 2)
fmt.Printf("r: %v\n", r)
f = cal("-")
r = f(3, 2)
fmt.Printf("r: %v\n", r)
}
func add(x, y int) int {
return x + y
}
func sub(x, y int) int {
return x - y
}
func cal(oper string) func(int, int) int {
switch oper {
case "+":
return add
case "-":
return sub
default:
return nil
}
}
func main() {
f := cal("+")
r := f(1, 2)
fmt.Printf("r: %v\n", r)
f = cal("-")
r = f(3, 2)
fmt.Printf("r: %v\n", r)
}
6.匿名函数
go语言函数不能嵌套,但是在函数内部可以定义匿名函数,实现一下简单功能调用。
所谓匿名函数就是,没有名称的函数。
func(参数列表)(返回值)
func(参数列表)(返回值)
当然可以既没有参数,可以没有返回值
- 实例
func main(){
max := func(a int, b int)int {
if a > b{
return a
}else{
return b
}
}
r:=max(1,2)
fmt.Printf(r)
}
//或者,自己调用自己
func main(){
r:=func(a int, b int)int{
if a >b{
return a
}else{
return b
}
}(1,2)
fmt.Printf(r)
}
func main(){
max := func(a int, b int)int {
if a > b{
return a
}else{
return b
}
}
r:=max(1,2)
fmt.Printf(r)
}
//或者,自己调用自己
func main(){
r:=func(a int, b int)int{
if a >b{
return a
}else{
return b
}
}(1,2)
fmt.Printf(r)
}
- 在函数内部做一些运算
func test1(){
name := "tom"
age := "20"
//
f1 := func()string{
return name + age
}
msg:=f1()
fmt.Printf("msg:%v\n",msg)
}
func test1(){
name := "tom"
age := "20"
//
f1 := func()string{
return name + age
}
msg:=f1()
fmt.Printf("msg:%v\n",msg)
}