高阶函数编程:探索Go语言中的函数一等公民

简介: Go 函数一等公民。你是否听说过 `Go` 语言中的函数是一等公民?如果没有,那么恭喜你,本文将带你一起揭开这个神秘的面纱。如果你已经了解这个概念,你是否知道为什么 `Go` 语言中的函数被称为一等公民?不管你的答案是什么,通过阅读本文,你将对这个概念有更深入的了解。

作者:陈明勇
个人网站:https://chenmingyong.cn
文章持续更新,如果本文能让您有所收获,欢迎关注本号。
微信阅读可搜《Go 技术干货》。这篇文章已被收录于 GitHub https://github.com/chenmingyong0423/blog 欢迎大家 Star 催更并持续关注。

前言

你是否听说过 Go 语言中的函数是一等公民?如果没有,那么恭喜你,本文将带你一起揭开这个神秘的面纱。如果你已经了解这个概念,你是否知道为什么 Go 语言中的函数被称为一等公民?不管你的答案是什么,通过阅读本文,你将对这个概念有更深入的了解。

准备好了吗?准备一杯你最喜欢的咖啡或茶,随着本文一探究竟吧。

一等公民

In a given programming language design, a first-class citizen is an entity which supports all the operations generally available to other entities. These operations typically include being passed as an argument, returned from a function, and assigned to a variable.

什么是一等公民?上面引文来自维基百科,直译过来的意思是:“在给定的编程语言设计中,一等公民是指支持所有通常可用于其他实体的操作的实体。这些操作通常包括作为参数传递、从函数返回和赋值给变量。”。

直译过来似乎有点难以理解,没关系,我们可以用更简单的方式来理解:在编程语言设计中,被称为一等公民的元素可以自由地进行常见的操作,如作为参数传递、从函数返回和赋值给变量。

Go 语言中,函数具备这些特性,可以赋值给变量、作为参数传递,并且可以作为函数的返回值。
func 一等公民.jpg

函数作为一等公民的实际运用示例

当我们理解了 Go 语言中的函数为什么被视为一等公民之后,让我们来探索一下它作为一等公民的实际运用吧。

赋值给变量

Go 语言中,函数是一种类型,它可以像其他类型(如 int64string 等)一样被赋值给变量,这意味着我们可以创建一个变量,将函数赋值给它,然后通过该变量来调用函数。

将普通函数赋值给变量

我们可以将普通函数赋值给变量,以便通过变量调用该函数。下面是一个示例代码:

import (
    "fmt"
)

func SayHello(s string) {
   
   
    fmt.Println(s)
}

func main() {
   
   
    sayHelloFunc := SayHello
    sayHelloFunc("你好,我是陈明勇") // 你好,我是陈明勇
}

在上面的例子中,首先我们定义了一个普通函数 SayHello(s string),该函数接受一个字符串参数 s,并在函数体中使用 fmt.Println 函数打印字符串;

然后在 main 函数中,我们将该函数赋值给变量 sayHelloFunc,通过这个变量,我们可以调用 SayHello 函数,实现相同的功能。这种方式可以在需要动态选择函数的情况下使用,使得代码更加 灵活可复用

创建匿名函数并赋值给变量

除了将普通函数赋值给变量,我们还可以通过创建匿名函数的形式并将其赋值给变量。下面是一个示例代码:

import (
    "fmt"
)

func main() {
   
   
    sayHelloFunc := func(s string) {
   
   
        fmt.Println(s)
    }
    sayHelloFunc("你好,我是陈明勇") // 你好,我是陈明勇
}

在上述代码中,我们使用 func 关键字创建了一个匿名函数,该函数也是接受一个字符串参数 s,并在函数体中使用 fmt.Println 函数打印字符串;然后,我们将该匿名函数赋值给 sayHelloFunc 变量。通过 sayHelloFunc 变量,我们可以调用匿名函数并传入相应的参数,实现相同的功能。

匿名函数的创建方式灵活且简洁,特别适用于一次性的函数需求或需要在不同的上下文中定义函数的场景。

作为参数传递

Go 语言中,函数可以作为函数参数传递给其他函数,这使得函数可以更加灵活的操作和组合。我们来看看一个时间转换的例子;

import (
    "fmt"
    "time"
)

// ApplyFormatTimeToStringFunc 根据参数 t 和 operation,将时间类型转成对应格式的字符串类型,字符串的格式由参数 operation 决定
// 如果参数 t 为零值,则返回空字符串
func ApplyFormatTimeToStringFunc(t time.Time, operation func(t time.Time) string) string {
   
   
    if t.IsZero() {
   
   
        return ""
    }
    return operation(t)
}

// FormatTimeToString 将时间转成 yyyy-MM-dd 的形式
func FormatTimeToString(t time.Time) string {
   
   
    return t.Format(time.DateOnly)
}

// FormatDateTimeToString 将时间转成 yyyy-MM-dd HH:mm:ss 的形式
func FormatDateTimeToString(t time.Time) string {
   
   
    return t.Format(time.DateTime)
}

