函数式语言特性之函数是一等公民

Posted 大前端菁英荟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了函数式语言特性之函数是一等公民相关的知识,希望对你有一定的参考价值。


第一篇:函数式语言特性之函数是一等公民

为什么要学习函数式?

函数编程(简称FP)不只代指Haskell, Lisp等之类的语言,还表示一种编程思维,软件思考方式,也称面向函数编程,我们可以将它与面向对象编程OOP进行对比:


面向对象 面向函数
抽象方式 对象 函数
变量 通常可变 通常不可变
调用方式 通过对象接口 通过函数或者函数组合
与数据耦合度 数据和对数据的操作紧紧耦合 数据与函数是松耦合的

当您掌握了函数式编程,也即掌握了一种抽象世界新的方式,多一种编程思想,而不是万物皆对象。

简言之,无论是面向对象还是面向函数,如果我们走了极端,那都是错误的,我们必须博采众长。如果您还想了解函数式,那请跟我来。

函数式概念

以下摘自维基百科:

又称泛函编程,是一种编程范型,它将电脑运算视为数学上的函数计算,并且避免使用程序状态以及易变对象。函数编程语言最重要的基础是λ演算(lambda calculus)。而且λ演算的函数可以接受函数当作输入(引数)和输出(传出值)。

比起命令式编程,函数式编程更加强调程序执行的结果而非执行的过程,倡导利用若干简单的执行单元让计算结果不断渐进,逐层推导复杂的运算,而不是设计一个复杂的执行过程。结果比过程更重要。

函数式编程中的函数这个术语不是指计算机中的函数(实际上是Subroutine),而是指数学中的函数,即自变量的映射。也就是说一个函数的值仅决定于函数参数的值,不依赖其他状态。比如sqrt(x)函数计算x的平方根,只要x不变,不论什么时候调用,调用几次,值都是不变的。函数式编程为我们提供了另外一种抽象和思考的方式。函数式编程是一种风格,与编程语言无关, 面向对象也是一种风格,与编程语言无关,两种风格并不矛盾,可以结合的- 叫 functional object(Objects in OCaml)

接下来,我们就来了解一下函数式最重要的特性:函数是一等公民

函数是一等公民

“一等公民”就是说,它和其他对象都一样。函数真没什么特殊的,我们可以像对待任何其他数据类型一样对待,将函数视为一个值。您可以对数字,数组或向量,列表,字符串,任何这些值进行任何操作。因此您可以将它们传递给函数,可以将它们存储在变量中,也可以从函数中返回它们。所有这些都使函数成为一等公民。

以下使用javascript语言描述。

  • 匿名函数赋值给变量

 
   
   
 
  1. var test = function(/* arguments */) {

  2. console.log("Hello!");

  3. }

  • 函数当作参数

 
   
   
 
  1. function sayHello() {

  2. return "Hello, ";

  3. }

  4. function greeting(helloMessage, name) {

  5. console.log(helloMessage() + name);

  6. }

  7. greeting(sayHello, "JavaScript!");

  • 函数当作返回值

 
   
   
 
  1. function sayHello() {

  2. return function() {

  3. console.log("Hello!");

  4. }

  5. }

而像filter, map, forEach, reduce大家都耳熟能详,通过传递一个函数给这些函数,实现更强大的功能,这就是最简单的2个函数进行组合,实现复杂的功能。

稍微复杂一点的例子,通过函数组合的方式,组装出不同的功能。

 
   
   
 
  1. // 例如我们有一个如下形式的 Ajax 请求函数:

  2. const ajax = method => type => query => { ... };


  3. const get = ajax('GET');

  4. const post = ajax('POST');


  5. const getJson = get('json');

  6. const gethtml = ajax('GET')('text/html') = get('text/html');

上面的ajax函数,由3个参数决定,而get和post函数确定了method参数,进一步getJson和getHtml确定了type参数,而最后一个参数,再由最终调用时确定。

再复杂一点的例子,了解一下compose函数,这是非常著名的redux框架中compose.js源码:

 
   
   
 
  1. export default function compose(...funcs) {

  2. if (funcs.length === 0) {

  3. return arg => arg

  4. }


  5. if (funcs.length === 1) {

  6. return funcs[0]

  7. }


  8. return funcs.reduce((a, b) => (...args) => a(b(...args)))

  9. }

compose函数作用是把一系列的函数,组装生成一个新的函数,并且从后到前,后面参数的执行结果作为其前一个的参数。例子:

 
   
   
 
  1. var fun1 = val => `fun1->${val}`

  2. var fun2 = val => `fun2->${val}`

  3. var fun3 = val => `fun3->${val}`


  4. // 可以这样调用, 输出fun1->fun2->fun3->测试

  5. compose(fun1,fun2,fun3)("测试")

核心代码:returnfuncs.reduce((a,b)=>(...args)=>a(b(...args)))

实际调用:[fun1,fun2,fun3].reduce((a, b) => (...args) => a(b(...args)))

第几轮循环 a的值 b的值 返回的值
第一轮循环 fun1 fun2 (...args)=>fun1(fun2(...args))
第二轮循环 (...args)=>fun1(fun2(...args)) fun3 (...args)=>fun1(fun2(fun3(...args)))

循环最后的返回值就是 (...args)=>fun1(fun2(fun3(...args)))。所以经过 compose 处理过之后,函数就变成我们想要的格式了。

因此,您是否已经理解为什么函数必须是一等公民:函数为一等公民是函数式编程的基础。 在面向对象里将对象进行组合才能建立更强大的类,而在函数式编程里,必须将函数进行组合才能创建更强大的函数。




以上是关于函数式语言特性之函数是一等公民的主要内容,如果未能解决你的问题,请参考以下文章

Go 语言设计哲学之十五:函数是一等公民

scala成长之路函数入门

Lambda 表达式与方法引用

Java8中的函数式编程

函数式编程,“香”吗?

函数式编程的特点