go 语言 函数

1.函数声明:

func function_name (parameter list) (return_types) {

  函数体

}

  • func:函数由 func 开始声明
  • function_name:函数名称,函数名和参数列表一起构成了函数签名。
  • parameter list:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
  • return_types:返回类型,函数返回一列值。return_types 是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types 不是必须的。
  • 函数体:函数定义的代码集合

下面的更进一步的说明:(跟上面是一样的,都是函数的声明)

func funcName(形参1 type[, 形参2 type...]) [([[返回变量1] type[, [返回变量2] type...]])] {
    [return [返回变量1[, 返回变量2...]]]
  }
  a. 如果形参类型都一样,可以这样写: 形参1, 形参2 type, 同时返回变量也一样
  b. 如果只有一个返回值或者无返回值, 可以去掉对应的()
  c. 如果返回有返回值,该函数中最外层要有return语句
  d. 返回语句(), 返回变量名可以省略
  e. []表示可省略
  f. 不支持默认参数

2. 函数的特点:

1)函数即是变量

x:=1

y:=x

上面的变量的赋值,即函数也可以像变量一样赋值

package main

import(
	"fmt"
)
func Add(a,b int)int{
	
	return a + b
}
func main(){
	var c func(int,int) int
	c =Add
	fmt.Printf("%p %T %p %T\n",c,c,Add,Add)//0x497fe0 func(int, int) int 0x497fe0 func(int, int) int   c和Add的内存地址是同一个,
	//说明c和Add使用同一块内存地址里保存的内容。c = Add只是又给内存的一块地址增加了一个名字。
	sum:=c(10,20)
	fmt.Printf("sum的值是:%d\n",sum)//30
	sum=Add(10,20)
	fmt.Printf("sum的值是:%d\n",sum)//30  c 和Add是等效的
}

  2)回调函数 

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

函数指针是指向函数的指针变量。因此“函数指针”本身首先应是指针变量,只不过该指针变量指向函数。函数指针有两个用途:调用函数和做函数的参数

type callback func(int,int)int // 定义一个带有两个int类型的参数,一个返回值 类型 为int类型的函数叫callback

func Add(a,b int){ //定义回调函数
	
	return a + b
}
func callbacktest(a, b int,callback callback) int{ //定义一个函数,把callback类型的函数当作参数传递给这个函数
	return callback(a,b)  //返回callback类型函数的结果,相当于调用回调函数
}
func main(){
    fmt.Println(callbacktest(20,30,Add)) //50  触发调用
}

  3)函数参数传递方式

           1.值传递

      2.引用传递

                 ⽆论是值传递,还是引⽤传递,传递给函数的都是变量的副本,不过,值传递是值的拷⻉。引⽤传递是地址的拷⻉,⼀般来说,地址拷⻉更为⾼效。⽽值拷⻉取决于拷⻉的对象⼤⼩,对象越⼤,则性能

                 越低。

               mapslicechan、指针、 interface默认以引⽤的⽅式传递

    

func Change(a int){
	a = 200
}
func Change1(a *int){
	*a = 200
}
func ChangeSlice(b []int){
	b[0] = 100
	b[1] = 200
	b[5] = 500
}
func TestValuePass(){
	a:=100
	Change(a)
	fmt.Printf("a的值是:%d\n",a) //100  a没有被改变,值传递
	Change1(&a)
	fmt.Printf("a的值是:%d\n",a) //200  指针传递
	var b []int
	b = make([]int,20,30)
	ChangeSlice(b)   //切片传递
	fmt.Printf("%#v\n",b)//[]int{100, 200, 0, 0, 0, 500, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}

	
}

  4)命名返回值

        

func Minus(a,b int)(c int){
	c = a+b
	return                   //省略了返回值的名字
}
func cacl(a,b int)(sum,avg int){
	sum = a+b
	avg = (a+b)/2
	return          省略了返回值的名字
}
func TestNamedRe(){
	fmt.Printf("%d\n",Minus(1000,2000)) //3000
	sum,avg := cacl(1000,2000)
	fmt.Printf("sum 的值=%d avg的值=%d",sum,avg) //3000  1500
}

  5)_标识符,⽤来忽略返回值:

