函数式语言特性之函数是一等公民
Posted 大前端菁英荟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了函数式语言特性之函数是一等公民相关的知识,希望对你有一定的参考价值。
第一篇:函数式语言特性之函数是一等公民
为什么要学习函数式?
函数编程(简称FP)不只代指Haskell, Lisp等之类的语言,还表示一种编程思维,软件思考方式,也称面向函数编程,我们可以将它与面向对象编程OOP进行对比:
面向对象 | 面向函数 | |
---|---|---|
抽象方式 | 对象 | 函数 |
变量 | 通常可变 | 通常不可变 |
调用方式 | 通过对象接口 | 通过函数或者函数组合 |
与数据耦合度 | 数据和对数据的操作紧紧耦合 | 数据与函数是松耦合的 |
当您掌握了函数式编程,也即掌握了一种抽象世界新的方式,多一种编程思想,而不是万物皆对象。
简言之,无论是面向对象还是面向函数,如果我们走了极端,那都是错误的,我们必须博采众长。如果您还想了解函数式,那请跟我来。
函数式概念
以下摘自维基百科:
又称泛函编程,是一种编程范型,它将电脑运算视为数学上的函数计算,并且避免使用程序状态以及易变对象。函数编程语言最重要的基础是λ演算(lambda calculus)。而且λ演算的函数可以接受函数当作输入(引数)和输出(传出值)。
比起命令式编程,函数式编程更加强调程序执行的结果而非执行的过程,倡导利用若干简单的执行单元让计算结果不断渐进,逐层推导复杂的运算,而不是设计一个复杂的执行过程。结果比过程更重要。
函数式编程中的函数这个术语不是指计算机中的函数(实际上是Subroutine),而是指数学中的函数,即自变量的映射。也就是说一个函数的值仅决定于函数参数的值,不依赖其他状态。比如sqrt(x)函数计算x的平方根,只要x不变,不论什么时候调用,调用几次,值都是不变的。函数式编程为我们提供了另外一种抽象和思考的方式。函数式编程是一种风格,与编程语言无关, 面向对象也是一种风格,与编程语言无关,两种风格并不矛盾,可以结合的- 叫 functional object(Objects in OCaml)
接下来,我们就来了解一下函数式最重要的特性:函数是一等公民
函数是一等公民
“一等公民”就是说,它和其他对象都一样。函数真没什么特殊的,我们可以像对待任何其他数据类型一样对待,将函数视为一个值。您可以对数字,数组或向量,列表,字符串,任何这些值进行任何操作。因此您可以将它们传递给函数,可以将它们存储在变量中,也可以从函数中返回它们。所有这些都使函数成为一等公民。
以下使用javascript语言描述。
匿名函数赋值给变量
var test = function(/* arguments */) {
console.log("Hello!");
}
函数当作参数
function sayHello() {
return "Hello, ";
}
function greeting(helloMessage, name) {
console.log(helloMessage() + name);
}
greeting(sayHello, "JavaScript!");
函数当作返回值
function sayHello() {
return function() {
console.log("Hello!");
}
}
而像filter, map, forEach, reduce大家都耳熟能详,通过传递一个函数给这些函数,实现更强大的功能,这就是最简单的2个函数进行组合,实现复杂的功能。
稍微复杂一点的例子,通过函数组合的方式,组装出不同的功能。
// 例如我们有一个如下形式的 Ajax 请求函数:
const ajax = method => type => query => { ... };
const get = ajax('GET');
const post = ajax('POST');
const getJson = get('json');
const gethtml = ajax('GET')('text/html') = get('text/html');
上面的ajax函数,由3个参数决定,而get和post函数确定了method参数,进一步getJson和getHtml确定了type参数,而最后一个参数,再由最终调用时确定。
再复杂一点的例子,了解一下compose函数,这是非常著名的redux框架中compose.js源码:
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
compose函数作用是把一系列的函数,组装生成一个新的函数,并且从后到前,后面参数的执行结果作为其前一个的参数。例子:
var fun1 = val => `fun1->${val}`
var fun2 = val => `fun2->${val}`
var fun3 = val => `fun3->${val}`
// 可以这样调用, 输出fun1->fun2->fun3->测试
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
处理过之后,函数就变成我们想要的格式了。
因此,您是否已经理解为什么函数必须是一等公民:函数为一等公民是函数式编程的基础。 在面向对象里将对象进行组合才能建立更强大的类,而在函数式编程里,必须将函数进行组合才能创建更强大的函数。
以上是关于函数式语言特性之函数是一等公民的主要内容,如果未能解决你的问题,请参考以下文章