分享
GO语言基础入门--函数
chenqijing2 · · 2753 次点击 · · 开始浏览这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。
函数
本Go语言基础入门内容均来自于《Go语言编程》,个人记录,加深理解。
函数声明
函数的基本组成为:关键字func、函数名、参数列表、返回值、函数体和返回语句。
一般的: func 函数名 (传入参数) (返回参数) {函数体}
如:
package mymath
import "errors"
func Add(a int, b int) (ret int, err error) {
if a < 0 || b < 0 { // 假设这个函数只支持两个非负数字的加法
err= errors.New("Should be non-negative numbers!")
return
}
return a + b, nil // 支持多重返回值
}如果参数列表中若干个相邻的参数类型相同,可以省略前面变量的类型声明,如:
func Add(a, b int</span>)(ret int, err error) {
// ...
}如果返回值列表中多个返回值的类型相同,也可以用同样的方式合并。如果函数只有一个返回值,也可以这么写:
func Add(a, b int) int {
// ...
}函数调用
1、导入该函数所在包
2、如下调用
import "mymath"// 假设Add被放在一个叫 mymath的包中 // ... c := mymath.Add(1, 2)
值得注意的是:如果想在外部调用该包函数,所调用的函数名必须大写。
Go语言有着这样的规定:
小写字母开头的函数只在本包内可见,大写字母开头的函数才能被其他包使用。
这个规则也适用于类型和变量的可见性。
不定参数
这个规则也适用于类型和变量的可见性。
不定参数
不定参数是指函数传入的参数个数为不定数量。为了做到这点,首先需要将函数定义为接受不定参数类型:
func myfunc(args ... int) {//参数名 ...type
for _, arg := range args {
fmt.Println(arg)
}
}该函数可接收不定数量参数,如:myfunc(2, 3, 4) myfunc(1, 3, 7, 13)
从内部实现机理上来说,类型...type本质上是一个数组切片,也就是[]type,这也是为什么上面的参数args可以用for循环来获得每个传入的参数。
假如没有...type这样的语法糖,开发者将不得不这么写:
func myfunc2(args [] int) {
for _, arg := range args {
fmt.Println(arg)
}
}从函数的实现角度来看,这没有任何影响,该怎么写就怎么写。但从调用方来说,情形则完全不同:myfunc2([] int{1, 3, 7, 13})你会发现,我们不得不加上[]int{} 来构造一个数组切片实例。不定参数的传递
func myfunc(args ... int) {
// 按原样传递
myfunc3(args...)
// 传递片段,实际上任意的int slice都可以传进去
myfunc3(args[1:]...)
}
之前的例子中将不定参数类型约束为 int ,如果你希望传任意类型,可以指定类型为interface{} 。
下面是Go语言中fmt,Printf()函数原型:
func Printf(format string, args ... interface{}) {
// ...
}下面代码可以让我们清晰的知道传入参数的类型:package main
import "fmt"
func MyPrintf(args ... interface{}) {
for _, arg := range args {
switch arg. (type) {
case int:
fmt.Println(arg, "is an int value.")
case string:
fmt.Println(arg, "is a string value.")
case int64:
fmt.Println(arg, "is an int64 value.")
default:
fmt.Println(arg, "is an unknown type.")
}
}
}
func main() {
var v1 int = 1
var v2 int64 = 234
var v3 string = "hello"
var v4 float32 = 1.234
MyPrintf(v1, v2, v3, v4)
}该程序的输出结果为:
1 is an int value. 234 is an int64 value. hello is a string value. 1.234 is an unknown type.
匿名函数和闭包
在Go里面,函数可以像普通变量一样被传递或使用,这与C语言的回调函数比较类似。不同
的是, Go语言支持随时在代码里定义匿名函数。
匿名函数由一个不带函数名的函数声明和函数体组成,如下所示:
匿名函数可以直接赋值给一个变量或者直接执行:
闭包
的是, Go语言支持随时在代码里定义匿名函数。
匿名函数由一个不带函数名的函数声明和函数体组成,如下所示:
func(a, b int, z float64) bool {
return a*b <int(z)
}匿名函数可以直接赋值给一个变量或者直接执行:
f := func(x, y int) int {
return x + y
}
func(ch chan int) {
ch <- ACK
} (reply_chan) // 花括号后直接跟参数列表表示函数调用闭包
闭包是可以包含自由(未绑定到特定对象)变量的代码块,这些变量不在这个代码块内或者
任何全局上下文中定义,而是在定义代码块的环境中定义。要执行的代码块(由于自由变量包含
在代码块中,所以这些自由变量以及它们引用的对象没有被释放)为自由变量提供绑定的计算环
境(作用域)。
任何全局上下文中定义,而是在定义代码块的环境中定义。要执行的代码块(由于自由变量包含
在代码块中,所以这些自由变量以及它们引用的对象没有被释放)为自由变量提供绑定的计算环
境(作用域)。
这是官方的说法,我的理解是:
简单说就一个封闭的func,它可以访问它外部声明的变量,但外部访问不了它内部声明的变量,除非它愿意提供一个句柄
Go语言中的闭包同样也会引用到函数外的变量。闭包的实现确保只要闭包还被使用,那么
被闭包引用的变量会一直存在, 如:
被闭包引用的变量会一直存在, 如:
package main
import (
"fmt"
)
func main() {
var j int = 5
a := func()( func()) {
var i int = 10
return func() {
fmt.Printf("i, j: %d, %d\n", i, j)
}
}()
a()
j *= 2
a()
}上述例子的执行结果是:
i, j: 10, 5 i, j: 10, 10
在上面的例子中,变量a指向的闭包函数引用了局部变量i和j , i的值被隔离,在闭包外不
能被修改,改变j 的值以后,再次调用a,发现结果是修改过的值。
在变量a指向的闭包函数中,只有内部的匿名函数才能访问变量i,而无法通过其他途径访问
到,因此保证了i的安全性。
有疑问加站长微信联系(非本文作者)
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
关注微信2753 次点击
添加一条新回复
(您需要 后才能回复 没有账号 ?)
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码` - 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传
收入到我管理的专栏 新建专栏
函数
本Go语言基础入门内容均来自于《Go语言编程》,个人记录,加深理解。
函数声明
函数的基本组成为:关键字func、函数名、参数列表、返回值、函数体和返回语句。
一般的: func 函数名 (传入参数) (返回参数) {函数体}
如:
package mymath
import "errors"
func Add(a int, b int) (ret int, err error) {
if a < 0 || b < 0 { // 假设这个函数只支持两个非负数字的加法
err= errors.New("Should be non-negative numbers!")
return
}
return a + b, nil // 支持多重返回值
}如果参数列表中若干个相邻的参数类型相同,可以省略前面变量的类型声明,如:
func Add(a, b int</span>)(ret int, err error) {
// ...
}如果返回值列表中多个返回值的类型相同,也可以用同样的方式合并。如果函数只有一个返回值,也可以这么写:
func Add(a, b int) int {
// ...
}函数调用
1、导入该函数所在包
2、如下调用
import "mymath"// 假设Add被放在一个叫 mymath的包中 // ... c := mymath.Add(1, 2)
值得注意的是:如果想在外部调用该包函数,所调用的函数名必须大写。
Go语言有着这样的规定:
小写字母开头的函数只在本包内可见,大写字母开头的函数才能被其他包使用。
这个规则也适用于类型和变量的可见性。
不定参数
这个规则也适用于类型和变量的可见性。
不定参数
不定参数是指函数传入的参数个数为不定数量。为了做到这点,首先需要将函数定义为接受不定参数类型:
func myfunc(args ... int) {//参数名 ...type
for _, arg := range args {
fmt.Println(arg)
}
}该函数可接收不定数量参数,如:myfunc(2, 3, 4) myfunc(1, 3, 7, 13)
从内部实现机理上来说,类型...type本质上是一个数组切片,也就是[]type,这也是为什么上面的参数args可以用for循环来获得每个传入的参数。
假如没有...type这样的语法糖,开发者将不得不这么写:
func myfunc2(args [] int) {
for _, arg := range args {
fmt.Println(arg)
}
}从函数的实现角度来看,这没有任何影响,该怎么写就怎么写。但从调用方来说,情形则完全不同:myfunc2([] int{1, 3, 7, 13})你会发现,我们不得不加上[]int{} 来构造一个数组切片实例。不定参数的传递
func myfunc(args ... int) {
// 按原样传递
myfunc3(args...)
// 传递片段,实际上任意的int slice都可以传进去
myfunc3(args[1:]...)
}
之前的例子中将不定参数类型约束为 int ,如果你希望传任意类型,可以指定类型为interface{} 。
下面是Go语言中fmt,Printf()函数原型:
func Printf(format string, args ... interface{}) {
// ...
}下面代码可以让我们清晰的知道传入参数的类型:package main
import "fmt"
func MyPrintf(args ... interface{}) {
for _, arg := range args {
switch arg. (type) {
case int:
fmt.Println(arg, "is an int value.")
case string:
fmt.Println(arg, "is a string value.")
case int64:
fmt.Println(arg, "is an int64 value.")
default:
fmt.Println(arg, "is an unknown type.")
}
}
}
func main() {
var v1 int = 1
var v2 int64 = 234
var v3 string = "hello"
var v4 float32 = 1.234
MyPrintf(v1, v2, v3, v4)
}该程序的输出结果为:
1 is an int value. 234 is an int64 value. hello is a string value. 1.234 is an unknown type.
匿名函数和闭包
在Go里面,函数可以像普通变量一样被传递或使用,这与C语言的回调函数比较类似。不同
的是, Go语言支持随时在代码里定义匿名函数。
匿名函数由一个不带函数名的函数声明和函数体组成,如下所示:
匿名函数可以直接赋值给一个变量或者直接执行:
闭包
的是, Go语言支持随时在代码里定义匿名函数。
匿名函数由一个不带函数名的函数声明和函数体组成,如下所示:
func(a, b int, z float64) bool {
return a*b <int(z)
}匿名函数可以直接赋值给一个变量或者直接执行:
f := func(x, y int) int {
return x + y
}
func(ch chan int) {
ch <- ACK
} (reply_chan) // 花括号后直接跟参数列表表示函数调用闭包
闭包是可以包含自由(未绑定到特定对象)变量的代码块,这些变量不在这个代码块内或者
任何全局上下文中定义,而是在定义代码块的环境中定义。要执行的代码块(由于自由变量包含
在代码块中,所以这些自由变量以及它们引用的对象没有被释放)为自由变量提供绑定的计算环
境(作用域)。
任何全局上下文中定义,而是在定义代码块的环境中定义。要执行的代码块(由于自由变量包含
在代码块中,所以这些自由变量以及它们引用的对象没有被释放)为自由变量提供绑定的计算环
境(作用域)。
这是官方的说法,我的理解是:
简单说就一个封闭的func,它可以访问它外部声明的变量,但外部访问不了它内部声明的变量,除非它愿意提供一个句柄
Go语言中的闭包同样也会引用到函数外的变量。闭包的实现确保只要闭包还被使用,那么
被闭包引用的变量会一直存在, 如:
被闭包引用的变量会一直存在, 如:
package main
import (
"fmt"
)
func main() {
var j int = 5
a := func()( func()) {
var i int = 10
return func() {
fmt.Printf("i, j: %d, %d\n", i, j)
}
}()
a()
j *= 2
a()
}上述例子的执行结果是:
i, j: 10, 5 i, j: 10, 10
在上面的例子中,变量a指向的闭包函数引用了局部变量i和j , i的值被隔离,在闭包外不
能被修改,改变j 的值以后,再次调用a,发现结果是修改过的值。
在变量a指向的闭包函数中,只有内部的匿名函数才能访问变量i,而无法通过其他途径访问
到,因此保证了i的安全性。