var f1 float64 = 12.34
var f2 float64 = 12.00
var oper byte = '-'
result := cal(f1, f2, oper)
fmt.Println(result)
}
func cal(n1 float64, n2 float64, operator byte) float64 {
var res float64
switch operator {
case '+':
res = n1 + n2
case '-':
res = n1 - n2
case '*':
res = n1 * n2
case '/':
res = n1 / n2
default:
fmt.Println("操作符号错误")
}
return res
}
go的每一个文件都属于一个包,也就是说go是以包的形式来管理文件和项目目录结构的
Package 包名
Import 包的路径
[root@localhost learn]# go build -o bin\my.exe go_codee\fundemo01\main
上图说明:
一个函数在函数体内又调用了本身,我们称为递归调用
分析图
案例
递归调用的总结
练习题
func fbn(n int) int {
if n == 1 || n == 2 {
return 1
} else {
return fbn(n-1) + fbn(n-2)
}
}
f(1)=3
f(n)=2*f(n-1)+1
func fbn(n int) int {
if n == 1 {
return 3
} else {
return f(n)=2*f(n-1)+1
}
}
有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个!以后每天猴子都吃其中的一半,然后 再多吃一个。当到第十天时,想再吃时(还没吃),发现只有 1 个桃子了。问题:最初共多少个桃子?
思路分析:
func peach(n int) int {
if n > 10 || n < 1 {
fmt.Println("天数不对")
return 0
}
if n == 10 {
return 1
} else {
return (peach(n+1) + 1) * 2
}
}
【基本语法:type 自定义数据类型名 数据类型】
13-----Go 支持可变参数
func main(){
res4 := sum(10, 0, -1, 90, 10)//109
fmt.Println(res4)
}
func sum(n1 int, args ...int) int {
sum := n1
for i := 0; i < len(args); i++ {
sum += args[i]
}
return sum
}
12------Go 支持自定义数据类型
相当于别名
但是与原来的int是两种类型,需要转换
type myInt int
var num1 myInt
var num2 int
num1 = 48
num2 = int(num1)
fmt.Println("num1=", num1, "num2=", num2)
函数别名
func main(){
res3 := myFun2(getSum, 56, 60)
fmt.Println(res3)
}
type myFunType func(int, int) int
func myFun2(funvar myFunType, num1 int, num2 int) int {
return funvar(num1, num2)
}
func getSum(n1 int, n2 int) int {
return n1 + n2
}
11-----支持对函数返回值命名
func main(){
a, b := getSumAndSub(1, 2)
fmt.Println(a, b)
}
func getSumAndSub(n1 int, n2 int) (sum int, sub int) {
sub = n1 - n2
sum = n1 + n2
return
}
如果希望函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量。从效果上看类似引用
--------------------------------------------
num := 14
test1(num)
fmt.Println(num) //14
func test1(n int) {
n = n + 10
fmt.Println(n)//24
//return 0
}
---------------------------------------------
//传入地址修改值
func main(){
num := 14
//test1(num)
test3(&num)
fmt.Println("main num=", num)//24
}
//n就是 *int类型
func test3(n *int) {
*n = *n + 10
fmt.Println("test03 n=", *n)//24
}
//*取值
//&取地址
---------------------------------------------
函数也是一种类型
func main(){
a := getSum
fmt.Printf("a的类型是%T,getSum类型是%T\n", a, getSum)
res := a(10, 40)
fmt.Println(res)
}
func getSum(n1 int, n2 int) int {
return n1 + n2
}
-----------------------------------------------
函数既然是一种类型,也可以作为参数
func main(){
res2 := myFun(getSum, 50, 60)
fmt.Println(res2)
}
func getSum(n1 int, n2 int) int {
return n1 + n2
}
func myFun(funvar func(int, int) int, num1 int, num2 int) int {
return funvar(num1, num2)
}
案例
交换两个值的位置
func main(){
a = 10
b = 20
swap(&a, &b)
fmt.Printf("a=%v,b=%v", a, b)
}
func swap(n3 *int, n4 *int) {
t := *n3
*n3 = *n4
*n4 = t
}
每一个源文件都可以包含一个 init 函数,该函数会在 main 函数执行前,被 Go 运行框架调用,也
就是说 init 会在 main 函数前被调用。
package main
import "fmt"
func init() {
fmt.Println("init...")
}
func main() {
fmt.Println("main...")
}
//GOROOT=D:\go #gosetup
init...
main...
函数->main 函数
var age = test()
func test() int {
fmt.Println("test()")
return 90
}
func init() {
fmt.Println("init...")
}
func main() {
fmt.Println("main...", age)
}
//
test()
init...
main... 90
Go 支持匿名函数,匿名函数就是没有名字的函数,如果我们某个函数只是希望使用一次,可以考
虑使用匿名函数,匿名函数也可以实现多次调用。
在定义匿名函数时就直接调用,这种方式匿名函数只能调用一次
func main() {
res1 := func(n1, n2 int) int {
return n1 + n2
}(10, 20)//调用
fmt.Println(res1)
}
匿名函数赋给一个变量(函数变量),再通过该变量来调用匿名函数
func main() {
a := func(n1, n2 int) int {
return n1 - n2
}
res2 := a(10, 5)
fmt.Println(res2)
res3 := a(12, 6)
fmt.Println(res3)
}
如果将匿名函数赋给一个全局变量,那么这个匿名函数,就成为一个全局匿名函数,可以在程序
有效。
var (
//fun1就是全局匿名函数
Func1 = func(n1, n2 int) int {
return n1 + n2
}
)
基本介绍:闭包就是一个函数和与其相关的引用环境组合的一个整体(实体)
案例
func AddUpper() func(int) int {
var n int = 10
return func(x int) int {
n = n + x
return n
}
}
func main() {
f := AddUpper()
fmt.Println(f(1))
fmt.Println(f(2))
fmt.Println(f(3))
}
//
11
13
16
----这部分属于闭包----
var n int = 10
return func(x int) int {
n = n + x
return n
}
--------------------
返回的是一个匿名函数, 但是这个匿名函数引用到函数外的 n ,因此这个匿名函数就和 n 形成一
个整体,构成闭包
func AddUpper() func(int) int {
var n int = 10
var str = "hello"
return func(x int) int {
n = n + x
str += string(36) //36=>$
fmt.Println("str=", str)
return n
}
}
func main() {
f := AddUpper()
fmt.Println(f(1))
fmt.Println(f(2))
fmt.Println(f(3))
}
//结果
str= hello$
11
str= hello$$
13
str= hello$$$
16
案例:
func makeSuffix(suffix string) func(string) string {
return func(name string) string {
if !strings.HasSuffix(name, suffix) {
return name + suffix
}
return name
}
}
f1 := makeSuffix(".jpg")
fmt.Println("文件名处理后=", f1("winter"))
fmt.Println("文件名处理后=", f1("winter.jpg"))
fmt.Println("文件名处理后=", f1("winter.avi"))
//
文件名处理后= winter.jpg
文件名处理后= winter.jpg
文件名处理后= winter.avi.jpg
在函数中,程序员经常需要创建资源(比如:数据库连接、文件句柄、锁等) ,为了在函数执行完毕后,及时的释放资源,Go 的设计者提供 defer (延时机制)。
func main() {
res := sum(10, 20)
fmt.Println("main4 res=", res)
}
func sum(n1 int, n2 int) int {
//当执行到defer时,暂时不执行,会将defer后面的语句亚茹到独立的栈(defer栈)
//当函数执行完毕后,再从defer栈,按照先入后出的方式出栈,执行
defer fmt.Println("sum3 n1=", n1)
defer fmt.Println("sum2 n2=", n2)
res := n1 + n2
fmt.Println("sum1 res=", res)
return res
}
//
sum1 res= 30
sum2 n2= 20
sum3 n1= 10
main4 res= 30
func main() {
res := sum(10, 20)
fmt.Println("main4 res=", res)
}
func sum(n1 int, n2 int) int {
//当执行到defer时,暂时不执行,会将defer后面的语句亚茹到独立的栈(defer栈)
//当函数执行完毕后,再从defer栈,按照先入后出的方式出栈,执行
defer fmt.Println("sum3 n1=", n1)
defer fmt.Println("sum2 n2=", n2)
//增加
n1++
n2++
defer fmt.Println("sum3 n1=", n1)
defer fmt.Println("sum2 n2=", n2)
res := n1 + n2
fmt.Println("sum1 res=", res)
return res
}
//
sum1 res= 32
sum2 n2= 21
sum3 n1= 11
sum2 n2= 20
sum3 n1= 10
main4 res= 32
defer 最主要的价值是在,当函数执行完毕后,可以及时的释放函数创建的资源。
值类型参数默认就是值传递,而引用类型参数默认就是引用传递
其实,不管是值传递还是引用传递,传递给函数的都是变量的副本,不同的是,值传递的是值的拷贝,引用传递的是地址的拷贝,一般来说,地址拷贝效率高,因为数据量小,而值拷贝决定拷贝的数据大小,数据越大,效率越低
//传入地址修改值
func main(){
num := 14
//test1(num)
test3(&num)
fmt.Println("main num=", num)//24
}
//n就是 *int类型
func test3(n *int) {
*n = *n + 10
fmt.Println("test03 n=", *n)//24
}
//*取值
//&取地址
赋值语句不能在函数外执行
package main
import "fmt"
var Age int = 20
Name := "test" //先声明后赋值 var Name string / Name = "test",执行语句需在函数体内执行
func main() {
fmt.Println("name",Name)
}
课堂练习
var age int = 50 //其他包不可用
var Name string = "jack" //其他包可用
func main() {
fmt.Println("age=", age)
fmt.Println("Name=", Name)
fmt.Println()
test()
}
func test() {
age := 10
Name := "time"
fmt.Println("age=", age)
fmt.Println("Name=", Name)
}
-------------------
age= 50
Name= jack
age= 10
Name= time
//如果变量是在一个代码块,比如 for / if 中,那么这个变量的的作用域就在该代码块
for i := 0; i < 10; i++ {
fmt.Println(i)
}
for i := 0; i < 10; i++ {
fmt.Println(i)
}
fmt.Println(i) //未定义
要局部使用
var i int
for i = 0; i < 10; i++ {
fmt.Println(i)
}
---------------------------
var age int = 50 //其他包不可用
fmt.Println(age)
test01()
test02()
test03()
func test01() {
fmt.Println(age)
}
func test02() {//就近原则
age := 12
fmt.Println(age)
}
func test03() {
fmt.Println(age)
}
函数课堂联系题
//todo
//1-取长度
str := "hello 北京"
//golang的编码是utf8,ascii的字符(字吗和数字)占一个字节,汉字占用三个字节
fmt.Println("str len=", len(str)) //5+1+3+3=12
//2-中文遍历
str2 := "hello北京"
r := []rune(str2) //切片
for i := 0; i < len(r); i++ {
fmt.Printf("字符=%c\n", r[i]) //中文乱码 一个汉字三个字符
}
/**
字符=h
字符=e
字符=l
字符=l
字符=o
字符=北
字符=京
*/
//3-字符串转整数
str3 := "123"
n, err := strconv.Atoi(str3)
if err != nil {
fmt.Println("错误结果", err)
} else {
fmt.Println("结果是", n)
} //结果是 123
//4-整数转字符串
m := strconv.Itoa(12345)
fmt.Println("结果是", m) //结果是 12345
//5-字符串转切片 []byte
var bytes = []byte("hello go")
fmt.Printf("bytes=%v\n", bytes) // [104 101 108 108 111 32 103 111]
//6-切片 []byte 到字符串
str = string([]byte{97, 98, 99})
fmt.Printf("str=%v\n", str) //str=abc
//7- 10 进制转 2, 8, 16 进制: str = strconv.FormatInt(123, 2) // 2-> 8 , 16 返回对应的字符串
str = strconv.FormatInt(123, 2)
fmt.Printf("123对应的二进制是=%v\n", str) //123对应的二进制是=1111011
str = strconv.FormatInt(123, 16)
fmt.Printf("123对应的16进制是=%v\n", str) //123对应的16进制是=7b
//8-查找子串是否在指定的字符串中: strings.Contains("seafood", "foo") //bool
b := strings.Contains("seafood", "sea")
fmt.Printf("b=%v\n", b) //b=true
//9-统计一个字符串有几个指定的子串 : strings.Count("ceheese", "e") //4
num := strings.Count("ceheeeee", "e")
fmt.Printf("b=%v\n", num) //b=6
//10-不区分大小写的字符串比较(==是区分字母大小写的): fmt.Println(strings.EqualFold("abc", "Abc")) // true
b = strings.EqualFold("abc", "ABc")
fmt.Printf("b=%v\n", b) //b=true
fmt.Println("结果", "abc" == "Abc") //区分大小写 //结果 false
//12-返回子串在字符串第一次出现的 index 值,如果没有返回-1 : strings.Index("NLT_abc", "abc") // 4
index := strings.Index("NTL_abcabcabc", "abc")
fmt.Printf("index=%v\n", index) //index=4
//13-返回子串在字符串最后一次出现的 index,如没有返回-1 : strings.LastIndex("go golang", "go")
indexL := strings.LastIndex("NTL_abcabcabc", "abc")
fmt.Printf("index=%v\n", indexL) //index=10
//14-将指定的子串替换成 另外一个子串: strings.Replace("go go hello", "go", "go 语言", n)
// n 可以指定你希望替换几个,如果 n=-1 表示全部替换
str3 = "go go hello"
str = strings.Replace(str3, "go", "北京", -1)
fmt.Printf("str=%v str3=%v\n", str, str3) //str=北京 北京 hello str3=go go hello
//15-按照指定的某个字符,为分割标识,将一个字符串拆分成字符串数组:strings.Split("hello,wrold,ok",",")
strArr := strings.Split("hello,hello,test", ",")
for i := 0; i < len(strArr); i++ {
fmt.Printf("str[%v]=%v\n", i, strArr[i])
/**
str[0]=hello
str[1]=hello
str[2]=test
*/
}
fmt.Printf("strArr=%v\n", strArr) //strArr=[hello hello test] 值拷贝不改变本身
//16-将字符串的字母进行大小写的转换: strings.ToLower("Go") // go strings.ToUpper("Go") // GO
str4 := "golang hello"
str4 = strings.ToLower(str4) //改变了str4
fmt.Printf("str4=%v\n", str4) //str4=golang hello
str4 = strings.ToUpper(str4)
fmt.Printf("str4=%v\n", str4) //str4=GOLANG HELLO
//17-将字符串左右两边的空格去掉: strings.TrimSpace(" tn a lone gopher ntrn ")
str = strings.TrimSpace(" tn a lone gopher ntrn ")
fmt.Printf("str=%q\n", str) //str="tn a lone gopher ntrn"
//18-将字符串左右两边指定的字符去掉 : strings.Trim("! hello! ", " !") // ["hello"] //将左右两边 !
//和 " "去掉
str = strings.Trim("! hello !! ", " !")
fmt.Printf("str=%q\n", str) //str="hello"
//19-将字符串左边指定的字符去掉 : strings.TrimLeft("! hello! ", " !") // ["hello"] //将左边 ! 和 "
//"去掉
str = strings.TrimLeft("! hello! ", " !")
fmt.Printf("str=%q\n", str) //str="hello! "
//20-将字符串右边指定的字符去掉 :strings.TrimRight("! hello! ", " !") // ["hello"] //将右边 ! 和 "
//"去掉
str = strings.TrimRight("! hello! ", " !")
fmt.Printf("str=%q\n", str) //str="! hello"
//21-判断字符串是否以指定的字符串开头: strings.HasPrefix("ftp://192.168.10.1", "ftp") // true
b = strings.HasPrefix("ftp://192.168.10.1", "ftp")
fmt.Printf("str=%v\n", b) //str=true
//22-判断字符串是否以指定的字符串结束: strings.HasSuffix("NLT_abc.jpg", "abc") //false
b = strings.HasSuffix("NLT_abc.jpg", "abc")
fmt.Printf("str=%v\n", b) //str=false
now := time.Now()
fmt.Printf("now=%v now type=%T", now, now)
//now=2021-03-25 16:40:28.2768429 +0800 CST m=+0.003021201 now type=time.Time
//获取年月日时分秒
fmt.Printf("年=%v\n", now.Year())
fmt.Printf("月=%v\n", now.Month())
fmt.Printf("月=%v\n", int(now.Month()))
fmt.Printf("日=%v\n", now.Day())
fmt.Printf("时=%v\n", now.Hour())
fmt.Printf("分=%v\n", now.Minute())
fmt.Printf("秒=%v\n", now.Second())
月=March
月=3
日=25
时=16
分=43
秒=42
2006-01-02 15:04:05这个字符串必须这样写!
下面的自由调整以获取灵活的时间组合
now := time.Now()
fmt.Printf("当前年月日 %d-%d-%d %d:%d:%d\n", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())
datastr := fmt.Sprintf("当前年月日 %d-%d-%d %d:%d:%d\n", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())
fmt.Printf("datastr=%v", datastr)
fmt.Printf(now.Format("2006-01-02 15:04:05"))
fmt.Println()
fmt.Printf(now.Format("2006-01-02"))
fmt.Println()
fmt.Printf(now.Format("15:04:05"))
fmt.Println()
---------------------------------------------------------------
当前年月日 2021-4-15 19:52:44
datastr=当前年月日 2021-4-15 19:52:44
2021-04-15 19:52:44
2021-04-15
19:52:44
//fmt.Printf(now.Format("2006-02-01 15:04:05"))
不能除,所以产生那么多常量,不能用 Second /10
const (
Nanosecond Duration = 1 //纳秒
Microsecond = 1000 * Nanosecond //微妙
Millisecond = 1000 * Microsecond //毫秒
Second = 1000 * Millisecond //秒
Minute = 60 * Second //分钟
Hour = 60 * Minute //小时
)
//time.secod 秒
i := 0
for {
i++
fmt.Println(i)
time.Sleep(time.Second)
time.Sleep(time.Millisecond*100) //100毫秒
}
fmt.Printf("unix=%v unixnano=%v", time.Now().Unix(), time.Now().UnixNano())
//unix=1616731171 unixnano=1616731171139452200
统计函数执行的时间
func main(){
start := time.Now().Unix()
fmt.Println(start)
test01()
end := time.Now().Unix()
fmt.Println(end)
fmt.Printf("执行test01()耗时%v秒", end-start)
}
func test01() {
str := ""
for i := 0; i < 10000; i++ {
str += "hello" + strconv.Itoa(i)
fmt.Println(str)
}
}
https://studygolang.com/pkgdoc
数组:v中元素的数量
数组指针:*v中元素的数量(v为nil时panic)
切片、映射:v中元素的数量;若v为nil,len(v)即为零
字符串:v中字节的数量
通道:通道缓存中队列(未读取)元素的数量;若v为 nil,len(v)即为零
指针存的是一个地址,这个地址指向了一个值
num1 := 100
fmt.Printf("num1的类型%T,num1的值%v,num1的地址%v\n", num1, num1, &num1)
num2 := new(int)
//num2的类型 *ine
//num2的值 = 一个地址A(系统分配)
//num2的地址 = 地址A指向的地址B(系统分配)
//num2指向的值 = 0
fmt.Printf("num2的类型%T,num2的值%v,num2的地址%v", num2, num2, &num2,*num2)
------------------------------------------------------------------
num1的类型int,num1的值100,num1的地址0xc00000a0a0
num2的类型*int,num2的值0xc00000a0a8,num2的地址0xc000006030
变量存的是一个值,值有个地址
指针存的是一个地址A,这个地址对应的有个值B,这个值自身也有地址C
func test() {
//使用defer + recover 来捕获和处理异常
defer func() {
err := recover() //recover()内置函数,可以捕获到异常
if err != nil {
fmt.Println("err=", err)
}
}()
num1 := 10
num2 := 0
res := num1 / num2
fmt.Println("res=", res)
}
func main() {
test()
fmt.Println("main下的方法")
}
----------------------------------------------------------
err= runtime error: integer divide by zero
main下的方法
Go 程序中,也支持自定义错误, 使用 errors.New 和 panic 内置函数。
型的变量,输出错误信息,并退出程序.
func main() {
test02()
}
func test02() {
err := readConf("config2.ini")
if err != nil {
panic(err)
}
fmt.Println("test02继续执行")
}
func readConf(name string) (err error) {
if name == "config.ini" {
return nil
} else {
return errors.New("读取文件错误")
}
}
循环打印输入的月份的天数 使用continue实现
要有判断输入的月份是否错误的语句
编写一个函数
随机猜数游戏:
随机生成一个1--100的整数
有十次机会
如果第一次就猜中,提示:“天才”
如果第2--3次猜中,提示“聪明”
如果第4-9次猜中,提示“一般”
如果最后一次猜中,提示“猜对”
一次都没有猜对,提示“哈哈”
编写一个函数,输出100以内的所有素数(素数就是只能被1和本身整除的书)。每行显示5个,并求和
编写一个函数,判断是打鱼还是晒网
如果从1990年1月1日起开始执行“三天打鱼两天晒网”,如何判断在以后的某一天中是打鱼还是晒网
输出小写的a-z以及大写的Z-A,使用for循环。Asic码