go语言学习笔记(数组,切片,函数,闭包,递归,装饰器)

数组

数组是具有相同类型的一组长度固定的数据项序列。

  1. 数组是长度固定的数据类型
  2. 数据元素的类型相同

定义方式

//数组的定义方式1
var arrayVariables [10]int
arrayVariables[0] = 100
arrayVariables[3] = 200
//arrayVariables[10] = 100
fmt.Println(arrayVariables)
//数组的定义方式2
var arrayVariables2 [5]int = [5]int{1,2,3,4,5}
//在这种情况左边类型可简写
var arrayVariables3 = [5]int{1,2,3,4,5}

数组的遍历

//遍历数组1
var arrayVariables2 [5]int = [5]int{1,2,3,4,5}
var length = len(arrayVariables2)
for i :=0;i<length;i++ {
   fmt.Printf("arrayVariables2[%d]=%d,地址=%p\n",i,arrayVariables2[i],&arrayVariables2[i])
}
//遍历数组2
for key,value := range arrayVariables6 {
   fmt.Printf("arrayVariables6[%d]=%v,地址=%p\n",key,value,&arrayVariables6[key])
}

切片

介绍

  1. 一种数据结构,便于使用与管理我们的数据集合。
  2. 按需自动增长,动态数组(通过append来完成)
  3. 底层指向的是数组
  4. 内存当中是连续的存储空间,可以有效的提升cpu的执行效率。
  5. 引用类型
    组成方式
  6. 指向底层数组的指针
  7. 切片元素的长度,通过len()获取
  8. 容量,通过cap()获取
//切片的定义
var sliceVariables []int
//定义一个数组
arrayVariables := [...]int{12,21,23,55,98,2}
for i :=0;i<len(arrayVariables);i++ {
   fmt.Printf("arrayVariables[%d]=%d,地址=%p\n",i,arrayVariables[i],&arrayVariables[i])
}

//切片去引用数组
sliceVariables = arrayVariables[:]
for i:=0;i<len(sliceVariables);i++ {
   fmt.Printf("sliceVariables[%d]=%d,地址=%p\n",i, sliceVariables[i],&sliceVariables[i])
}


var sliceVariables2 []int
sliceVariables2 = arrayVariables[1:3]
//12,21,23,55,98,2
//[21 23]
fmt.Println(sliceVariables2)
//切片指向的是底层的数组
for i :=0;i<len(sliceVariables2);i++ {
   fmt.Printf("sliceVariables2[%d]=%d,地址=%p\n",i, sliceVariables2[i],&sliceVariables2[i])
}
sliceVariables2[0] = 100

fmt.Println(arrayVariables)
fmt.Println(sliceVariables2)

遍历和数组一样

切片的追加append

切片注意

  1. 切片是引用类型
  2. 切片做为参数传递给函数的意义重大,同数组,当传递较大的数组切片时可以有效的提升cpu执行效率
  3. new 用于各种类型的内存分配 返回的是指针,var variables = new(T)
    返回的是T,指针类型,即返回的是一个地址,默认是当前类型的零值,它返回了一个指针,指向新分配的类型 T 的零值。也就是说new 返回指针。
  4. make 用来分配内存主要用来分配引用类型,比如channel ,map ,slice,返回一个有初始值 (非零) 的 T 类型,不是T类型

函数

函数声明包括函数名、形式参数列表、返回值列表(可省略)以及函数体。
func 函数名(形式参数列表)(返回值列表){
函数体
}

可变参数

func actionVariables3(args ...interface{}) {
    for _,value := range args {
        switch value.(type) {
        case int:
            fmt.Println(value,"int")
        case string:
            fmt.Println(value,"string")
        case float64:
            fmt.Println(value,"float64")
        case bool:
            fmt.Println(value,"bool")
        default:
            fmt.Println(value,"unknow")
        }
    }
}

匿名函数

//1
sumVariables := func(var1,var2 int) int {
        return var1 + var2
    }(1,2)
 //2
 func1 := func(var1,var2 int) int{
        return var1 - var2
    }
    - //将一个匿名函数赋值给一个变量,这个变量存储的是这个匿名的地址
    func1 := func(var1,var2 int) int{
        return var1 - var2
    }

