本文是对匿名函数、高阶函数、闭包、同步、延时及其他 Goalng 函数类型或特性的概览。
这篇文章是针对 Go 语言中不同的函数类型或特性的摘要总结。
更为深入的探讨我会在近期的文章中进行,因为那需要更多的篇幅。这只是一个开端。
命名函数
一个命名函数拥有一个函数名,并且要声明在包级作用域中——其他函数的外部
我已经在另一篇文章中对它们进行了完整的介绍
可变参数函数
变参函数可接受任意数量的参数
我已经在另一篇文章中对它们进行了完整的介绍
方法
当你将一个函数附加到某个类型时,这个函数就成为了该类型上的一个方法。因此,它可以通过这个类型来调用。在通过类型来调用其上的某个方法时,Go 语言会将该类型(接收者)传递给方法。
示例
新建一个计数器类型并为其定义一个方法:
func (c Count) Incr() int {
func Incr(c Count) int
如上的方法与以下写法有同样的效果(但并不等价):
func Incr(c Count) int
值传递
当 Incr 被调用时,Count 实例的值会被复制一份并传递给 Incr。
var c Count; c.Incr(); c.Incr()
// output: 1 1
c 的值并不会增加,因为 c 是通过值传递的方式传递给方法
指针传递(引用传递)
想要改变计数器 c 的值,你需要给 Incr 方法传入 Count 类型指针——*Count。
func (c *Count) Incr() int {
*c = *c + 1
return int(*c)
}
var c Count
c.Incr(); c.Incur()
// output: 1 2
在我之前的一些文章中有更多的示例:看这里!看这里!
接口方法
Counter
type Counter interface {
Incr() int
}
下面的 onApiHit 函数能使用任何拥有 Incr() int 方法的类型:
func onApiHit(c Counter) {
c.Incr()
}
我们即刻使用一下这个改造版的计数器——现在你可以使用一个名副其实的计数器接口了:
dummyCounter := Count(0)
onApiHit(&dummyCounter)
// dummyCounter = 1
Incr() intIncr() int
func onApiHit(c Counter) {
@@ -138,13 +122,11 @@ onApiHit(&dummyCounter)
// dummyCounter = 1
Incr() intonApiHit()Incr() intonApiHit()
接口方法与普通方法的区别在于接口方法更具伸缩性、可扩展性,并且它是松耦合的。你可以利用接口方法在不同的包之间进行各自所需的实现,而不用修改 onApiHit 或是是其他方法的代码
函数是一等公民
一等公民意味着 Go 语言中函数也是一种值类型,可以像其他类型的值一样被存储或是传递。
示例
Crunchers”crunch“Crunchers”crunch“
intint
type Cruncher func(int) int
cruncher
func mul(n int) int {
return n * 2
}
func add(n int) int {
return n + 100
}
func sub(n int) int {
return n - 1
}
CrunchCruncher
func crunch(nums []int, a ...Cruncher) (rnums []int) {
// 创建一个等价的切片
rnums = append(rnums, nums...)
for _, f := range a {
for i, n := range rnums {
rnums[i] = f(n)
}
}
return
}
声明一个具有一些初始值的整型切片,之后对它们进行处理:
nums := []int{1, 2, 3, 4, 5}
crunch(nums, mul, add, sub)
输出:
[101 103 105 107 109]
匿名函数
匿名函数即没有名字的函数,它以函数字面量的方式在行内进行声明。它在实现闭包、高阶函数、延时函数等特殊函数时有极大作用。
函数签名
命名函数:
func Bang(energy int) time.Duration
匿名函数:
func(energy int) time.Duration
它们有相同的函数签名形式,所以它们可以互换着使用:
func(int) time.Duration
示例
crunchermaincruncher
func main() {
crunch(nums,
func(n int) int {
return n * 2
},
func(n int) int {
return n + 100
},
func(n int) int {
return n - 1
})
}
crunchCruncher
crunch
mul := func(n int) int {
return n * 2
}
add := func(n int) int {
return n + 100
}
sub := func(n int) int {
return n - 1
}
crunch(nums, mul, add, sub)
高阶函数
高阶函数可以接收或返回一个甚至多个函数。本质上来来讲,它用其他函数来完成工作。
下面闭包单元中的 split 函数就是一个高阶函数。它的返回结果是一个 tokenizer 类型的函数。
crunch(nums, mul, add, sub)
type tokenizer func() (token string, ok bool)
下面的 split 函数是一个高阶函数,它根据指定的分割符来分割一个字符串,然后返回一个可以遍历这个被分割的字符串中所有单词的闭包。这个闭包可以使用”token“和”last“两个在其捕获的环境下定义的变量。 下面的 split 函数是一个高阶函数,它根据指定的分割符来分割一个字符串,然后返回一个可以遍历这个被分割的字符串中所有单词的闭包。这个闭包可以使用 ”token“ 和 ”last“ 两个在其捕获的环境下定义的变量。
小试牛刀:
const sentence = "The quick brown fox jumps over the lazy dog"
iter := split(sentence, " ")
for {
token, ok := iter()
if !ok { break }
fmt.Println(token)
}
- 在这里,我们使用了 split 函数将一句话分割成了若干个单词,然后用得到了一个迭代器函数,并将它赋值给 iter 变量
- 在这里,我们使用了 split 函数将一句话分割成了若干个单词,然后得到了一个迭代器函数,并将它赋值给 iter 变量
- 然后,我开始了一个当 iter 函数返回 false 的时候才停止的无限循环
- 每次调用 iter 都能返回下一个单词
结果:
The
quick
brown
fox
jumps
over
the
lazy
dog
再次提示,这里面有更详细的描述哦~
延时函数
延时函数 (defer funcs)
延时函数只在其父函数返回时被调用。多个延时函数会以栈的形式一个接一个被调用。
我在另一篇文章中对延时函数有详细介绍
并发函数
go func()go func()
goroutine 是一种轻量级的线程机制,它能使你方便快捷的安排并发体系。其中,main 函数在 main-goroutine 中执行。
示例
这里,“start”匿名函数通过“go”关键字进行调用,不会阻塞父函数的执行: 这里,“start” 匿名函数通过 “go” 关键字进行调用,不会阻塞父函数的执行:
start := func() {
@@ -408,18 +380,16 @@ concurrent func: ends
main: ends
如果并发函数没有调起睡眠状态,那么main 函数不会它们的执行结束。
如果 main 函数中没有睡眠等阻塞调用,那么,main 函数会终止,而不会等待并发函数执行完。
main: continues...
main: ends
你能在任意一门语言中使用递归函数,Go 语言中的递归函数实现与它们也没有本质上的区别。然而,你可别忘了每一次的函数调用通常都会创建一个调用栈。但在 Go 中,栈是动态的,它们能根据相应函数的需要进行增减。如果你可以不使用递归解决手上的问题,那最好。
黑洞函数
黑洞函数能被多次定义,并且不能用通常的方式进行调用。它们在测试解析器的时候有时会非常有用:看这里
func _() {}
func _() {}
内联函数
行内函数
Go 语言的连接器会在运行时将函数置于可执行环境以方便后面的调用。相比于直接执行代码,有时候调用函数所需开销会小一些。因此,编译器将函数体直接注入到调用器中。 Go 语言的链接器会将函数放置到可执行环境中,以便稍后在运行时调用它。与直接执行代码相比,有时调用函数是一项昂贵的操作。所以,编译器将函数的主体注入调用者函数中。
更多的相关资料请参阅:这里、这里、这里和这里。
外部函数
如果你省略掉函数体,仅仅进行函数声明,连接器会尝试在任何可能的地方找到这个外部函数。例如:Atan Func在这里只进行了声明,而后在这里进行了实现。 @@ -460,6 +424,6 @@ via: https://blog.learngoprogramming.com/go-functions-overview-anonymous-closure
校对:rxcai、polaris1119
本文由 GCTT 原创编译,Go 中文网 荣誉推出
想看更多golang特讯请关注微信公众号: