go的条件语句、循环、函数、指针、数组
与其说是教程,更像是总结
一起学习
1. 条件语句
if
if条件语句与其他语言的区别在于条件语句没有括号
if条件中也可以定义变量
第一种语法
中规中矩的写法
//文件名
const fileName = "abc.txt"
//读取文件
//这里可能会好奇,咋有两个返回值,等下面介绍函数时会详细说明的
contents, err := ioutil.ReadFile(fileName)
//第一种if写法
if err != nil {//判断是否有误
//如果err不等于nil(null),说明里面有东西,也就是错误信息
fmt.Println(err)
} else {
//如果err == nil,就没有错,正常数据文件中的内容
fmt.Printf("%s\n", contents)
}
第二种写法
类似其他语言的for循环的语法,同时也要注意作用域
//第二种if写法
if contents, err := ioutil.ReadFile(fileName); err != nil {
fmt.Println(err)
} else {
fmt.Printf("%s\n", contents)
}
//出了if作用域就无法访问contents了,和其他语言for很象
fmt.Println(contents)//报错
switch
没有繁琐的break,默认是有break的
第一种写法
中规中矩
//定义一个函数(后面会详细说的)
func cal(a, b int, op string) int {
//定义返回值
var result int
//switch语句
switch op {
case "+":
result = a + b
case "-":
result = a - b
case "*":
result = a * b
case "/":
if b == 0 {
panic(fmt.Sprintf(
"%s\n", "被除数不能为0"))
}
result = a / b
default:
panic("unsupported operator:" + op)
}
return result
}
第二种写法
省略参数(上略了上个例子中的op)
func grade(score int) string {
var g string
switch {
case score < 0 || score > 100:
//定义错误
panic(fmt.Sprintf(
"Wrong score :%d", score))
case score < 60:
g = "F"
case score < 80:
g = "C"
case score < 90:
g = "B"
case score <= 100:
g = "A"
}
return g
}
goto
这个一般不常用,因为比较难控制
/**
求素数
*/
func findPrime(num int) {
var C, c int
C = 1
LOOP:
for C < num {
C++
for c = 2; c < C; c++ {
if C%c == 0 {
goto LOOP
}
}
fmt.Printf("%d \t", C)
}
}
2. 循环
就一个:for
Go中没有while,用for就够了
for,if后面的条件没有括号
第一种写法:完整for参数
和其他语言一样,标准的三段式
/**
二进制转换函数
*/
func convertToBin(n int) string {
result := ""
//标准的三段式
for ; n > 0; n /= 2 {
lsb := n % 2
//由于二进制求完需要后往前倒推,这里直接放在前面
result = strconv.Itoa(lsb) + result
}
return result
}
第二种写法:省略for两个参数
/**
输出文件内容
*/
func printFile(fileName string) {
//打开文件
file, err := os.Open(fileName)
//判断错误
if err != nil {
panic(err)
}
//读取文件
scanner := bufio.NewScanner(file)
for scanner.Scan() {
//逐行扫描读取
fmt.Println(scanner.Text())
}
}
第三种写法:省略for全部参数
直接变成while(true)
/**
死循环
*/
func forever() {
//相当于while循环
for {
fmt.Println("abc")
}
}
3. 函数
返回一个值
就是和其他语言一样普通的函数
//格式
func 函数名(参数) 返回值类型{
return 返回值
}
//返回返回一个值
func eval(a, b int, op string) int {
switch op {
case "+":
return a + b
case "-":
return a - b
case "*":
return a * b
case "/":
return a / b
default:
//中断程序运行
panic("unsupported operation " + op)
}
}
返回两个值
//格式
func 函数名(参数) 返回值类型,返回值类型{
return 返回值,返回值
}
//返回两个值
func div(a, b int) (int, int) {
return a / b, a % b
}
另一种写法-带参数(不建议)
写明了参数,return返回时就不用返回具有内容了
//返回两个值(带参数名称),不建议这么写
func divWithParameterName(a, b int) (q, r int) {
q = a / b
r = a % b
return
}
返回错误
其实就是多返回了个error类型的值,发现上面写的错误判断来源了
//返回错误
func evalWithError(a, b int, op string) (int, error) {
switch op {
case "+":
return a + b, nil
case "-":
return a - b, nil
case "*":
return a * b, nil
case "/":
return a / b, nil
default:
//定义错误
return 0, fmt.Errorf("unsupported operation %s", op)
}
}
函数式编程
函数作为参数
函数可作为参数传递,因为可以反射获取到函数的函数名和地址,就可以调用函数
案例1
/**
函数式编程
*/
package main
import (
"fmt"
"math"
"reflect"
"runtime"
)
func pow(a, b int) int {
return int(math.Pow(float64(a), float64(b)))
}
/**
1. 【op func(int, int) int】函数的格式
2. 【a, b int】 参数
3. 【int】 返回值
*/
func apply(op func(int, int) int, a, b int) int {
//反射获取函数指针
p := reflect.ValueOf(op).Pointer()
//获取函数名称
opName := runtime.FuncForPC(p).Name()
fmt.Printf("Calling function %s with args (%d , %d)\n", opName, a, b)
fmt.Print("ans is ")
fmt.Print(op(a, b))
return op(a, b)
}
func main() {
apply(pow, 1, 2)
}
结果输出:
PS F:\Desktop\test> go run main.go
Calling function main.pow with args (1 , 2)
ans is 1
案例2
package main
import (
"fmt"
)
// 相当于取别名
type processFunc func(int) bool
func main() {
//数组
slice := []int{1, 2, 3, 24, 32423, 4, 24, 2, 34}
//
odd := filter(slice, isOdd)
fmt.Println("奇数: ", odd)
enve := filter(slice, isEven)
fmt.Println("偶数: ", enve)
}
func filter(slice []int, f processFunc) []int {
var result []int
for _, value := range slice {
// 调用函数
if f(value) {
result = append(result, value)
}
}
return result
}
//判断元素是否为奇数
func isOdd(n int) bool {
return n&1 == 1
}
//判断元素是否为偶数
func isEven(n int) bool {
return n&1 == 0
}
结果输出:
PS F:\Desktop\test> go run main.go
奇数: [1 3 32423]
偶数: [2 24 4 24 2 34]
回调函数
回调函数,函数有一个参数是函数类型,这个函数就是回调函数
package main
import (
"fmt"
"math"
)
func main() {
arr := []float64{1, 9, 16, 25, 36, 49}
visit(arr, func(v float64) {
v = math.Pow(v, 2) //平方
fmt.Printf("%.0f\t", v)
})
fmt.Println()
visit(arr, func(v float64) {
v = math.Sqrt(v) //平方根
fmt.Printf("%.0f\t", v)
})
}
func visit(list []float64, f func(float64)) {
for _, value := range list {
f(value)
}
}
结果输出:
PS F:\Desktop\test> go run main.go
1 81 256 625 1296 2401
1 3 4 5 6 7
闭包(难点)
后面会有专题讲解,可以暂时简单理解为,堆的内存没被释放
package main
import (
"fmt"
)
func main() {
myfunc := Counter()
fmt.Println("myfunc", myfunc)
fmt.Println(myfunc())
fmt.Println(myfunc())
fmt.Println(myfunc())
myfunc1 := Counter()
fmt.Println("myfunc1", myfunc1)
fmt.Println(myfunc1())
fmt.Println(myfunc1())
fmt.Println(myfunc())
}
func Counter() func() int {
i := 0
res := func() int {
i += 1
return i
}
fmt.Println("Counter中的内部函数: ", res)
return res
}
可变参数函数
【…】后面会详细说明的
/**
可变参数函数
*/
//不限制参数的传递
func sumArgs(numbers ...int) int {
sum := 0
// 经典for range循环
for i := range numbers {
sum += numbers[i]
}
return sum
}
主函数和匿名函数
主函数:main函数,也就是Go中所有程序的入口
匿名函数:更像是其他语言的Lambda
// 要用主函数,就要使用main包
package main
//”头文件“
import (
"fmt"
"math"
)
//主函数
func main() {
//匿名函数
fmt.Println((func(a, b int) int {
return int(math.Pow(float64(a), float64(b)))
})(3, 4))//(3, 4) 相当于立即调用并传入参数
}
4. 指针
go语言只有值传递,没有引用传递
go语言指针不能做运算
一般就是判断需要需要改变参数的值,考虑使不使用指针(当然也有其他操作)
/**
交换值
*/
func swap_p(a, b *int) (int, int) {
return *b, *a
}
func swap(a, b int) (int, int) {
return b, a
}
func main() {
a, b := 3, 4
fmt.Println(swap_p(&a, &b))
fmt.Println(swap(a, b))
}
值传递和引用传递
go只有值传递,传递的都是一个副本,有些能修改是因为参数是引用类型
但也有引用的数据类型
默认副本的有:int string bool array struct
引用类型的有:pointer slice map chan
5. 数组
一维和二维数组定义
//定义一维数组,var定义默认初始化为0
var arr1 [5]int
//需要给值
arr2 := [3]int{1, 2, 3}
//让编译器计算个数
arr3 := [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9}
//定义二维数组
var grid [4][5]int
输出数组
fmt.Println(arr1, arr2, arr3, grid)
//数组的遍历1
//Go语言没有++i的操作
for i := 0; i < len(arr3); i++ {
fmt.Println(arr3[i])
}
//遍历2
for i, v := range arr3 {
fmt.Println(i, v)//下标、值
}
for _, v := range arr3 {
fmt.Println(v)//只要值
}
for i := range arr3 {
fmt.Println(arr3[i])//只要下标
}
值传递
Go语言中数组作为参数是拷贝值传递的
package main
import "fmt"
func main() {
arr := [5]int{1, 4, 5, 6, 7}
changeArray(arr)
for i, v := range arr {
fmt.Println(i, v)
}
printChangeArray(&arr)
for i, v := range arr {
fmt.Println(i, v)
}
}
//[5]int->值类型数组 []int->切片(后面会补充)
//传的是值类型,会拷贝数组
func changeArray(arr [5]int) {
arr[0] = 100 //不被修改
}
//传指针就可以修改了
func printChangeArray(arr *[5]int) {
arr[0] = 100 //会被修改
}
小案例
生成不重复的真随机数
//生成count个[start,end)结束的不重复的随机数
func generateRandomNumber(start int, end int, count int) []uint64 {
//范围检查
if end < start || (end-start) < count {
return nil
}
//存放结果的slice
nums := make([]uint64, 0)
for len(nums) < count {
//生成真随机数
num, _ := rand.Int(rand.Reader, big.NewInt(9))
a := num.Uint64()
//查重
exist := false
for _, v := range nums {
if v == a {
exist = true
break
}
}
if !exist {
nums = append(nums, a)
}
}
return nums
}
PREVIOUS配置Typora样式
NEXT第四块 vue组件