func main() {
   
   
    // yyyy-MM-dd
    formatTimeToString := ApplyFormatTimeToStringFunc(time.Now(), FormatTimeToString)
    fmt.Println(formatTimeToString) // 2023-07-18

    // yyyy-MM-dd HH:mm:ss
    formatDateTimeToString := ApplyFormatTimeToStringFunc(time.Now(), FormatDateTimeToString)
    fmt.Println(formatDateTimeToString) // 2023-07-18 00:00:00
}

在上述例子中,首先我们定义了一个 ApplyFormatTimeToStringFunc 函数,该函数接收一个时间类型参数 t 和一个函数类型参数 operation,根据参数 toperation,将时间类型转成字符串类型,字符串的格式由参数 operation 决定;

然后定义两个操作函数 FormatTimeToStringFormatDateTimeToString,这两个函数分别将时间转换为 yyyy-MM-ddyyyy-MM-dd HH:mm:ss 的格式;

最后在 main 函数中,我们通过将不同的操作函数作为参数传递给 ApplyFormatTimeToStringFunc 函数来格式化当前时间。通过使用函数作为参数传递给另一个函数,动态改变函数的行为,使得我们可以根据需要选择不同的格式化方式来处理时间,提高代码的灵活性和可复用性。

作为函数的返回值

Go 语言中,函数除了可以赋值给变量和作为参数进行传递以外,它还可以作为函数的返回值进行使用。以下是示例代码:

import (
    "fmt"
)

func CreateDialogueFormatter(name string) func(string) string {
   
   
    return func(s string) string {
   
   
        return fmt.Sprintf("[%s]: ", name) + s
    }
}

func main() {
   
   
    DialogueOfCmy := CreateDialogueFormatter("陈明勇")
    fmt.Println(DialogueOfCmy("你好")) // [陈明勇]: 你好

    DialogueOfGopher := CreateDialogueFormatter("Gopher")
    fmt.Println(DialogueOfGopher("你好")) // [Gopher]: 你好
}

在上面的例子中,首先我们定义了 CreateDialogueFormatter 函数,该函数接收一个 name 参数,用于设置对话人昵称,并返回一个可定制化的对话函数;

然后在 main 函数中,通过调用 CreateDialogueFormatter 函数并传入不同的昵称,可以创建多个针对不同对话人的对话函数。

通过将函数作为返回值,我们可以在运行时动态地生成函数,从而使函数更具灵活性和可定制性。

小结

函数作为一等公民在 Go 语言中非常重要,借助其三大特性,我们能够实现高阶函数编程,提升代码的灵活性和可复用性。

目录
相关文章
|
8天前
|
存储 监控 算法
员工上网行为监控中的Go语言算法:布隆过滤器的应用
在信息化高速发展的时代,企业上网行为监管至关重要。布隆过滤器作为一种高效、节省空间的概率性数据结构,适用于大规模URL查询与匹配,是实现精准上网行为管理的理想选择。本文探讨了布隆过滤器的原理及其优缺点,并展示了如何使用Go语言实现该算法,以提升企业网络管理效率和安全性。尽管存在误报等局限性,但合理配置下,布隆过滤器为企业提供了经济有效的解决方案。
44 8
员工上网行为监控中的Go语言算法:布隆过滤器的应用
|
28天前
|
存储 Go 索引
go语言中数组和切片
go语言中数组和切片
39 7
|
28天前
|
Go 开发工具
百炼-千问模型通过openai接口构建assistant 等 go语言
由于阿里百炼平台通义千问大模型没有完善的go语言兼容openapi示例,并且官方答复assistant是不兼容openapi sdk的。 实际使用中发现是能够支持的,所以自己写了一个demo test示例,给大家做一个参考。
|
28天前
|
程序员 Go
go语言中结构体(Struct)
go语言中结构体(Struct)
100 71
|
27天前
|
存储 Go 索引
go语言中的数组(Array)
go语言中的数组(Array)
104 67
|
30天前
|
Go 索引
go语言for遍历数组或切片
go语言for遍历数组或切片
98 62
|
1月前
|
并行计算 安全 Go
Go语言中的并发编程:掌握goroutines和channels####
本文深入探讨了Go语言中并发编程的核心概念——goroutine和channel。不同于传统的线程模型,Go通过轻量级的goroutine和通信机制channel,实现了高效的并发处理。我们将从基础概念开始,逐步深入到实际应用案例,揭示如何在Go语言中优雅地实现并发控制和数据同步。 ####
|
3天前
|
算法 安全 Go
Go 语言中实现 RSA 加解密、签名验证算法
随着互联网的发展,安全需求日益增长。非对称加密算法RSA成为密码学中的重要代表。本文介绍如何使用Go语言和[forgoer/openssl](https://github.com/forgoer/openssl)库简化RSA加解密操作,包括秘钥生成、加解密及签名验证。该库还支持AES、DES等常用算法,安装简便,代码示例清晰易懂。
28 16
|
28天前
|
存储 Go
go语言中映射
go语言中映射
36 11
|
30天前
|
Go
go语言for遍历映射(map)
go语言for遍历映射(map)
35 12