首页 > 其他分享 >Go语言函数

Go语言函数

时间:2024-11-13 21:20:40浏览次数:1  
标签:函数 int fmt Println func Go main 语言

函数

入门简单精通难

函数式编程

1、什么是函数

  • 函数就是一段代码的集合
  • go语言中至少要有一个 main函数
  • 函数需要有一个名字,独立定义的情况下。见名知意
  • 函数可能需要有一个结果,也可能没有
func print() {
	fmt.Println("hello")
}
package main

import "fmt"

func main() {
	// 调用函数,一定要加()
	print()
}

// 定义函数
/*
func 函数名(参数1,参数2,...) {
	// 函数的集合,一段代码的集合
	// 封装的思维,代码多了我们将一些功能代码抽离出来,方便复用
}
*/
func print() {
	fmt.Println("hello")
}

2、函数的具体定义

  • 无参无返回值
  • 有一个参数的函数
  • 有两个 or 多个参数的函数
  • 有一个返回值的函数
  • 有两个 or 多个返回值的函数
package main

import "fmt"


func main() {
	// 函数调用
    a := max(113017245, 24560445)
	fmt.Println(a)
}


// 函数定义,两个参数,一个返回值
func max(num1, num2 int) int {
	if num1 > num2 {
		return num1
	} else {
		return num2
	}
}
 
// 多个返回值
package main

import "fmt"

func main() {
	zc, area := fun1(1, 2)
	fmt.Println(zc, area)
}

func fun1(len, wid float64) (float64, float64) {
	area := len * wid
	zc := (len + wid) * 2
	return zc, area
}

// 结合匿名函数使用
package main

import "fmt"

func main() {
	a, b := swap("abc", "def")
	fmt.Printf("a=%s,b=%s\n", a, b)
	c, _ := swap("123", "456")
	fmt.Printf("c=%s\n", c)
	_, d := swap("aaa", "bbb")
	fmt.Printf("d=%s", d)
}

func swap(a, b string) (string, string) {
	return b, a
}

3、可变参数

package main

import "fmt"

func main() {
	sum := getSum(112, 23, 45, 5, 2, 3, 4)
	fmt.Printf("sum=%d\n", sum)
}

func getSum(nums ...int) int {
	// 可变参数: 一个函数的参数类型确定,参数的个数不确定,可以使用可变参数
	// 可变参数如果有多个参数必须放在最后一个参数
	sum := 0
	for i := 0; i < len(nums); i++ {
		sum += nums[i]
	}
	return sum
}

4、函数中作用域

package main

import "fmt"

// 函数作用域
// 局部变量
// 1、函数内部定义的变量,只能在函数内部调用
// 全部变量(全局变量)
// 1、函数外部定义的变量,默认我们定义在上面,方便文件统一查看和管理全局变量
var num int = 1

func main() {
	temp := 100
	// 定义一个只在 if 中生效的变量 if 临时变量(a,b := 1,2);条件判断{}
	// 小作用域可以用到大作用域中的变量,反之则不行。
	// 对于很多一次性的变量,都可以这么写
	if a := 1; a <= 10 {
		fmt.Println(temp)
		fmt.Println(num)
		fmt.Println(a)
		// 就近原则
		if a := 2; a <= 10 {
			fmt.Println(a)
		}
	}
	fmt.Println(num)
}

func f1() {
	fmt.Println(num)
}
func f2() {

}

5、递归函数

package main

import "fmt"

func main() {
	/*
		定义:一个函数自己调用自己,就叫做递归函数
		注意:递归函数需要有一个出口,逐渐向出口靠近,没有出口就会形成死循环。
	*/
	sum := getsum(5)
	fmt.Printf("%d\n", sum)
}

func getsum(i int) int {
	sum := 0
	if i == 1 {
		sum = 1
	} else {
		sum = i + getsum(i-1)
	}
	return sum
}

6、defer延迟函数

defer函数或者方法:一个函数或方法的执行被延迟了

  • 你可以在函数中添加多个defer语句当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回,特别是当你在进行一些打开资源的操作时i/o 流,遇到错误需要提前返回,在返回前你需要关闭相应的资源,不然很容易造成资源泄露等问题
  • 如果有很多调用 defer,那么 defer 是采用 后进先出(栈) 模式。
package main

import "fmt"

func main() {
	// defer  主要是用来处理善后的任务
	// 特点:多个defer,最后倒序输出
	f("1")
	fmt.Println("2")
	defer f("3")
	f("4")
	defer f("5")
	f("6")
	defer f("7")

}

func f(s string) {
	fmt.Println(s)
}
// 结果:
1
2
4
6
7
5
3
// defer传参的调用时机
package main

import "fmt"

func main() {
	n := 10
	fmt.Printf("main中的n=%d\n", n)
	defer f2(n) // 在此时函数已经被调用了,参数已经传递进去了,但是没有执行
	n++
	fmt.Printf("main中的n=%d\n", n)
}

func f2(n int) {
	fmt.Printf("函数中的n=%d\n", n)
}

// 结果:
main中的n=10
main中的n=11
函数中的n=10

defer在文件流之后进行关闭操作

文件.open()  二进制流 建立了连接
defer 文件.close() // 关闭文件

//读写操作
//.......

defer:程序会报错: 异常(程序执行的时候突然报错了)、错误(我们开发的时候知道的预期错误)

善后工作:defer 处理异常。

7、函数的数据类型

  • func (xxxx,xxx) xxx,xxxx
  • 函数也是一种数据类型,可以定义函数类型的变量
package main

import "fmt"

func main() {
	fmt.Printf("f3:%T\n", f3) // f3:func()
	fmt.Printf("f4:%T\n", f4) // f4:func(int) int
	// 函数在Go语言中本身也是一个数据类型,加了() 是调用函数,不加(), 函数也是一个变量,可以赋值给别人。
	// 函数的类型就等于该函数创建的类型,他也可以赋值给
	f5 := f4
	fmt.Println(f5(3))
	// var f6 func(int,int) int
}

func f3() {

}

func f4(a int) int {
	return a
}

函数在Go语言中不是一个简单的调用或者接收结果的。

函数在Go中是一个符合类型,可以看做是一个特殊的变量。var 定义吗,赋值。类型相同即可

函数类型的样子 :var f1 函数名(参数) 结果

变量名:指向一段内存 (num --> 0x11111aaaa)

函数名:指向一段函数体的内存地址,是一种特殊类型的变量。我们可以将一个函数赋值给另外一个类型相同的函数

8、匿名函数

package main

import (
	"fmt"
)

// 匿名函数:没有名字的函数
func main() {
	f10()
	f2 := f10
	f2()
	// 匿名函数理论上只能执行一次
	func() {
		fmt.Println("wo shi niming hanshu")
	}()

	// 将匿名函数进行赋值,就可以实现多次调用了
	f3 := func() {
		fmt.Println("wo shi ni ming han shu")
	}
	f3()

	// 匿名函数添加参数
	func(a, b int) {
		fmt.Printf("%d,%d\n", a, b)
	}(1, 2)

	// 将匿名函数的返回值给一个变量

	// 由于Go语言中的函数是一个特殊的变量,支持匿名操作
	// Go语言支持函数式编程
	a := func(a, b int) int {
		return a + b
	}(1, 2)
	fmt.Println(a)
}

func f10() {
	fmt.Println("wo shi f10")
}

9、回调函数

高阶函数:可以将一个函数作为另外一个函数的参数。

fun1()

fun2(fun1)

fun1 函数作为 fun2 函数的参数

fun2函数,叫做高阶函数,接收了另外一个函数作为参数的函数

fun1函数,叫做回调函数,作为另外一个函数的参数

package main

import "fmt"

func main() {
	r1 := yunsuan(2, 4, add)
	fmt.Println(r1)
	sub := func(a, b int) int {
		return a - b
	}

	// 将匿名函数作为函数参数
	r2 := yunsuan(4, 2, sub)
	fmt.Println(r2)

	// 能够直接传递匿名函数
	r3 := yunsuan(6, 2, func(a int, b int) int {
		if b == 0 {
			fmt.Println("除数不能为0")
			return 0
		}
		return a / b
	})
	fmt.Println(r3)
}

// 高阶函数
func yunsuan(a, b int, fun func(int, int) int) int {
	r := fun(a, b)
	return r
}

func add(a, b int) int {
	return a + b
}

10、闭包函数

一个外层函数中,有内层函数,该内层函数中,会操作外层函数的局部变量
并且该外层函数的返回值就是这个内层函数。
这个内层函数和外层函数的局部变量,统称为闭包结构。

局部变量的生命周期就会发生改变,正常的局部变量会随着函数的调用而创建,随着函数的结束而销毁
但是闭包结构中的外层函数的局部变量并不会随着外层函数的结束而销毁,因为内层函数还在继续使用

package main

import "fmt"

// 是一种特殊的结构:闭包结构,违反了程序正常的生命周期。合法的使用。程序允许的一种特殊结构,变量作用域升级了。

// 什么时候用闭包: js (xxxxxxx.html   引用大量的第三方库:10个js库,js库中很多变量名是冲突的)
// js 很多框架都是闭包结构的,防止变量冲突,全局变量污染

// 我的代码里面的变量就不会和你代码里面的变量冲突了。解决一些变量作用域冲突的问题。

/*
闭包结构:
一个外层函数中,有内层函数,该内层函数中,会操作外层函数的局部变量并且该外层函数的返回值就是这个内层函数。

在闭包结构中:局部变量的生命周期就会发生改变,
正常的局部变量会随着函数的调用而创建,随着函数的结束而销毁
但是闭包结构中的外层函数的局部变量并不会随着外层函数的结束而销毁,因为内层函数还在继续使用.

// 由于垃圾回收期不会将闭包中的变量销毁,可能会造成内存泄漏。
*/

// 你的代码变量和你同事的变量冲突了,解决。 i 新建一个变量。 第三方库中的代码都是闭包结构实现的导出。

func main() {
	r1 := incre() // 返回的是一个 increment() 内存函数,还没有执行
	fmt.Println(r1())
	fmt.Println(r1())
	fmt.Println(r1())
	fmt.Println("---------------------------------")
	r2 := incre()
	fmt.Println(r2())
	fmt.Println(r1())
	// 这里的i 并没有随着 第二次创建就被销毁归0,而是在内层函数继续调用着。
	fmt.Println(r2())
}

func incre() func() int {
	i := 0
	fun := func() int {
		i++
		return i
	}
	return fun
}

如果我们想使用闭包结构来解决全局变量污染的问题,那我们就可以写一个闭包结构来创建执行的函数。

通过这个闭包结构创建的函数内部的变量,都在这个函数中作用,不会和其他函数冲突。

闭包结果的返回值是一个函数。这个函数可以调用闭包结构中的变量。

11、函数中的参数传递

函数的参数传递中。存在的问题:值传递、引用传递。

按照数据类型存储特点

值类型:int、string、bool、float64、array...... 拷贝,创建的时候,拷贝一份

引用类型:操作的是数据的地址,切片slice、map、chan.....

(1)、值传递

package main

import "fmt"

func main() {

	arr1 := [4]int{1, 2, 3, 4}
	fmt.Println("arr1修改之前的数据:", arr1)
	update(arr1)
	fmt.Println("arr1修改之后的数据:", arr1)
}

// 更新数组
func update(arr2 [4]int) {
	fmt.Println("arr2修改之前的数据:", arr2)
	arr2[0] = 100
	fmt.Println("arr2修改之后的数据:", arr2)
}

(2)、参数传递

package main

import "fmt"

// 参数传递中值类型、引用类型的问题
func main() {
   // 定义一个切片
   s1 := []int{1, 2, 3, 4}
   fmt.Println("s1修改前的数据:", s1) // 1 2 3 4
   updatee(s1)
   fmt.Println("s1修改后的数据:", s1) // 100 2 3 4
   // 如果参数是引用类型的,那么修改函数内的值,会影响函数外的值。因为两个变量指向同一个内存空间,修改任意一个都会导致另外一个发送变化
   
   // 值传递   每个变量参数都有自己的内存空间,拷贝
   // 引用传递 每个变量参数都指向同一个内存空间,指向,改变任意一个,其他的都会发生变化。

}

// 更新数组
func updatee(s2 []int) {
   fmt.Println("s2修改前的数据:", s2) // 1 2 3 4
   s2[0] = 100
   fmt.Println("s2修改后的数据:", s2) // 100 2 3 4
}

标签:函数,int,fmt,Println,func,Go,main,语言
From: https://www.cnblogs.com/xuruizhao/p/18544829

相关文章

  • CICD03 Jenkins对golang项目构建, 结合ansible, 构建通知, 自动化构建(定时,webhook),
    2.7.2基于Maven风格的任务构建基于WAR包运行Tomcat服务器JAVA项目maven配置繁琐,功能固定不灵活,不如自由风格好用,这里推荐用自由风格脚本实现更好目前最高依赖到tomcat9,更高版本的tomcat不支持2.7.2.2安装tomcat服务器和配置#在gitlab新建java项目(此项目使用JD......
  • Go中数组和切片
    数组和切片【1】、数组1、什么是数组一组数数组需要是相同类型的数据的集合数组是需要定义大小的数组一旦定义了大小是不可以改变的。packagemainimport"fmt"//数组//数组和其他变量定义没什么区别,唯一的就是这个是一组数,需要给一个大小[6]int[10]string/......
  • go变量和常量
    go基础语法【1】、注释我们为什么要写注释?一个项目,是很多组成的。写的时候,你能看懂的。半年、一年(时间久了,自己写代码看不懂了)对接、项目很多时候都是多人协作完成。(很难沟通,直接读懂代码是比较困难)在刚开始学习的时候,不喜欢写注释。什么是注释:来描述我当前这个代码是......
  • go中if、for
    流程控制面向过程编程自上而下的执行()面向对象编程:让程序像人一样的思考来运行,而不是机械式的自上而下(架构)将世界抽象为一个个的对象,然后用程序来模拟这一个个的对象,然后进行一些人工化的操作实现现实世界的逻辑定义(属性)判断(对错,导向不同的结果)循环(很多事情本质上......
  • GO面试-切片
    一、结构介绍切片(Slice)在Go语言中,有一个很常用的数据结构,切片是一个拥有相同类型元素的可变长度的序列,它是基于数组类型做的一层封装。它非常灵活,支持自动扩容。并发不安全。切片是一种引用类型,它有三个属性:指针,长度和容量。底层源码定义:typeslicestruct{arrayunsa......
  • 二分查找(折半查找)函数与非函数写法代码介绍及其优缺点 C语言
    什么是二分查找?二分查找也叫折半查找 在有序的数组中查找目标的方法需要借助中间元素与目标值的比较来逐步缩小范围一直到找到目标元素或者不存在为止查找的步骤↓1确定左右端点的下标值(注:数组下标从0开始)2计算中间下标位置3比较中间下标位置的数组值与目标值的大......
  • C++ 移动构造和拷贝构造函数匹配
    既有拷贝构造又有移动构造这个比较好理解,普通的函数匹配规则就可以。右值移动,左值拷贝。——《C++Primer》P477我们不能隐式地将一个右值引用绑定到一个左值。有拷贝构造但没有移动构造这种情况,右值也会被拷贝。如果一个类没有移动构造函数,函数匹配规则保证该类型的对象......
  • 多项式乘幂函数之和 2
    H4.2.1.8.多项式乘幂函数之和2\(n,k\)都是给定数,没什么区别记\(S_k=\sum_{i=1}^ni^kp^i\)\(p=1\)时\(S_k\in\Theta(n^{k+1})\)\(p<1\)时\[\begin{aligned}(1-p)S_k&=\sum_{i=1}^n\left(i^k-(i-1)^k\right)p^i-n^kp^{n+1}\\&=\sum_{i=1}^n\left(......
  • javaScript对象函数初相识
    1.1、对象初相识1.1.1、对象的初识1.1.1.1、对象的定义现实生活中,万物皆对象,对象是一个具体的事物,看得见摸得着的实物。例如一本书,一辆汽车,一个人可以是“对象”,一个数据库,一张网页,一个与远程服务器的连接也可以是“对象”。例子:明星、女朋友、班主任、苹果、手机周星驰......
  • 解析 Go 切片:为何按值传递时会发生改变?|得物技术
    一、前言在Go语言中,切片是一个非常常用的数据结构,很多开发者在编写代码时都会频繁使用它。尽管切片很方便,但有一个问题常常让人感到困惑:当我们把切片作为参数传递给函数时,为什么有时候切片的内容会发生变化?这让很多人一头雾水,甚至在调试时浪费了不少时间。这篇文章简单明了地......