func cacl(a,b int)(sum,avg int){
	sum = a+b
	avg = (a+b)/2
	return
}
func TestNamedRe(){
	sum,_ := cacl(1000,2000)
	fmt.Printf("sum 的值=%d",sum) //3000
}

  6)可变参数

func add(arg…int) int {
}                                                           0个或者多个参数 

func add(a int,arg…int) int {                1个或者多个参数
}

func add(a int,b int,arg…int) int {         2个或者多个参数
}

arg 可以被任意合法的标识符替代

 

其中arg是⼀个slice,我们可以通过arg[index]依次访问所有参数
通过len(arg)来判断传递参数的个数

func indefinite(args...int){
	fmt.Println(args)        // 打印出每个参数 100 200     
	for _,v:= range(args){
		fmt.Printf("%d\n",v)  /100  200
	}
	var sum int
	for i:=0;i<len(args);i++{      //len(args) 求出参数的长度
		sum = sum + args[i]     //args[i]     求出每个参数的值
	}
	fmt.Printf("%d\n",sum)  //300
}
func testindefinite(){
	a := 100
	b := 200
	indefinite(a,b)   
}

  同样是上面的indenifite(args …int)函数为例,在参数赋值时可以不用用一个一个的赋值,可以直接传递一个数组或者切片,特别注意的是在参数后加上“…”即可。

func indefinite(aaa...int){
	fmt.Println(aaa)
	for _,v:= range(aaa){
		fmt.Printf("%d\n",v)
	}
	var sum int
	for i:=0;i<len(aaa);i++{
		sum = sum + aaa[i]
	}
	fmt.Printf("%d\n",sum)
}
func testindefinite(){
	arr := []int{100, 200, 300}
	indefinite(arr...)
	indefinite(arr[:2]...)	
	// a := 100
	// b := 200
	// indefinite(a,b)   // 100 200
}
//输出结果
[100 200 300]
100
200
300
600
[100 200]
100
200
300

 

3.匿名函数

匿名函数是指不需要定义函数名的一种函数实现方式。

在Go里面,函数可以像普通变量一样被传递或使用,Go语言支持随时在代码里定义匿名函数。 
匿名函数由一个不带函数名的函数声明和函数体组成。匿名函数的优越性在于可以直接使用函数内的变量,不必申明。 
直接看一个例子:

func main(){
getSqrt := func(a float64) float64 {        //匿名函数需要赋值给一个变量来调用,需要有一个名字,否则无法调用。就跟调用变量一样,也是需要一个名字的。
		return math.Sqrt(a)
	}
	fmt.Printf("%f\n",getSqrt(8))
	fmt.Printf("%f\n",getSqrt(16))
}
//运行结果
2.828427
4.000000

 4.递归函数

⼀个函数调⽤⾃⼰,就叫做递归。

递归的设计原则
1)⼀个⼤的问题能够分解成相似的⼩问题
2)定义好出⼝条件

利用递归函数可以很方便的求解问题,例如阶乘、斐波那契数列

func factorial(n int)int{
	if n ==1 {
		return 1
	}else{
		return factorial(n-1)*n
	}
}
func testfac(){
	n:=factorial(5)
	fmt.Printf("%d",n)  // 120

}//阶乘

  

func fibonacii(n int)int{
	if n<=1 {
		return 1
	}
	return fibonacii(n-1) + fibonacii(n-2)
}
func testfibonacii(){
	for i:=0;i<10;i++{
		n:=fibonacii(i)
		fmt.Printf("%d,",n) //1,1,2,3,5,8,13,21,34,55,
		
	}
}  //斐波那契

  

 



                   

posted @ 2018-01-03 16:52  whj999  阅读(152)  评论(0编辑  收藏  举报