Go功能动物园

有关以下内容的概述:匿名,高阶,闭包,并发,延迟和其他类型的Golang函数。
这篇文章是Go中不同类型的func的摘要。 我将在以后的文章中更详细地介绍它们,因为它们应该得到更多。 这只是一个开始。

什么是功能?

函数是一个单独的可重复使用的代码块,可以一次又一次地运行。 函数可以接受输入值,也可以返回输出值。

我们为什么需要功能?

  • 提高可读性,可测试性和可维护性
  • 使代码的某些部分可单独执行
  • 从较小的事物组成事物
  • 为类型添加行为
  • 组织代码
  • 干燥

命名为Funcs

命名的func具有名称,并在包级别声明- 在另一个func的主体之外

👉我在 这里的 另一篇文章中详细解释了它们

这是一个命名的func:Len func接收一个字符串并返回并返回int

可变参数功能

可变参数函数接受可变数量的输入值-零个或多个。 输入类型前面的省略号(三点)前缀使函数可变。

声明一个带有可变输入参数的可变参数,该输入参数的字符串名为“ names”。

让我们创建一个Logger,在运行时可以使用选项模式更改详细程度和前缀选项:

type Logger struct { verbosity prefix string }

SetOptions将选项应用于记录器,以使用可变参数选项参数更改其行为:

func (lo * Logger ) SetOptions(opts ...option ) { for _, applyOptTo := range opts { applyOptTo(lo) } }

让我们创建一些函数,这些函数返回选项func作为关闭以更改Logger行为的结果:

func HighVerbosity() option { return func (lo * Logger ) { lo.verbosity = High } } func HighVerbosity() option { return func (lo * Logger ) { lo.verbosity = High } } func Prefix(s string ) option { return func(lo * Logger ) { lo.prefix = s } }

现在,让我们使用默认选项创建一个新的Logger:

logger := & Logger {}

然后通过可变参数为记录器提供选项:

logger. SetOptions ( HighVerbosity(), Prefix("ZOMBIE CONTROL"), )

现在让我们检查输出:

logger.Critical("zombie outbreak!") // [ZOMBIE CONTROL] CRITICAL: zombie outbreak! logger.Info("1 second passed") // [ZOMBIE CONTROL] INFO: 1 second passed
请参阅工作代码及其内部说明

👉要了解更多有关它们的信息,请 在此处 查看我关于可变参数功能的文章

方法

将功能附加到类型时,功能将成为该类型的方法 因此,可以通过该类型调用它。 Go在调用时会将类型( 接收者)传递给方法。

创建一个新的计数器类型并为其添加方法:

type Count int func (c Count) Incr() int { c = c + 1 return int (c) }

上面的方法与此功能类似:

func Incr(c Count) int
并非完全正确,但您可以想到上述方法
价值接收者

Count实例的值将被复制并在调用时传递给方法。

var c Count; c.Incr(); c.Incr() var c Count; c.Incr(); c.Incr() // output: 1 1
它不会增加,因为“ c”是一个价值接收者。
指针接收器

为了增加计数器的值,你需要进行增量FUNC连接 计数指针类型 - *计数。

func (c *Count) Incr() int { *c = *c + 1 return int(*c) } var c Count c.Incr(); c.Incr() c.Incr(); c.Incr() // output: 1 2
我以前的帖子中还有更多示例: 在这里这里

接口方式

让我们使用接口方法重新创建上述程序。 让我们创建一个名为Counter的新接口:

type Counter interface { Incr() int }

下面的onApiHit函数可以使用具有Incr()int方法的任何类型:

func onApiHit(c Counter) { c.Incr() }

现在就使用我们的虚拟计数器- 您也可以使用真实的api计数器

dummyCounter := Count(0) onApiHit(&dummyCounter) // dummyCounter = 1

因为Count类型在其方法列表中具有Incr()int方法,所以onApiHit函数可以使用它来增加计数器—我将dummyCounter的指针传递给onApiHit,否则它不会增加计数器。

接口方法与普通方法之间的区别在于,接口更加灵活且耦合松散。 您可以跨软件包切换到不同的实现,而无需更改onApiHit等内部的任何代码。

一流的功能

一流意味着函子是值对象,就像可以存储和传递的任何其他值一样。

Funcs可以与其他类型一起用作值,反之亦然
例:

此处的示例程序通过使用一片Crunchers作为名为“ crunch ”的函数的输入参数值来处理数字序列。

声明一个新的“ 用户定义的函数类型 ”,该类型需要一个int并返回一个int。

这意味着使用此类型的任何代码都将接受具有以下确切 签名 的func

type Cruncher func ( int ) int

声明一些紧缩功能:

func mul(n int ) int { return n * 2 } func add(n int ) int { return n + 100 } func sub(n int ) int { return n - 1 }

紧缩FUNC处理的一系列使用整数的可变参数 排排坐funcs中

