Go(geekr.dev)学习三

本文详细介绍了Go语言中的算术运算符、关系运算符、逻辑运算符、位运算符以及赋值运算符的使用规则。此外,还讲解了函数的定义、参数传递方式以及变长参数的概念。同时,提到了变量的作用域和数据类型的默认初始化值。最后,文章讨论了Go中的范围(range)关键字和类型转换,并给出了Map-Reduce-Filter模式的示例。
摘要由CSDN通过智能技术生成

运算符

  • 算术运算符

    +-*/%++--

    %(取余运算只能用于整数)

    不同类型的整型值不能直接进行算术运算,必需先转化相同类型再执行计算

    自增/自减运算符,只能作为语句,不能作为表达式,且只能用作后缀,不能放到变量前面

    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")
    }
    
  • 逻辑运算符

    &&||!

    1. 没有单与、单或
    2. 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||

函数

  1. 最少有一个 main 函数
  2. 函数名称,参数列表和返回值(类型,个数,顺序)一起构成了函数签名
  3. 标准库提供了多种可动用的内置的函数(存在于builtinunsafe标准库)
    Go 语言内置函数可以参考 built 包文档
  4. 函数本身也是 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)

  1. 匿名函数作为形参
// 匿名函数作为参数传递
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))
}
  1. 匿名函数作为返回值
    可以通过将函数返回值声明为函数类型来实现业务逻辑的延迟执行
func main() {
    // 获取返回的匿名函数
    addFunc := deferAdd(1, 2)
    // 执行真正的函数动作
	fmt.Println(addFunc())
}

// 匿名函数对应的定义是:func() int
func deferAdd(a, b int) func() int {
	return func() int {
		return a + b
	}
}
  1. 高阶函数:接收其他函数作为形参,或者把其他函数作为结果返回的函数

Map-Reduce-Filter 模式

准确的说是一种处理思想(不与固定场景绑定)

多用于处理集合

Map-Reduce-Filter 并不是一个整体,而是要分三步实现:Filter、Map 和 Reduce(以字典类型切片为例)

  1. 首先将字典类型切片按照条件过滤,即Filter
  2. 再将过滤后的字典类型切片转化为一个字符串类型切片(Map,字面意思就是映射)
  3. 最后再将转化后的切片元素转化为目标类型执行计算
    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
}

变量用域

变量可以在三个地方声明:

  • 函数内定义的变量称为局部变量
  • 函数外定义的变量称为全局变量
  • 函数定义中的变量称为形式参数

局部变量

  1. 在函数体内声明
  2. 作用域只在函数体内
  3. 参数和返回值变量也是局部变量

全局变量

  1. 在函数体外声明
  2. 可以在整个包甚至外部包(被导出后)使用

全局变量与局部变量名称可以相同,但是函数内的局部变量会被优先考虑

形式参数(形参)

出现在函数/方法形参表中的变量(当作局部变量使用)

初始化局部和全局变量

数据类型初始化默认值
int0
float320
pointer(fixme)nil

语言范围

range关键字用于for循环中迭代数组array、切片slice、链表channel或集合map等元素

个人理解:使用for循环遍历数组、切片、链表或集合用到的一个关键字

Range表达式第一个值第二个值[可选的]
Array 或者 slice a [n]E索引 i inta[i] E
String s string type索引 i intrune int
map m map[K]V键 k K值 m[k] V
channel c chan E元素 e Enone
// 迭代string时,index为索引,value为字符(Unicode值)
for index|key, value := array|slice|channel|map|string {
    // 业务代码
}

类型转换

  1. 将一种数据类型的变量转换为另外一种类型的变量
  2. 不支持隐式类型转换
// type_name 为类型,expression 为表达式
type_name(expression)

i := 32
float32(i)

//var a int32 = 3
//var b int64
//b = a
//fmt.Printf("b 为 : %d", b)

参考资料:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值