函数参数传递方式

  1. 值传递,内存通常在栈上保存
  2. 引用传递 ,内存通常在堆上, 其实,不管是值传递还是引用传递,传递给函数的都是变量的副本,不同的是,值传递的是值的拷贝,引用传递的是地址的拷贝,一般来说,地址拷贝效率高,因为数据量小,而值拷贝决定拷贝的 数据大小,数据越大,效率越低。
    值类型和引用类型
    **值类型:**基本数据类型 int 系列, float 系列, bool, string 、数组和结构体 struct
    引用类型:指针、slice 切片、map、管道 chan、interface 等都是引用类型

变量作用域

  1. 函数内部声明/定义的变量叫局部变量,作用域仅限于函数内部
  2. 函数外部声明/定义的变量叫全局变量,作用域在整个包都有效,如果其首字母为大写,则作用 域在整个程序有效
  3. 如果变量是在一个代码块,比如 for / if 中,那么这个变量的的作用域就在该代码块
    变量符合就近原则

闭包

所谓的闭包是指有权访问另一个函数作用域中的变量的函数,就是在一个函数内部创建另一个函数

示例1.

在这里插入图片描述

package main

import (
	"fmt"
	"strings"
)

//import "fmt"

func main() {
	function := makeSuffix(".jpg")

	name := function("hello.png")
	fmt.Println(name)


}
//实现加后缀
func makeSuffix(suffix string) func(string) string  {
	return func(name string) string {

		if !strings.HasSuffix(name,suffix){
			return name + suffix
		}
		return name
	}

}

示例二 累计器

func counter() func() int {
	i := 0

	res := func () int{
		i += 1
		return i
	}
	return res
}

defer关键字

  1. 当 go 执行到一个 defer 时,不会立即执行 defer 后的语句,而是将 defer 后的语句压入到一个栈 中 然后继续执行函数下一个语句。
  2. 当函数执行完毕后,在从 defer 栈中,依次从栈顶取出语句执行(注:遵守栈 先入后出的机制),
    所以同学们看到前面案例输出的顺序。
  3. 在 defer 将语句放入到栈时,也会将相关的值拷贝同时入栈。
  4. defer 在return之后
    defer 最主要的价值是在,当函数执行完毕后,可以及时的释放函数创建的资源。
  5. 在 golang 编程中的通常做法是,创建资源后,比如(打开了文件,获取了数据库的链接,或者是 锁资源), 可以执行 defer file.Close() defer connect.Close()
  6. 在 defer 后,可以继续使用创建资源.
  7. 当函数完毕后,系统会依次从 defer 栈中,取出语句,关闭资源.

defer示例一

func main() {
	a:=10
	b := 20
	defer func() {
		fmt.Println("匿名函数a",a)
		fmt.Println("匿名函数b",b)
	}()
	a = 100
	b = 200
	fmt.Println("main函数a",a)
	fmt.Println("main函数b",b)
	/**
	main函数a 100
	main函数b 200
	匿名函数a 100
	匿名函数b 200
	由于defer调用了函数不回立即执行,等待执行完毕之后在执行,那时a和b已经变成100,200了
	*/
}

示例二

func main() {
	a:=10
	b := 20
	defer func(a,b int) {
		fmt.Println("匿名函数a",a)
		fmt.Println("匿名函数b",b)
	}(a,b)
	a = 100
	b = 200
	fmt.Println("main函数a",a)
	fmt.Println("main函数b",b)
	/**
	main函数a 100
	main函数b 200
	匿名函数a 10
	匿名函数b 20
	由于defer调用了函数不回立即执行,但是已经完成参数的传递
	*/
}

示例三

func main() {
	fmt.Println("defer begin")
	// 将defer放入延迟调用栈
	defer fmt.Println(1)
	defer fmt.Println(2)
	// 最后一个放入, 位于栈顶, 最先调用
	defer fmt.Println(3)
	fmt.Println("defer end")
	/**
	defer begin
	defer end
	3
	2
	1

	*/
}

装饰器模式

装饰器模式(Decorator)是一种软件设计模式,其应用场景是为某个已经存在的功能模块(类或者函数)添加一些「装饰」功能,而又不会侵入
和修改原有的功能模块。
java等语言实现装饰器模式可以直接用注解实现,但是GO没有提供「注解」之类的语法糖,在函数式编程中,要实现装饰器模式,可以借助高阶函数来实现.

示例

//计算乘法的函数
func multiply(a,b int) int  {
	return  a*b
}
func main() {
	a:=2
	b:=4
	c:= multiply(a,b)
	fmt.Println(a," X " , b ," = " , c )
}

若要实现乘法计算的时捡,则需要对其实行装饰器

type Multiply func(a,b int) int

func multiply(a,b int) int  {
	return  a*b
}
//装饰器,传入函数,并返回结果
func execTime(f Multiply) Multiply  {
	return func(a, b int) int {
		start := time.Now()
		c := f(a,b)
		end := time.Since(start)
		fmt.Println("执行耗时",end)
		return c

	}

}
func main() {
	a:=2
	b:=4
	decorator := execTime(multiply)
	c := decorator(a,b)
	fmt.Println(a," X " , b ," = " , c )
}

示例二

//比较位运算和乘法云算时间
type Multiply func(a,b int) int

func multiply(a,b int) int  {
	return  a*b
}
func multiply2(a,b int) int  {
	return a << b

}
func execTime(f Multiply) Multiply  {
	return func(a, b int) int {
		start := time.Now()
		for i := 0; i < 1000000; i++ {
			f(a,b)
		}
		c:=f(a,b)
		end := time.Since(start)
		fmt.Println("执行耗时",end)
		return c

	}

}
func main() {
	a:=2
	b:=4
	decorator := execTime(multiply)
	c := decorator(a,b)
	fmt.Println(a," X " , b ," = " , c )

	decorator2 := execTime(multiply2)
	c2 := decorator2(a,b)
	fmt.Println(a," << " , b ," = " , c2 )

}

Go语言计算函数执行时间
Since() 函数返回从 t 到现在经过的时间,等价于time.Now().Sub(t)。
Go语言递归函数
构成递归需要具备以下条件:

  1. 一个问题可以被拆分成多个子问题;
  2. 拆分前的原问题与拆分后的子问题除了数据规模不同,但处理问题的思路是一样的;
  3. 不能无限制的调用本身,子问题需要有退出递归状态的条件。
  4. 注意:编写递归函数时,一定要有终止条件,否则就会无限调用下去,直到内存溢出。
    示例一
//实现函数嘉和
func main() {
	c := run(100)
	fmt.Println(c)
}

func run(num int) int {
	if num == 0 {
		return 0
	}else{
		return run(num-1) +num
	}

}

实现斐波那契数列

func main() {
	res := 0
	for i := 1; i < 10; i++ {
		res = fibonacci(i)
		fmt.Println("fibonacci(i)=",res)
	}
	
}

func fibonacci(n int) (res int)  {
	if n <= 2 {
		res = 1
	}else{
		res = fibonacci(n-1) + fibonacci(n-2)
	}
	return
}

在这里插入图片描述

func main() {
	c := f1(3)
	fmt.Println(c)
}
func f1(n int) int  {
	if n == 1 {
		return 3
	}else{
		return 2*f1(n-1)+1
	}
}

在这里插入图片描述

func main() {
	c := peach(1)
	fmt.Println(c)
}

func peach(n int) int  {
	if n > 10 || n < 1 {
		return 0
	}
	if n == 10 {
		return 1
	}else {
		return (peach(n+1) + 1) * 2
	}

}

综合联系
在这里插入图片描述

func main() {
	var n int
	fmt.Println("请输入打印金字塔层数")
	fmt.Scanln(&n)
	printPyramind(n)
}
func printPyramind(n int)  {
	for i := 1; i <= n ; i++ {
		for k := 0; k < n - i; k++ {
			fmt.Print(" ")
		}
		for j := 0; j < 2 * i -1; j++ {
			fmt.Print("*")
		}
		fmt.Println()
	}

}

总结

  1. 基本数据类型和数组默认都是值传递的,即进行值拷贝。在函数内修改,不会影响到原来的值。
  2. 如果希望函数内的变量能修改函数外的变量(指的是默认以值传递的方式的数据类型),可以传 入变量的地址&,函数内以指针的方式操作变量
  3. Go 函数不支持函数重载
  4. 使用 _ 标识符,忽略返回值
  5. 如果一个函数的形参列表中有可变参数,则可变参数需要放在形参列表最后。

哈希函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值