func crunch(nums [] int , a ...Cruncher) (rnums [] int ) { // create an identical slice rnums = append(rnums, nums...) for _, f := range a { for i, n := range rnums { rnums[i] = f(n) } } // create an identical slice rnums = append(rnums, nums...) for _, f := range a { for i, n := range rnums { rnums[i] = f(n) } } return }

用一些数字声明一个int切片并处理它们:

nums := [] int {1, 2, 3, 4, 5} crunch(nums, mul, add, sub)
输出:
[101 103 105 107 109]

匿名功能

一个NONAME FUNC是匿名FUNC和它的声明为inline 使用 函数文本 。 当它用作闭包,高阶函数,延迟函数等时,它将变得更加有用。

签名

一个命名的func:

func Bang(energy int ) time.Duration

匿名函数:

func (energy int ) time.Duration

它们都具有相同的签名,因此可以互换使用:

func ( int ) time.Duration

让我们使用匿名函数从上面的First-Class Funcs部分重新创建Cruncher程序。 在主功能内部将处理程序声明为匿名功能。

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 }) }

之所以可行,是因为紧缩功能仅需要Cruncher功能类型,而不管它们是命名功能还是匿名功能。

为了提高可读性,您还可以在传递给紧缩功能之前将它们分配给变量:

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 func是高阶函数。 结果返回一个分词器功能类型

关闭

闭包可以记住定义它的所有周围的值。 封闭的好处之一是,只要您愿意,它就可以在捕获的环境中运行-当心泄漏!

声明一个新的func类型,该类型返回分割后的字符串中的下一个单词:

type tokenizer func () (token string , ok bool )

下面的split func是一个高阶函数 ,它通过分隔符分割字符串并返回一个闭包 ,该闭包使遍历分割后的字符串的单词成为可能。 返回的闭包可以使用周围的变量:“令牌”和“最后一个”。

我们试试吧:
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 func将句子拆分为单词,然后获得一个新的iter func作为结果值并将其放入iter变量中。
  • 然后,我们开始一个无限循环,该循环仅在iter函数返回false时才终止。
  • 每次调用iter函数都将返回下一个单词。
结果:
The quick brown fox jumps over the lazy dog
同样,里面有更多解释。

延迟功能

延迟的功能仅在其父功能返回后才执行。 也可以使用多个延迟器,它们以一个堆栈一个一个地运行。

Go运行时会在注册延迟时( 而不是在运行时)将所有传递的参数保存到延迟功能中。

声明一个注册了延迟关闭的伪函数。 它还使用命名结果值“ n”来第二次增加传递的数字

func count(i int ) (n int ) { defer func () { n = n + i }(i) i = i * 2 n = i return }
我们试试吧:
count(10) // output: 30
发生了什么?
根据数字(在左侧)解析视觉效果:1,2,3。

在某些情况下 ,如示例所示, defer可以通过使用 命名结果值 来帮助您在返回之前更改结果值

👉要了解有关它们的更多信息,请 在此处 查看我有关Go defers的帖子

并发函数

go func()与其他goroutines同时运行传递的func。

goroutine是一种较轻的线程机制,可让您有效地构造并发程序。 主函数在main-goroutine中执行。

在这里,“开始”匿名函数成为并发函数,当使用“ go”关键字调用时,该函数不会阻止其父函数的执行:

start := func () { time.Sleep(2 * time.Second) fmt.Println( "concurrent func: ends" ) } go start() fmt.Println(" main: continues... ") time.Sleep(5 * time.Second) fmt.Println(" main: ends ")
输出量
main: continues... concurrent func: ends main: ends

如果主函数中没有睡眠调用,则主函数将终止,而无需等待并发函数完成:

main: continues... main: ends

其他种类

递归函数

您可以像在其他任何lang中一样使用递归函数,在Go中没有实际的实际区别。 但是,您一定不要忘记每个调用都会创建一个新的调用堆栈 。 但是,在Go中,堆栈是动态的,它们可以根据func的需求而收缩和增长。 如果您可以解决问题而无需递归,请改用该方法。

黑洞功能

黑洞函数可以定义多次,并且不能以通常的方式调用它们。 它们有时对测试解析器很有用:请参阅this

func _() {} func _() {}
内联函数

Go链接器将func放入可执行文件中,以便以后在运行时调用它。 与直接执行代码相比,有时调用函数是一项昂贵的操作。 因此,编译器将func的主体注入调用程序。 要了解更多关于他们:阅读这个这个这个 还有这个

外部功能

如果您省略函子的主体而仅声明其签名,则链接程序将尝试在可能已在其他地方编写的外部函子中找到它。 例如, 此处仅使用签名声明Atan函数 ,然后在此处实现

with与您的朋友分享这篇文章。 谢谢! 💓
我也在为Go创建在线课程→ 加入我的新闻
“让我们每周保持联系以获取新的教程和技巧”
阅读更多:

最初于 2017 年11月9日 发布在 blog.learngoprogramming.com 上。

From: https://hackernoon.com/the-zoo-of-go-functions-8f6458f51ed1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值