浅谈函数式编程

这段时间准备了一个公司内部的技术分享,也不知道讲些什么,浏览一些技术资讯的时候看到函数式编程近来很火的样子,所以决定分享一下这方面的东东。我觉得我这个分享充其量就是个抛砖引玉的作用。希望为看到这篇博客的朋友有个思路上的启发,进而自己去深入的去研究究竟什么是函数式编程。
说到函数式编程还是要追溯到去年参加的swift开发者大会。那时候函数式编程才走入我的视线。
函数式编程是什么?我们先看一个例子,让我们有个大体的认识。

// 非函数式的例子:
int count;
void increment(){
    count++;
}
// 函数式的写法:
int increment(int count){
    return count+1;
}

大家可能会特别疑惑,上面的方法和下面的方法有什么不一样吗?是的不一样,不一样在哪呢,等看完了下面对于函数式编程的解释你应该就能明白了。那么什么是函数式编程呢,我特意在wiki上搜索了一下,得到了下面的解释

Functional programming is a programming paradigm
- treats computation as the evaluation of mathematical functions
- avoids changing-state and mutable data

函数式编程是编程范式中的一种,是一种典型的编程思想和方法。他是以计算来评估价值的并且避免状态和数据的改变。
那我们为什么要学习函数式编程呢?因为函数式编程是为了更好的模块化我们的代码。那么模块化给我带来了哪些好处呢?我想大致会有下面这三点好处:
- 模块化使得开发更快、维护更容易
- 模块可以重用
- 模块化便于单元测试和debug
相信这是我们每个程序猿一直在不断追求的终极目标。而函数式编程呢恰恰是以函数为核心来组织模块的一套编程方法。

函数式编程的主张
- 函数是第一等公民
- 纯函数

函数式编程将函数作为第一等公民。既然函数式编程的基本理念是以函数为核心来组织代码,很自然的,它首先将函数的地位提高,视其为“第一等公民” (first class)。所谓“第一等公民”,是指函数和其他数据类型拥有平等的地位,可以赋值给变量,也可以作为参数传入另一个函数,或者作为别的函数的返回值。相当于它支持了高阶函数。
为什么将函数视作“第一等公民”有利于写出模块化的代码?看看下面的例子:

有数组numberList = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],编写程序完成以下目标:
- 1.1 将numberList中的每个元素加1得到一个新的数组
- 1.2 将numberList中的每个元素乘2得到一个新的数组
- 1.3 将numberList中的每个元素模3得到一个新的数组

// 函数不是“第一等公民”情况下的代码:
# 1.1 将numberList中的每个元素加1得到一个新的数组
newList = []
for num in numberList:
    newList.append(num + 1)
# 1.2 将numberList中的每个元素乘2得到一个新的数组
newList = []
for num in numberList:
    newList.append(num * 2)
# 1.3 将numberList中的每个元素模3得到一个新的数组
newList = []
for num in numberList:
    newList.append(num % 3)

// 我们将可复用的代码抽取出来编写成高阶函数map
# 高阶函数`map`
# 该函数接受一个函数和一个数组作为输入,函数体中将这个函数作用于数组的每个元素然后作为返回值返回

def map(mappingFuction, numberList):
    newList = []
    for num in numberList:
        newList.append(mappingFuction(num))

# 1.1 将numberList中的每个元素加1得到一个新的数组
map(lambda x: x + 1, numberList)

# 1.2 将numberList中的每个元素乘2得到一个新的数组
map(lambda x: x * 2, numberList)

# 1.3 将numberList中的每个元素模3得到一个新的数组
map(lambda x: x % 3, numberList)

那么纯函数又是什么意思呢?我又wiki了一下:

Pure function
- The function always evaluates the same result value given the same argument value(s).
- Evaluation of the result does not cause any semantically observable side effect or output, such as mutation of mutable objects or output to I/O devices
由纯函数的两点条件可以看出,纯函数是相对独立的程序构件。因为函数的结果只依赖于输入的参数且与外部系统状态无关,使得单元测试和debug变得异常容易,而这也正是模块化的优点之一。

函数式编程带来了什么?

1 . 可以利用Memoization技术提升性能。
满足任意两次调用只要输入相同,其结果总是不变的特点。于是可 以将第一次的计算结果缓存起来,遇到下一次执行时直接替换,依然能保证程序的正确性。这种优化方法称为Memoization。我的理解是这样的,从纯函数的特性里面可以了解到只要入参是一样的,那么输出的结果就是相同的,如果我们在计算一些特别大型的数据,可能需要两三天才能有结果,那么我们保存一些入参和相应的结果是不是就省去了这两三天的时间呢。

2 . 延迟求值 ( Lazy Evaluation )
trace函数在debugFlag等于True时会将debug信息打印到标准输出。
def trace(debugFlag, debugStr):
if debug == True:
print debugStr
在实际使用中debug信息可能会由几部分拼接而成,如下:
trace(debugFlag, ‘str1’ + ‘str2’ + ‘str3’)
这样调用的话,按照正常的逻辑会是先将 ‘str1’ + ‘str2’ + ‘str3’的结果求出来,然后再去判断debug是否为true。然而函数式编程呢,他是先去判断debug是否为true,如果是才会计算’str1’ + ‘str2’ + ‘str3’。是不是优点意思,这样可以省去很多资源和时间在一些特别耗费资源的方法调用上。

3 . 可以使用 currying 技术进行函数封装
curryiny 将接受多个参数的函数变换成接受其中部分参数,并且返回接受余下参数的新函数。(维基百科中的定义是接受一个单一参数的新函数,然而现实中currying技术的涵义被延伸了。)

那回到最初那个方法为什么
int increment(int count){
return count+1;
}
就是函数式编程,回想一下上面我们说的,以为它不会改变参数的值,而去也只要入参一样,返回结果始终是相同的。

这个就是我对于函数式编程的一些粗浅的理解,在这里我也参考过一些其它的技术博客和帖子,引用了一些他们的例子结合自己的理解写下了这篇博客。希望对读者有些许的启发和帮助。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值