关于函数式编程的思考(2)

1,780

作者:李英杰,美团金融前端团队成员。欢迎大家一起来探讨FP

题外话:只是单纯地谈谈个人对函数式编程的理解,欢迎大家来一起探讨。也不会提及高阶函数与范畴学的内容,只聊一些很入门的问题。函数式编程的优点这里也不做过多说明,会推荐大家看几篇文章,里面有很好的阐述。

斜体灰字部分是一些个人的吐槽和私货

目录

  1. 为什么函数式编程在前端复兴?

  2. 什么是函数式编程?

  3. 函数式编程如何思考问题?

  4. 函数式编程与面向对象编程有什么区别,各自的优缺点是什么?

  5. map,reduce是函数式编程吗?

  6. 推荐


书接上文

4.用这道很简单的应用题,我们来尝试对比一下函数式编程与面向对象编程的区别。

其实这个应用题给了一些暗示,让大家不知不觉走到了“陷阱”中

  • 标明这是应用题,引导大家用数学的思维去思考
  • 给出的已知条件,进一步引导大家用四则运算去思考

把这两个暗示去掉,我们再来看一下这个问题
小雨带着n个苹果出去玩,路过薛大叔家,薛大叔给了她n个苹果,薛大婶给了她1个苹果。
路过张砖头家,小雨给了张砖头n个苹果
小雨现在有多少个苹果?

用面向对象的思维该如何解决这个问题?
第一步 抽象个类
第二步 添加属性和方法

class Person {
  constructor(name, apples) {
    this.name = name;
    this.apples = apples;
  }

  receive(num) {
    this.apples += num;
  }

  give(num) {
    this.apples -= num;
  }

  getApples() {
    console.log(this.apples);
  }
}

let n = 10;
let xiaoYu = new Person('小雨', n);
xiaoYu.receive(n);// 薛大叔给了n个
xiaoYu.receive(1);// 薛大婶给了n个
xiaoYu.give(n);// 给了张砖头n个
let result = xiaoYu.getApples();// 答案

对比一下刚刚函数式思维的代码

let n = 10;
// 加法
function add(x, y) {
  return x + y;
}
// 减法
function sub(x, y) {
  return x - y;
}
let result = add(n, 1);// 答案

首先说说函数式思维代码的优点

  • 代码更加简洁,行数更少
  • 执行更加高效
  • 测试更加简单。如果我们要测试送苹果这个动作,只需要测试sub这个函数。但是对于面向对象思维的代码,我们需要new一个对象,初始化name与apples,运行give方法,验证getApples得到的结果是否正确。
    看一个吐槽:“面向对象编程语言的问题在于,它总是附带着所有它需要的隐含环境。你想要一个香蕉,但得到的却是一个大猩猩拿着香蕉,而其还有整个丛林。” — Joe Armstrong(Erlang语言发明人)

再说一说函数式思维代码的缺点

  • 直接给了结果add(n, 1),如果不知道你的推导过程,根本不知道你究竟在搞什么。
  • 如果不写出你的思路,完全不知道这个题目跟add与sub函数有什么关系

从以上分析的优缺点,我们可以初步理解为什么函数式编程会很高效。
但是同时也要清楚,函数式编程要比面向对象编程更加需要注释,要清楚地描述是如何将实际问题抽象成数学问题的,推导过程是怎样的。
通过对比也可以看出来,面向对象的代码相对容易理解,而容易理解是非常重要的优势,因为我们常说:要写出人能理解的代码。

如果上面的应用题的一个条件改为
“路过张砖头家,小雨给了张砖头5个苹果”
两种思维方式的改动成本也是不一样的。对于面向对象思维,需要找到描述这个动作的代码,进行修改;对于函数式思维可能需要重新推导相关流程。

面向对象的思维在努力地告诉我们:说出你的世界,我们用代码描述它
函数式的思维在冷冰冰地跟我们说:说出你的世界,我们把它转换成数学

这个例子应该会引起很多人的疑惑,这是一道应用题,简单用了个加减乘除,跟函数式编程没有什么关系啊。
    其实我自己也很苦恼啊,该怎么把这个事情圆回来啊

如果大家认可这道题用加减法去做才是最正常的思维,那恭喜你已经有了函数式的思维,大家已经很自然地把题目中小明收到苹果抽象成了加法,小明给出去苹果抽象成了减法。你们已经做到了第一步——把实际问题抽象为数学问题 :)
    再吹一波我朝的基础教育。这是一个应付98元,给收银员103元,秒找5元的国度。
四则运算尽管没有那么高大上,但是还真可以跟函数式编程扯上点儿关系。运算符号+和运算符号-是可以当成函数看待的,运算符号的两边的数字看做函数输入,结果看做函数输出。如果感兴趣,可以看一下Haskell中对四则运算符的使用,相信看过之后大家会有更加直观的感受。

5.最后,map、reduce是不是函数式编程?

这是一个好问题,也是一个很有意思的问题,确实有很多函数式编程的文章在讲解并且强调这些函数的用法。
我想用另一个问题来解释一下这个问题:类是不是面向对象编程?
很多人会告诉我,类是面向对象编程中一个很重要的概念,但是不应该仅仅用有没有类来区分是不是面向对象的思维,即使用了类,如果不理解面向对象的思维,依旧会写出很多不是面向对象的代码。
我觉得同样可以解释一下map和reduce:这是函数式编程中比较常用的函数,但是如果你不理解函数式编程的思维,依旧可以用map和reduce写出很多非函数式的代码。

6.推荐

里面有一本讲haskell的书,这种为函数式而生的语言,个人觉得大家可以了解一下。简单看看Haskell语言的设计,会让大家有一些收获。
    看到Haskell的函数天生柯里化,可能会让你感到这个语言的神奇,同时也可以思考一下js的柯里化
    看到 (+3) 可以作为一个函数使用的时候,可能会让你感到这个世界的神奇

看看名人大家是如何吐槽面向对象的

傻瓜函数式编程

JS函数式编程指南

HASKELL 趣学指南

Functor, Applicative, 以及 Monad 的图片阐释


最后,团队为了招聘方便,整了个公众号,主要是一些招聘信息,团队信息,所有的技术文章在公众号里也可以看到,对了,如果你想去美团其他团队,我们也可以帮你内推哦 ~

二维码
二维码