运算符
-
算术运算符
含
+
、-
、*
、/
、%
、++
、--
%
(取余运算只能用于整数)不同类型的整型值不能直接进行算术运算,必需先转化相同类型再执行计算
自增/自减运算符,只能作为语句,不能作为表达式,且只能用作后缀,不能放到变量前面
func method() { a := 10 b := 20 var c int c = a + b c = a - b c = a * b c = a / b c = a % b // 等效于:a=a+1 a++ // 等效于:a=a-1 a-- // 没有 ++a 或 --a 的操作 }
-
关系运算符
含
==
、!=
、>
、<
、>=
、<=
各种类型的整型变量都可以直接与字面常量进行比较
var intVal1 int8 = 1 // 字面常量为 int if intVal1 == 8 { fmt.Println("intValue1 = 8") }else { fmt.Println("intValue1 != 8") }
-
逻辑运算符
含
&&
、||
、!
- 没有单与、单或
- go的风格是不必将逻辑运算符计算使用括号包裹
func method() { a := true b := true // go 的风格是不必将逻辑运算使用括号包裹 if a || b { fmt.Println("a||b is true") } // 逻辑运算符中没有单与、单或 // if a | b{} }
-
位运算符
可以对整数在内存中的二进制位进行操作
含
&
、|
、^
运算符 说明 &
与运算,全真为真 |
或运算,全假才假 ^
异或运算,相同为假,不同为真 <<
左移运算符,左移n位就是乘以2的n次方(高位丢弃,低位补0) >>
右移运算符,右移n位就是除以2的n次方 -
赋值运算符
运算符 说明 实例 =
将一个表达式的值赋给一个左值 C = A + B +=
相加后再赋值 C += A 等于 C = C + A -=
相减后再赋值 C -= A 等于 C = C - A *=
相乘后再赋值 C *= A 等于 C = C * A /=
相除后再赋值 C /= A 等于 C = C / A %=
求余后再赋值 C %= A 等于 C = C % A <<=
左移后赋值 C <<= 2 等于 C = C << 2 >>=
右移后赋值 C >>= 2 等于 C = C >> 2 &=
按位与后赋值 C &= 2 等于 C = C & 2 ^=
按位异或后赋值 C ^= 2 等于 C = C ^ 2 |=
按位或后赋值 C |= 2 等于 C = C | 2 -
其他运算符
运算符 说明 实例 &
变量内存地址 &var_name
返回变量的实际地址*
(fixme)指针变量 *var_name
是一个指针变量 -
运算符优先级(由高到低)
优先级 运算符 7 ^ ! 6 * / % <> & &^ 5 + - | ^ 4 == != < = > 3 <- 2 && 1 ||
函数
- 最少有一个 main 函数
- 函数名称,参数列表和返回值(类型,个数,顺序)一起构成了函数签名
- 标准库提供了多种可动用的内置的函数(存在于
builtin
和unsafe
标准库)
Go 语言内置函数可以参考 built 包文档 - 函数本身也是 Go 的一种数据类型
分为三种:
- 普通函数
- 匿名函数(闭包)
- 类方法
定义:
// 1、可以返回多个值
// 2、如果没有返回值,return_types 可缺省
// 3、入参类型相同可使用简单定义
func function_name( [parameter list] ) [return_types] {
// function body
}
func function_name( [parameter list] ) (type1,type2,...,typeN) {
// function body
}
func max(a, b int) int {
// 方法体省略
return ret
}
func swap(x, y string) (string, string) {
return y, x
}
类方法:相当于Java
中的一个类拥有的自定义方法
函数参数
按值传参
Go 语言默认使用按值传参来传递参数,也就是传递参数值的一个副本,在调用过程中不会影响到实际参数
引用传参
可以实现在函数中修改形参值的同时修改实参值
默认使用引用传参的类型:切片(slice)、字典(map)、接口(interface)、通道(channel)等
传递给函数的参数是一个指针,而指针代表的是实参的内存地址,修改指针引用的值即修改变量内存地址中存储的值,所以实参的值也会被修改
(这种情况下,传递的是变量地址值的拷贝,所以从本质上来说还是按值传参)
引用传递的一个例子:
// 引用传递入参及方法体有所不同
func swap(x *int, y *int) {
var tmp int
tmp = *x
*x = *y
*y = tmp
}
变长参数
在参数类型前加上 ...
前缀(只能作为形参存在,且必须是最后一个)
//定义:numbers 为变长参数
func myfunc(vars ...data_type) {
for _, val := range vars {
fmt.Println(val)
}
}
// 调用:加后缀表明变长参数
slice := []data_type{v1,v2,v3}
myfunc(slice...)
任意类型的变长参数(泛型)
指定变长参数类型为 interface{}
Go 语言并没有在语法层面提供对泛型的支持(当前go version = go1.17.2 windows/amd64)
interface{}
是一个空接口,可以用于表示任意类型
(范围太宽泛了,需要在运行时通过反射对数据进行类型检查)
(多)返回值
- 多返回值
- 命名返回值
// 多返回值之间通过英文逗号分隔;使用括号包裹
func method(var1 type,var2 type) (return_type1,return_type2) {
// work code
}
// 命名返回值
func method(var1 type,var2 type) (ret type, err error) {
// 在函数中直接对返回变量进行赋值
// work code
// return 后面不用再写返回变量
}
func add(a, b *int) (c int, err error) {
if *a < 0 || *b < 0 {
err = errors.New("Error message")
return
}
c = *a + *b
return
}
匿名函数与闭包
// 1、将匿名函数赋值给变量
add := func(a, b int) int {
return a + b
}
// 调用匿名函数 add
fmt.Println(add(1, 2))
// 2、定义时直接调用匿名函数
func(a, b int) {
fmt.Println(a + b)
} (1, 2)
闭包:引用了自由变量(未绑定到特定对象的变量,通常在函数外定义)的函数,被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的上下文环境也不会被释放(比如传递到其他函数或对象中)
「闭」的意思是「封闭外部状态」,即使外部状态已经失效,闭包内部依然保留了一份从外部引用的变量
闭包只能通过匿名函数实现,我们可以把闭包看作是有状态的匿名函数;
反过来,如果匿名函数引用了外部变量,就形成了一个闭包(Closure)
- 匿名函数作为形参
// 匿名函数作为参数传递
add := func(a, b int) int {
return a + b
}
// 立即调用;关键字:call;函数对应的定义是:func(int, int) int
func(func_name func(int, int) int) {
fmt.Println(func_name(1, 2))
}(add)
// 匿名函数作为参数传递:抽离匿名函数
func main(){
add := func(a, b int) int {
return a + b
}
handleAdd(1, 2, add)
}
// 函数对应的定义是:func(int, int) int
func handleAdd(a, b int, func_name func(int, int) int) {
fmt.Println(func_name(a, b))
}
- 匿名函数作为返回值
可以通过将函数返回值声明为函数类型来实现业务逻辑的延迟执行
func main() {
// 获取返回的匿名函数
addFunc := deferAdd(1, 2)
// 执行真正的函数动作
fmt.Println(addFunc())
}
// 匿名函数对应的定义是:func() int
func deferAdd(a, b int) func() int {
return func() int {
return a + b
}
}
- 高阶函数:接收其他函数作为形参,或者把其他函数作为结果返回的函数
Map-Reduce-Filter 模式
准确的说是一种处理思想(不与固定场景绑定)
多用于处理集合
Map-Reduce-Filter 并不是一个整体,而是要分三步实现:Filter、Map 和 Reduce(以字典类型切片为例)
- 首先将字典类型切片按照条件过滤,即
Filter
- 再将过滤后的字典类型切片转化为一个字符串类型切片(
Map
,字面意思就是映射)- 最后再将转化后的切片元素转化为目标类型执行计算
(Reduce
,字面意思就是将多个集合元素通过迭代处理减少为一个)
示例:
// 计算一个 []map[string]string 中 age 字段的累加
// 1、过滤
func itemsFilter(items []map[string]string, f func(map[string]string) bool) []map[string]string {
newSlice := make([]map[string]string, len(items))
for _, item := range items {
if f(item) {
newSlice = append(newSlice, item)
}
}
return newSlice
}
// 2、映射
func mapToString(items []map[string]string, f func(map[string]string) string) []string {
newSlice := make([]string, len(items))
for _, item := range items {
newSlice = append(newSlice, f(item))
}
return newSlice
}
// 3、计算
func fieldSum(items []string, f func(string) int) int {
var sum int
for _, item := range items {
sum += f(item)
}
return sum
}
管道 & 流式编程
func SumAge(users []User, pipes ...func([]User) interface{}) int {
var ages []int
var sum int
// 传入函数切片
for _, f := range pipes {
result := f(users)
// fixme result.(type)
switch result.(type) {
case []User:
// 判断结果类型并更新形参
users = result.([]User)
case []int:
ages = result.([]int)
}
}
if len(ages) == 0 {
log.Fatalln("没有在管道中加入 mapAgeToSlice 方法")
}
for _, age := range ages {
sum += age
}
return sum
}
变量用域
变量可以在三个地方声明:
- 函数内定义的变量称为局部变量
- 函数外定义的变量称为全局变量
- 函数定义中的变量称为形式参数
局部变量
- 在函数体内声明
- 作用域只在函数体内
- 参数和返回值变量也是局部变量
全局变量
- 在函数体外声明
- 可以在整个包甚至外部包(被导出后)使用
全局变量与局部变量名称可以相同,但是函数内的局部变量会被优先考虑
形式参数(形参)
出现在函数/方法形参表中的变量(当作局部变量使用)
初始化局部和全局变量
数据类型 | 初始化默认值 |
---|---|
int | 0 |
float32 | 0 |
pointer (fixme) | nil |
语言范围
range
关键字用于for
循环中迭代数组array
、切片slice
、链表channel
或集合map
等元素
个人理解:使用for
循环遍历数组、切片、链表或集合用到的一个关键字
Range表达式 | 第一个值 | 第二个值[可选的] |
---|---|---|
Array 或者 slice a [n]E | 索引 i int | a[i] E |
String s string type | 索引 i int | rune int |
map m map[K]V | 键 k K | 值 m[k] V |
channel c chan E | 元素 e E | none |
// 迭代string时,index为索引,value为字符(Unicode值)
for index|key, value := array|slice|channel|map|string {
// 业务代码
}
类型转换
- 将一种数据类型的变量转换为另外一种类型的变量
- 不支持隐式类型转换
// type_name 为类型,expression 为表达式
type_name(expression)
i := 32
float32(i)
//var a int32 = 3
//var b int64
//b = a
//fmt.Printf("b 为 : %d", b)
参考资料: