荥阳网站建设多少钱,深圳相册制作公司,企业网站内容建设 知乎,wordpress朋友圈图片函数声明和调用 go语言是通过func关键字声明一个函数的#xff0c;声明语法格式如下 func 函数名(形式参数) (返回值) {函数体return 返回值 // 函数终止语句
}函数名#xff1a;由字母、数字、下划线组成。但函数名的第一个字母不能是数字。在同一个包内#xff0c;函数名…函数声明和调用 go语言是通过func关键字声明一个函数的声明语法格式如下 func 函数名(形式参数) (返回值) {函数体return 返回值 // 函数终止语句
}函数名由字母、数字、下划线组成。但函数名的第一个字母不能是数字。在同一个包内函数名也称不能重名包的概念详见后文。 形式参数参数由参数变量和参数变量的类型组成多个参数之间使用,分隔。 返回值返回值由返回值变量和其变量类型组成也可以只写返回值的类型多个返回值必须用()包裹并用,分隔。 函数体实现指定功能的代码块。 func cal_sum100() {// 计算1-100的和var s 0for i : 1; i 100; i {s i}fmt.Println(s)
}声明一个函数并不会执行函数内代码只是完成一个一个包裹的作用。真正运行函数内的代码还需要对声明的函数进行调用一个函数可以在任意位置多次调用。调用一次即执行一次该函数内的代码。 func cal_sum100() {// 计算1-100的和var s 0for i : 1; i 100; i {s i}fmt.Println(s)
}
cal_sum100() 函数参数 什么是参数函数为什么需要参数呢将上面的打印的两个菱形换乘打印一个6行的和一个8行的如何实现呢这就涉及到了函数参数。 再比如上面我们将计算1-100的和通过函数实现了但是完成新的需求 分别计算并在终端打印1-100的和1-150的和以及1-200的和 package mainimport fmtfunc cal_sum100() {// 计算1-100的和var s 0for i : 1; i 100; i {s i}fmt.Println(s)
}func cal_sum150() {// 计算1-100的和var s 0for i : 1; i 150; i {s i}fmt.Println(s)
}func cal_sum200() {// 计算1-100的和var s 0for i : 1; i 200; i {s i}fmt.Println(s)
}func main() {cal_sum100()cal_sum150()cal_sum200()
}这样当然可以实现但是是不是依然有大量重复代码一会发现三个函数出了一个变量值不同以外其他都是相同的所以为了能够在函数调用的时候动态传入一些值给函数就有了参数的概念。 参数从位置上区分分为形式参数和实际参数。 // 函数声明
func 函数名(形式参数1 参数1类型,形式参数2 参数2类型,...){函数体
}
// 调用函数
函数名(实际参数1,实际参数2,...) 函数每次调用可以传入不同的实际参数传参的过程其实就是变量赋值的过程将实际参数按位置分别赋值给形参。 还是刚才的案例用参数实现 package mainimport fmtfunc cal_sum(n int) {// 计算1-100的和var s 0for i : 1; i n; i {s i}fmt.Println(s)
}func main() {cal_sum(100)cal_sum(101)cal_sum(200)
}这样是不是就灵活很多了呢所以基本上一个功能强大的函数都会有自己需要的参数让整个业务实现更加灵活。 位置参数 位置参数有时也称必备参数指的是必须按照正确的顺序将实际参数传到函数中换句话说调用函数时传入实际参数的数量和位置都必须和定义函数时保持一致。 // 函数声明 两个形参x,y
func add_cal(x int,y int){fmt.Println(xy)
}func main() {// 函数调用按顺序传参// add_cal(2) // 报错// add_cal(232,123,12) // 报错add_cal(100,12)
}不定长参数 如果想要一个函数能接收任意多个参数或者这个函数的参数个数你无法确认就可以使用不定长参数也叫可变长参数。Go语言中的可变参数通过在参数名后加…来标识。 package mainimport fmtfunc sum(nums ...int) { //变参函数fmt.Println(nums,nums)total : 0for _, num : range nums {total num}fmt.Println(total)
}func main() {sum(12,23)sum(1,2,3,4)}注意可变参数通常要作为函数的最后一个参数。 package mainimport fmtfunc sum(base int, nums ...int) int {fmt.Println(base, nums)sum : basefor _, v : range nums {sum sum v}return sum
}func main() {ret : sum(10,2,3,4)fmt.Println(ret)}go的函数强调显示表达的设计哲学没有默认参数 函数返回值 函数的返回值是指函数被调用之后执行函数体中的代码所得到的结果这个结果通过 return 语句返回。return 语句将被调函数中的一个确定的值带回到主调函数中供主调函数使用。函数的返回值类型是在定义函数时指定的。return 语句中表达式的类型应与定义函数时指定的返回值类型必须一致。 func 函数名(形参 形参类型)(返回值类型){// 函数体return 返回值
}变量 函数(实参) // return 返回的值赋值给某个变量程序就可以使用这个返回值了。同样是设计一个加法计算函数 func add_cal(x,y int) int{return xy
}func main() {ret : add_cal(1,2)fmt.Println(ret)
}无返回值,声明函数时没有定义返回值函数调用的结果不能作为值使用
func foo(){fmt.Printf(hi,yuan!)return // 不写return默认return空
}func main() {// ret : foo() // 报错:无返回值不能将调用的结果作为值使用foo()}返回多个值,函数可以返回多个值
func get_name_age() (string, int) {return yuan,18
}func main() {a, b : get_name_age()fmt.Println(a, b)
}返回值命名 函数定义时可以给返回值命名并在函数体中直接使用这些变量最后通过return关键字返回。 func calc(x, y int) (sum, sub int) {sum x ysub x - yreturn // return sum sub
}作用域
所谓变量作用域即变量可以作用的范围。 作用域scope通常来说程序中的标识符并不是在任何位置都是有效可用的而限定这个标识符的可用性的范围就是这个名字的作用域。 变量根据所在位置的不同可以划分为全局变量和局部变量 局部变量 写在{}中或者函数中或者函数的形参, 都是局部变量 1、局部变量的作用域是从定义的那一行开始, 直到遇到 } 结束或者遇到return为止 2、局部变量, 只有执行了才会分配存储空间, 只要离开作用域就会自动释放 3、局部变量存储在栈区 4、局部变量如果没有使用, 编译会报错。全局变量如果没有使用, 编译不会报错 5、:只能用于局部变量, 不能用于全局变量 全局变量 函数外面的就是全局变量 1、全局变量的作用域是从定义的那一行开始, 直到文件末尾为止 2、全局变量, 只要程序一启动就会分配存储空间, 只有程序关闭才会释放存储空间, 3、全局变量存储在静态区(数据区) func foo() {// var x 10x 10fmt.Println(x)
}var x 30 // 全局变量func main() {// var x 20foo()fmt.Println(x)
}注意iffor语句具备独立开辟作用域的能力 // if的局部空间
if true{x:10fmt.Println(x)
}fmt.Println(x)// for的局部空间
for i:0;i10 ;i {}
fmt.Println(i)值传递
// 案例1
var x 10
fmt.Printf(x的地址%p\n, x)
y : x
fmt.Printf(y的地址%p\n, y)
x 100
fmt.Println(y)// 案例2
var a []int{1, 2, 3}
b : a
a[0] 100
fmt.Println(b)// 案例3
var m1 map[string]string{name:yuan}
var m2 m1
m2[age] 22
fmt.Println(m1)函数传参
package mainimport fmtfunc swap(a int, b int) {c : aa bb cfmt.Println(a, a)fmt.Println(b, b)
}func main() {var x 10var y 20swap(x, y)fmt.Println(x, x)fmt.Println(y, y)
}package mainimport fmtfunc func01(x int) {x 100
}func func02(s []int) {fmt.Printf(func02的s的地址%p\n,s)s[0] 100// s append(s, 1000)
}func func03(p *int) {*p 100
}
func main() {// 案例1var x 10func01(x)fmt.Println(x)// 案例2var s []int{1, 2, 3}fmt.Printf(main的s的地址%p\n,s)func02(s)fmt.Println(s)//案例3var a 10var p *int afunc03(p)fmt.Println(a)}思考之前的scan函数为什么一定传参 Go语言中所有的传参都是值传递传值都是一个副本一个拷贝。因为拷贝的内容有时候是非引用类型int、string、struct等这些这样就在函数中就无法修改原内容数据有的是引用类型指针、map、slice、chan等这些这样就可以修改原内容数据。 func makemap(t *maptype, hint int64, h *hmap, bucket unsafe.Pointer) *hmap {
}make函数创建的map就是一个指针类型工作原理类似于案例3所以map数据和切片数据一样虽然值拷贝但依然可以修改原值。 匿名函数 匿名函数顾名思义没有函数名的函数。 匿名函数的定义格式如下 func(参数列表)(返回参数列表){函数体
}匿名函数可以在使用函数的时候再声明调用 //(1)
(func() {fmt.Println(yuan)
})()
//(2)
var z (func(x,y int) int {return x y})(1,2)fmt.Println(z)也可以将匿名函数作为一个func类型数据赋值给变量 var f func() {fmt.Println(yuan)
}fmt.Println(reflect.TypeOf(f)) // funcf() // 赋值调用调用Go语言不支持在函数内部声明普通函数只能声明匿名函数。 func foo() {fmt.Println(foo功能)f : func(){fmt.Println(bar功能)}fmt.Println(f)
}高阶函数 一个高阶函数应该具备下面至少一个特点 将一个或者多个函数作为形参 返回一个函数作为其结果 首先明确一件事情函数名亦是一个变量 package mainimport (fmtreflect
)func addCal(x int, y int)int{return x y
}func main() {var a addCala(2, 3)fmt.Println(a)fmt.Println(addCal)fmt.Println(reflect.TypeOf(addCal)) // func(int, int) int}结论函数参数是一个变量所以函数名当然可以作为一个参数传入函数体,也可以作为一个返回值。 函数参数
package mainimport (fmttime
)func timer(f func()){timeBefore : time.Now().Unix()f()timeAfter : time.Now().Unix()fmt.Println(运行时间, timeAfter - timeBefore)}func foo() {fmt.Println(foo function... start)time.Sleep(time.Second * 2)fmt.Println(foo function... end)
}func bar() {fmt.Println(bar function... start)time.Sleep(time.Second * 3)fmt.Println(bar function... end)
}func main() {timer(foo)timer(bar)
}注意如果函数参数也有参数该怎么写呢 package mainimport fmtfunc add(x,y int ) int {return xy
}func mul(x,y int ) int {return x*y
}// 双值计算器
func cal(f func(x,y int) int,x,y int,) int{return f(x,y)}func main() {ret1 : cal(add,12,3,)fmt.Println(ret1)ret2 : cal(mul,12,3,)fmt.Println(ret2)}函数返回值
package mainimport (fmt
)func foo() func(){inner : func() {fmt.Printf(函数inner执行)}return inner
}func main() {foo()()}闭包函数
闭包并不只是一个go中的概念在函数式编程语言中应用较为广泛。
首先看一下维基上对闭包的解释 在计算机科学中闭包英语Closure又称词法闭包Lexical Closure或函数闭包function closures是引用了自由变量外部非全局的函数。 简单来说就是一个函数定义中引用了函数外定义的变量并且该函数可以在其定义环境外被执行。这样的一个函数我们称之为闭包函数。 闭包就是当函数可以记住并访问所在的词法作用域时就产生了闭包即使函数是在当前词法作用域之 外执行。 需要注意的是自由变量不一定是在局部环境中定义的也有可能是以参数的形式传进局部环境另外在 Go 中函数也可以作为参数传递因此函数也可能是自由变量。 闭包中自由变量的生命周期等同于闭包函数的生命周期和局部环境的周期无关。 闭包在运行时可以有多个实例不同的引用环境和相同的函数组合可以产生不同的实例。 实现一个计数器函数不考虑闭包的情况下最简单的方式就是声明全局变量 package mainimport fmtvar i 0func counter() {ifmt.Println(i)
}func main() {counter()counter()counter()
}这种方法的一个缺点是全局变量容易被修改安全性较差。闭包可以解决这个问题从而实现数据隔离。 package mainimport fmtfunc getCounter() func() { var i 0return func() {ifmt.Println(i)}}func main() {counter : getCounter()counter()counter()counter()counter2 : getCounter()counter2()counter2()counter2()
}getCounter完成了对变量i以及counter函数的封装然后重新赋值给counter变量counter函数和上面案例的counter函数的区别就是将需要操作的自由变量转化为闭包环境。 闭包函数应用案例
在go语言中可以使用闭包函数来实现装饰器
案例1计算函数调用次数 package mainimport (fmtreflectruntime
)// 函数计数器
func getCounter(f func()) func() {calledNum : 0 // 数据隔离return func() {f()calledNum 1// 获取函数名fn : runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()fmt.Printf(函数%s第%d次被调用\n, fn, calledNum)}
}// 测试的调用函数
func foo() {fmt.Println(foo function执行)
}func bar() {fmt.Println(bar function执行)
}func main() {/*fooAndCounter : getCounter(foo) // 针对foo的计数器fooAndCounter()fooAndCounter()fooAndCounter()barAndCounter : getCounter(bar)barAndCounter()barAndCounter()barAndCounter()*/foo : getCounter(foo) // 开放原则foo()foo()foo()bar : getCounter(bar)bar()bar()
}案例2计算函数运行时间
package mainimport (fmttime
)func GetTimer(f func(t time.Duration)) func(duration time.Duration) {return func(t time.Duration) {t1 : time.Now().Unix()f(t)t2 : time.Now().Unix()fmt.Println(运行时间, t2-t1)}}func foo(t time.Duration) {fmt.Println(foo功能开始)time.Sleep(time.Second * t)fmt.Println(foo功能结束)
}func bar(t time.Duration) {fmt.Println(bar功能开始)time.Sleep(time.Second * t)fmt.Println(bar功能结束)
}func main() {var foo GetTimer(foo)foo(3)var bar GetTimer(bar)bar(2)}关键点将一个功能函数作为自由变量与一个装饰函数封装成一个整体作为返回值赋值给一个新的函数变量这个新的函数变量在调用的时候既可以完成原本的功能函数又可以实现装饰功能。