golang基础---func函数1
函数
6.1.1 定义
- 函数式编程是为解决代码复用, 减少代码的冗余.
- 概念: 为完成某一项功能的程序指令(代码语句)的集合, 称为函数. Go中分为
自定义函数,
系统内置函数.`
基本语法
-
func 函数名(形参列表) (返回值列表){ 执行语句 return 返回值列表 }
a. 形参列表:表示函数的输入
b. 函数中的语句: 表示为了实现某一功能代码块
c. 函数可以有返回值, 也可以没有返回值
-
快速入门
package main import ( "fmt" ) func cal(x float64, y float64, operator byte) float64 { var res float64 switch operator { case '+': res = x + y case '-': res = x - y case '*': res = x * y case '/': res = x / y default: fmt.Println("属于运算符错误!") } return res } func main() { res := cal(2.4, 2.1, '*') fmt.Println("res =", res) }
6.1.2 包(package)
包的基本概念
- 说明: go的每一个文件都是属于一个包, 就是说 go是以包的形式来管理文件和项目目录结构的.
包的作用
- 区分相同名字的函数, 变量等标识符
- 当程序文件很多时, 可以很好的管理项目
- 可以
控制函数
,变量
等访问范围, 即作用域.
包的相关说明
-
打包的基本语法
package 报名
-
包的引用方法
import '包的路径'
使用细节
-
再给一个文件打包时, 该包对应一个文件夹, 文件包名通常和文件所在的文件名一致, 一般为
小写字母
-
当一个文件要使用其它包的函数或变量时, 需要先导入对应的包
a. package 指令在文件第一行, 然后时import 指令
b. 导入包名路径是从 $GOPATH 的src 下开始的, 不用带src, 系统会自动从src下开始引入.
-
为了使某个包中的函数或者变量可以被其他包引用, 则函数和变量首字母必须是大写, 类似于 其他语言中的public, 全局变量.
-
访问包中的函数或变量时, 语法:
包名.函数名
/包名.变量名
-
如果包名过长, 可以为包取一个别名 : 导入其它包的时候可以为导入的包名起别名.
-
同一个包下: 不能有相同的函数名,或者变量名.
-
如果要编译成一个可执行程序文件, 就需要将这个包申明为 main, 即
package main
- 编译时需要编译main包所在的文件夹
- 编译后生成的
默认名
的可执行文件位于 main包下 - 编译时可以自定义 名字和目录 如
go build -o c:\project\demo.exe lesson6/demo/main
6.1.3 函数的调用机制
- 再调用一个函数时 : 会给该函数分配一个新的空间, 编译器会通过自身的处理让这个新的空间和其它的栈的空间区分开来
- 在每个函数对应的栈中, 数据空间(内存)时独立的. 不会混淆
- 当一个函数调用完毕(执行完毕)后, 程序会销毁这个函数对应的栈空间
6.1.4 函数的递归调用
-
一个函数在函数体内又调用了函数的本身.
-
一个示例:
package main import ( "fmt" ) func foo(x int) { if x > 2 { x -- foo(x) } fmt.Println("x=", x) } func main() { // 函数的递归调用 var y int = 4 foo(y) }
-
示例2:
package main import ( "fmt" ) func foo2(x int) { if x > 2 { x -- foo2(x) } else{ fmt.Println("x=", x) } } func main() { // 函数的递归调用 var y int = 4 foo2(y) }
递归遵守的原则
- 执行一个函数时, 就创建了一个新的受保护的独立内存空间(栈)
- 函数的局部变量时独立的, 不会相互影响
- 递归必须向退出递归条件逼近, 否则就是个
无限递归
! - 当一个函数执行完毕, 或者遇到return, 就会返回, 当函数执行完毕或者返回时, 该函数本身也会被系统所销毁.
练习
-
斐波那契数列
使用递归方式, 求出 斐波那契数列: 1,1,2,3,5,8,13...
给一个整数, 求出她的斐波那契数是多少?
package main import ( "fmt" ) func fei(n int) int { if (n == 1) || (n == 2) { return 1 } else { return fei(n-1) + fei(n-2) } } func main() { var n int = 3 res := fei(n) fmt.Println("res = ",res) }
-
猴子吃桃子问题
有一堆桃子, 猴子第一天吃了其中的一半, 并再多吃一个! 以后每天猴子都吃其中的一半, 然后在多吃一个, 当到第十天的时候, 想再吃时(还没吃), 发现只有一个桃子了, 问题, 最初共有多少个桃子?
思路分析:
1.t = 10,第十天桃子数量 Sum(n10) = 1个桃子.
2.t = 9,第九天时桃子的数量Sum(n9)= 第十天的桃子数量Sum(n10) +1) *2
3.t=x, 第x天使,桃子的数量Sum(nx) = 第 x+1 天数量(Sum(x+1) +1 ) *2
package main import ( "fmt" ) func sum(t int) int { if t > 10 || t < 1 { fmt.Println("输入的天数不对") return 0 } if t == 10 { return 1 } else { return (sum((t + 1)) + 1) * 2 } } func main() { fmt.Println("第一天桃子的数量:", sum(1)) }
6.1.5 函数的使用细节
-
函数的参数可以是多个, 返回值列表也可以是多个.
-
参数列表 和返回值列表可以是
值类型
也可以是引用类型
-
函数的 命名规范遵循标识符的命名规范, 首字母不能数数字, 首字母大写表示该函数可以被本报文件和其他包文件调用, 首字母小写只能被本包内部调用.
-
函数中的变量是局部的, 函数外不生效.
package main import "fmt" func foo(x int) int{ // y 是foo函数的局部变量, 只能在foo函数中使用,外部无法使用 y:=1 return x + y } func main(){ //fmt.Prinln("y=", y) }
-
基本数据类型 和数组默认都是值传递, 即进行值拷贝. 在函数内修改, 不会影响到原来的值.
-
如果希望函数内的变量能修改函数外的变量, 可以传入变量的地址
&
, 函数内以指针的方式操作 变量, 类似于 引用传递package main import ( "fmt" ) // n1 是int的指针类型 func test(n1 *int) { *n1 = *n1 + 100 fmt.Println("test() n1=", *n1) } func main() { num := 200 // 取num的指针 test(&num) fmt.Println("num的值:", num) }
-
Go中函数不支持传统的函数重载.(函数名相同,参数不同的函数)
-
在Go中, 函数也是一种数据类型. 可以赋值给一个变量, 该变量就是一个函数的类型.通过该变量就可以对函数调用.
package main import ( "fmt" ) func foo(n1 int, n2 int) int { return n1 + n2 } func main() { // 函数可以赋值个一个变量,该变量就是一个函数类型 a := foo fmt.Printf("a的类型%T\n foo的类型%T", a, foo) }
-
函数既然是一个数据类型, 因此在Go中, 函数可以作为另
一个函数的参数
, 并且调用.
package main
import (
"fmt"
)
func a(x int, y int) int {
return x + y
}
func b(funvar func(int, int) int, num int, num2 int) int {
return funvar(num, num2)
}
func main() {
// 函数可以作为另一个函数的类型
res := b(a, 27, 23)
fmt.Println("res=", res)
}
-
为了简化数据类型定义, Go支持自定义数据类型
基本语法:
type 自定义数据类型别名 数据类型
示例:
type myString string
等价使用了string类型package main type myInt int //给int取了别名 var myInt = 40
9 中的示例可以通过自定义数据类型做如下修改
package main import ( "fmt" ) //自定义一个函数的数据类型 type funType func(int,int) int func a(x int, y int) int { return x + y } func b(funvar funType, num int, num2 int) int { return funvar(num, num2) } func main() { res := b(a, 27, 23) fmt.Println("res=", res) }
-
支持 对函数返回值命名
package main import "fmt" func foo(n1 int, n2 int) (sum int, sub int) { //返回值命名, 可以省略return的返回 sum = n1 + n2 sub = n1 - n2 return } func main() { s1, s2 := foo(5, 4) fmt.Printf("s1=%v\ns2=%v", s1, s2) }
-
使用
_
下划线标识符, 忽略返回值 -
在Go中支持可变参数. 可变参数需要放在形参后面. 格式
args ... int
package main import ( "fmt" ) func main() { res := sum(10, 25, -12, 356) fmt.Println("res=", res) } //使用...表示可变参数 func sum(n1 int, args ... int) int { sum := n1 for i := 0; i < len(args); i++ { sum += args[i] } return sum }
6.1.6 练习
-
编写一个函数实现两个int变量值的交换
package main import ( "fmt" ) func main() { a := 11 b := 22 swap(&a, &b) // 传入地址 fmt.Printf("a=%v, b=%v", a, b) } func swap(n1 *int, n2 *int) { tmp := *n1 *n1 = *n2 *n2 = tmp }