#Golang# Golang与编程范式之函数式编程

#Golang# Golang与编程范式之函数式编程

介绍 Golang 中的函数式思想实践,主要讨论以下三个部分

  • 函数基础概念
  • 闭包
  • 装饰器模式

目录 Table of Contents


简介

Golang 的起源受诸多早期编程语言的影响。类 CGolang 本质上更倾向于是一门面向过程的语言,同时Golang 也借鉴了 Alef 来设计 Golang函数式编程特性,融合 CSP 中使用管道进行通信和控制同步的思想则很好地体现了如何面向消息编程。

Golang 不是一门纯函数式的编程语言,但是函数在 Golang 中是“第一公民”,表现在于:函数支持参数值和返回值;函数作为一种类型,它的变量、常量和字面量既可以被存储也可以被调用;函数支持闭包功能。下面我们就来对这些话题作进一步的探讨吧~

PS:本文 just 一点自己的见解,学识有限难免有误,也希望可以抛砖引玉,欢迎大家的勘误和讨论╰( ̄ω ̄o)

函数基础概念


函数定义一般包括关键字、函数名、参数值和返回值四个部分,注意区分函数类型和函数的区别。函数使用可以进行赋值变量和直接调用,注意区分函数变量、常量和字面量的区别。

函数定义

1
2
3
4
5
6
7
8
9
10
11
12
// 定义函数类型
type FuncType func(params paramsType) returnType

// 定义命名函数
func Func(params paramsType) returnType {
// ...
}

// 定义匿名函数
func (params paramsType) returnType {
// ...
}
  • 关键字:定义函数类型使用 type,定义命名函数或匿名函数使用 func
  • 函数名:大写时包外可见,小写时包内可见
  • 参数值:支持顺序参数,不支持关键字参数;支持不定参数,不支持默认值参数;有简化的写法
  • 返回值:支持多值返回;支持有名返回;有简化的写法

PS:Golang 函数支持不同的接收器所绑定的函数同名,不支持传统的函数重载;支持嵌套匿名函数赋值,不支持嵌套命名函数定义。

函数使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 函数变量定义
var fn FuncType

// 赋值命名函数
fn = Func

// 赋值匿名函数
fn = func (params paramsType) returnType {
// ...
}

// 函数变量作为参数值
func paramsFunc(fn FuncType) {
// ...
}

// 函数变量作为返回值
func returnFunc() (fn FuncType) {
// ...
}

// 函数变量调用
res := fn(req)

// 命名函数调用
res := Func(req)

// 匿名函数调用
res := func (params paramsType) returnType {
// ...
}(req)
  • 函数变量:属于引用类型
  • 函数常量:命名函数的函数名
  • 函数字面量:匿名函数的整体

PS:函数变量和函数常量都可以当作指针变量,该指针指向函数代码的开始位置。

闭包


闭包是由函数及其相关引用环境组合而成的实体,即闭包 = 函数 + 引用环境,一般通过在匿名函数中引用外部的局部变量或全局变量构成。

引用局部变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
func genSumFunc() (func(int) int, func(int) int) {
var sum int

add := func(num int) int {
sum = sum + num
return sum
}

sub := func(num int) int {
sum = sum - num
return sum
}

return add, sub
}

func main() {
f1, f2 := genSumFunc()
g1, g2 := genSumFunc()

var res int
res = f1(1) // 1
res = f1(1) // 2
res = f2(1) // 1
res = g1(1) // 1
res = g1(1) // 2
res = g2(1) // 1
}
  • 引用外部 sum 局部变量,传入外部 num 局部变量,实现累加或累减。
  • 多次调用普通函数 genSumFunc,引用外部 sum 局部变量创建副本
  • 多次调用不同的闭包函数 f1f2 ,引用外部 sum 局部变量共享引用
  • 多次调用同一个闭包函数 f1g1 ,引用外部 sum 局部变量共享引用

引用全局变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var sum int
func genSumFunc() (func(int) int, func(int) int) {
add := func(num int) int {
sum = sum + num
return sum
}

sub := func(num int) int {
sum = sum - num
return sum
}

return add, sub
}

func main() {
f1, f2 := genSumFunc()
g1, g2 := genSumFunc()

var res int
res = f1(1) // 1
res = f1(1) // 2
res = f2(1) // 1
res = g1(1) // 2
res = g1(1) // 3
res = g2(1) // 2
}
  • 引用外部 sum 全局变量,传入外部 num 局部变量,实现累加或累减。
  • 多次调用普通函数 genSumFunc,引用外部 sum 全局变量共享引用
  • 多次调用不同的闭包函数 f1f2 ,引用外部 sum 全局变量共享引用
  • 多次调用同一个闭包函数 f1g1 ,引用外部 sum 全局变量共享引用

一些说明

  • 对象是附有行为的数据,类在定义时已经显式地定义了行为。
  • 闭包是附有数据的行为,闭包中的数据没有显式地集中声明。

PS:闭包的目的是用共享局部变量来减少全局变量,代价是不够清晰和不够直接。

装饰器模式


类继承 (Class Inheritance) 在编译时增加行为,装饰器模式 (Decorator Pattern) 在运行时增加行为。装饰器的实现和闭包是分不开的。

Python 装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def some_decorator_without_params(fn):
@functools.wraps(fn)
def wrapper(*args, **kwargs):
# do something before function

fn(*args, **kwargs)

# do something after function
return wrapper

def some_decorator_with_params(params):
def decorator(fn):
@functools.wraps(fn)
def wrapper(*args, **kwargs):
# do something before function using params

fn(*args, **kwargs)

# do something after function using params
return wrapper
return decorator

@some_decorator_without_params
@some_decorator_with_params(params)
def HelloWorld(who, when="now"):
print("Hello World to %s at %s!" % (who, when))
  • 闭包 (some_decorator) = 函数 (wrapper) + 引用环境 (fn)
  • 装饰器 → 中间件 = @ 语法糖 + 洋葱顺序

Golang 装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
type Decorator func(http.HandlerFunc) http.HandlerFunc

func SomeDecorator(h http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// do something before handler

h(w,r)

// do something after handler
}
}

func Middlewares(h http.HandlerFunc, decos ...Decorator) http.HandlerFunc {
for deco := range decos {
d := decos[len(decos)-1-deco]
h = d(h)
}
return h
}

func HelloWorldHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf("Hello World!")
return
}

func main() {
closure := SomeDecorator(HelloWorldHandler)
}
  • 闭包 (closure) = 函数 (anonymous func) + 引用环境 (h)
  • 装饰器 → 中间件 = 闭包集合 + 洋葱顺序

参考链接

致谢

实际上再写这篇博客时,小王与我的关系已经发生微妙的变化。如你所知,世界是物质的,物质是运动的,人也难免发生变化,我们就是在这样那样的变化中忘掉自我而寻找无双的。有小王的一路陪伴,我当然要说我庆幸,并且希望可以一直幸运下去 :)


Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×