前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Golang之旅30-函数与闭包

Golang之旅30-函数与闭包

作者头像
皮大大
发布2021-03-02 15:57:31
发布2021-03-02 15:57:31
31500
代码可运行
举报
运行总次数:0
代码可运行

Golang知识点-函数和闭包

函数

在调用函数的时候,会给该函数分配一个新的栈区。基本数据类型一般放在栈区,引用类型放在堆区。

  • Go 语言总支持多个返回值
  • 希望忽略某个返回值,使用下划线_,表示占位符
  • 返回值如果只有一个,则可以不写
  • 基本数据类型和数组都是值传递,即进行值拷贝,在函数内修改,不会影响原来的值
  • Go语言不支持传统的函数重载(函数名相同,变量不同)
  • 函数也是一种数据类型,可以赋值给一个变量,该变量成为函数类型的变量。
  • 函数可以作为形参,进行调用
代码语言:javascript
代码运行次数:0
运行
复制
func 函数名 (参数列表) (返回值列表){
  函数体部分
  return 返回值列表
}
// --------

package main
import "fmt"

// 函数也是种数据类型
func test(a,b int){
  return a + b
}

func main(){
  x := test
  res := x(10, 20)
  fmt.Println(res)
}
代码语言:javascript
代码运行次数:0
运行
复制
package main
import "fmt"

func calc(a int, b int) res int{
  res = a + b
  return res  // res可以省略
}

func test1(n1 int){
  n1 = n1 + 10
  fmt.Println("n1=", n1)  // 20
}

func main(){
  n1 := 20
  test1(n1)
  fmt.Println("n1=", n1)   // 20
}
  • go语言中的每个文件都是一个包
  • 区分相同名字的函数、变量等标识符
  • 控制函数、变量等访问范围,即作用域
  • 为了外部访问,将函数名首字母大写
  • 文件的包名通常和所在的文件夹名相同
  • 引入包的时候,路径是从GOPATH开始的,从src下开始,不用带上src
  • 如果给包取了别名,则需要使用别名来进行访问
递归调用

在函数被调用的时候会分配一个新的栈区。栈是先进后出。

代码语言:javascript
代码运行次数:0
运行
复制
package main
improt "fmt"

func test(n int){
  if n > 2{
    n--
    test(n)
  }
  fmt.Println(n)
}

func main(){
  test(4)
}

// 2 2 3
// n=4执行if,n变成3,还有fmt;n=3执行if,n变成2,还有fmt;n=2不执行if,直接输出fmt。
// 调用输出顺序:先进后出
代码语言:javascript
代码运行次数:0
运行
复制
package main
improt "fmt"

func test(n int){
  if n > 2{
    n--
    test(n)
  }else{
    fmt.Println(n)
  }
}

func main(){
  test(4)
}

// 结果是 2
// n=3,4满足条件,根本不会执行else语句
代码语言:javascript
代码运行次数:0
运行
复制
// 斐波那契数列实现
package main
import "fmt"

func fibonacii(n int) int {
  if n == 1 || n == 2{
    return 1
  } else{
    return fibonacii(n - 1) + fibonacii(n - 2)
  }
}

func main(){
  res := fibonacii(5)
  fmt.Println(res)
}
值传递和引用传递
  1. 值类型:变量直接存储值,内存通常在中进行分配。传递效率取决于数据量的大小。
    • int\float
    • bool\string
    • 数组array
    • 结构体struct
  2. 引用类型:变量存储的是地址,地址的空间才是真正存储的数据(值),内存通常在上进行分配。如果没有任何变量引用这个地址,由GC进行垃圾回收。传递效率高
    • 指针pointer
    • 切片slice
    • 映射map
    • 管道channel
    • 接口interface

代码语言:javascript
代码运行次数:0
运行
复制
package main
import "fmt"

// 基本数据类型:值传递
func test1(n1 int){
  n1 = n1 + 10
  fmt.Println("n1=", n1)  // 30
}

// 引用传递
func test2(n1 *int){
  *n1 = *n1 + 10
  fmt.Println("n1=", *n1)
}

func main(){
  n1 := 20
  test1(n1)
  fmt.Println("n1=", n1)   // 20

  test2(&n1)
  fmt.Println("n1=", n1) // 30
}
自定义数据类型
代码语言:javascript
代码运行次数:0
运行
复制
type myInt int   // 相当于是别名;myInt和int不是同一个类型
var num1 myInt
var num2 int
num1 = 40
num2 = int(num1)   // 需要强制转换
fmt.Println(num1, num2)

type mySum func(int, int)int  // mySum等价于函数类型
Go语言支持可变参数
  • args是切片类型
  • 通过args[index]可以进行访问
  • 可变参数必须放在最后
代码语言:javascript
代码运行次数:0
运行
复制
func sum(args... int) sum int{
  //函数体部分
}

// 1到多个参数
func sum(1 int, args...int)sum int{
  //函数体部分
}
代码语言:javascript
代码运行次数:0
运行
复制
package main
import "fmt"

// 可变参数必须放在最后
func sum(n1 int, args...int)int{
  sum := n1
  for i := 0;i<len(args);i++{
    sum += args[i]
  }
  return sum
}

func main(){
  res := sum(10,20,20,30,91)
  fmt.Println(res)
}
代码语言:javascript
代码运行次数:0
运行
复制
// 两个变量的值交换

package main
import "fmt"

func swap(n1 *int, n2 *int){
  t = *n1   // 定义临时变量
  *n1 = *n2
  *n2 = t
}

func main(){
  a := 10
  b := 20
  swap(&a, &b)   // 传入的是地址
  fmt.Println(a, b)
}
init函数

每个原文件中都有个init函数;在main函数执行之前,先被调用,完成初始化工作。

  • 变量定义----init----main
代码语言:javascript
代码运行次数:0
运行
复制
package main
import "fmt"

var age = test()
func test()int{
  fmt.Println("test()...")    // 1
}

func init(){
  fmt.Println("init()...")   // 2
}

func main(){
  fmt.Println("main()....age=", age)   // 3
}
  • 如果main中引入了另一个文件,执行顺序为:

匿名函数

匿名函数可以多次使用。

  • 在定义的时候直接调用,只能调用一次
  • 将匿名函数赋值给变量
  • 全局匿名函数
代码语言:javascript
代码运行次数:0
运行
复制
package main
import "fmt"

var (
  // Fun是全局匿名函数
  Fun = func(n1,n2 int)int{
    return n1 * n2
  }
)

func main(){
  // 定义的时候直接调用
  res1 := func(n1, n2 int)int{
    return n1 + n2
  }(10,20)   // 直接赋值调用
  fmt.Println(res1)

  // 将匿名函数赋值给变量
  res2 := func(n1, n2 int)int{   // 定义匿名函数
    return n1 - n2
  }
  a := res2(80,20)
  fmt.Println(a)

  // 调用全局匿名函数
  res3 := Fun(1,3)
  fmt.Println(rew3)
}
必包

必包=函数+外部引用环境

代码语言:javascript
代码运行次数:0
运行
复制
package main
import "fmt"

fun addNumber() 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)  // str="hello$"  str="hello$$"  str="hello$$$"
    return n
  }
}

func main(){
  // 实现累加功能
  f := addNumber()  // 调用函数
  fmt.Printn(f(1))  // 11
  fmt.Printn(f(2))  // 13
  fmt.Printn(f(3))  // 16

}
defer

延时机制。在函数执行完毕之后及时释放资源。

  • 逆序执行,类似栈的结构
  • defer将语句压入栈中,同时也会将值拷贝压入栈中
代码语言:javascript
代码运行次数:0
运行
复制
package main
import "fmt"

func sum(n1, n2 int)int{
  // defer:压入栈中
  defer fmt.Println("ok1  n1=", n1)    // 3  n1=10   这两个值不会改变
  defer fmt.Println("ok2 n2=", n2)   // 2  n2=20
  // 增加部分,不会改变n1和n2的原来的值
  n1++
  n2++
  res := n1 + n2
  fmt.Println("ok3 res=", res)   // 1  res=32
  return res
}

func main(){
  res := sum(10,20)
  fmt.Println("res=", res)   // 4  res=32
}
代码语言:javascript
代码运行次数:0
运行
复制
func test(){
  // 关闭文件
  file = openfile(文件名)
  defer file.close()
  // 其他代码
}

func test(){
	// 释放数据库资源
  connect = openDatabase()
  defer connect.close()
  // 其他代码
}
变量作用域
  • 函数内部声明的变量称为局部变量,作用域仅在函数内部
  • 函数外部声明的变量叫全局变量,作用域在整个包内有效;如果首字母大写,则在整个程序内有效
  • 如果变量在一个代码块之内入for / if之内,变量的作用域在代码块之内
代码语言:javascript
代码运行次数:0
运行
复制
package main
import "fmt"

var name = "tom"   // 全局变量
func test1(){
  fmt.Println(name)
}

func test2(){
  name := "jack"   // 局部变量
  fmt.Println(name)
}

func main(){
  fmt.Println(name)  // tom
  test1()  // tom
  test2()  // jack
  test1()  // tom
}
代码语言:javascript
代码运行次数:0
运行
复制
package main
import "fmt"

var name = "tom"   // 全局变量
func test1(){
  fmt.Println(name)
}

func test2(){
  name = "jack"    // 相当于是修改全局变量
  fmt.Println(name)
}

func main(){
  fmt.Println(name)  // tom
  test1()  // tom
  test2()  // jack
  test1()  // jack
}
函数练习题
打印金字塔
代码语言:javascript
代码运行次数:0
运行
复制
package main
import "fmt"



func printPyramid(totalLevel int){

  for i:=1; i <= totalLevel;i++{  // i表示层数
    // 打印空格
    for k := 1;k <= totalLevel - i;k++{
      fmt.Print(" ")
    }

    for j :=1;j <= 2 * i - 1;j++{  // j表示每层打印多少个*
      // 打印空金字塔
      if j == 1 || j == 2 * i-1 || i == totalLevel {
      		fmt.Print("*")
      } else{
        fmt.Print(" ")
      }

    }
    fmt.Println()   // 换行操作
  }
}

func main(){
  // 调用函数
  var n int
  fmt.Println("请输入层数")
  fmt.Scanln(&n)
  printPyramid(n)
}
字符串中常用的函数
  • len()
  • r := []rune(str),字符遍历,解决中文的问题
  • str = strong.Itoa(1234)
  • var bytes = []byte(“hello go”)
  • str = str([]byte{98,97,99})
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-11-5,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Golang知识点-函数和闭包
    • 函数
    • 递归调用
    • 值传递和引用传递
    • 自定义数据类型
    • Go语言支持可变参数
    • init函数
    • 匿名函数
    • 必包
    • defer
    • 变量作用域
    • 函数练习题
      • 打印金字塔
    • 字符串中常用的函数
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档