Home  >  Article  >  Web Front-end  >  JavaScript functional programming (1)

JavaScript functional programming (1)

黄舟
黄舟Original
2017-03-06 14:14:211004browse

1. Introduction

When it comes to functional programming, your first impression may be of those obscure academic codes, which are full of abstract and unclear codes. The cloud symbol seems to be something only computer professors in universities use. This may have been true in a certain era, but in recent years, with the development of technology, functional programming has played a huge role in actual production, and more and more languages ​​have begun to add closures, Anonymous functions and other very typical features of functional programming, to some extent, functional programming is gradually "assimilating" imperative programming.

JavaScript is a typical multi-paradigm programming language. With the popularity of React in the past two years, the concept of functional programming has also become popular. RxJS, cycleJS, lodashJS, underscoreJS and other open source libraries are used Functional features. So let’s introduce some knowledge and concepts of functional programming.

2. Pure function

If you still remember some mathematics knowledge in junior high school, the concept of functionf is to produce an output# for the inputx ##y = f(x). This is the simplest pure function. The definition of a pure function is that for the same input, you will always get the same output, without any observable side effects, and without relying on the state of the external environment.

Let’s give an example. For example, in Javascript, some operations on arrays are pure, and some are not pure:

var arr = [1,2,3,4,5];

// Array.slice是纯函数,因为它没有副作用,对于固定的输入,输出总是固定的
// 可以,这很函数式
xs.slice(0,3);
//=> [1,2,3]
xs.slice(0,3);
//=> [1,2,3]

// Array.splice是不纯的,它有副作用,对于固定的输入,输出不是固定的
// 这不函数式
xs.splice(0,3);
//=> [1,2,3]
xs.splice(0,3);
//=> [4,5]
xs.splice(0,3);
//=> []

In functional programming, we want to What is needed is a pure function like slice, not a function like splice that messes up the data every time it is called.

Why does functional programming exclude impure functions? Let’s look at another example:

//不纯的
var min = 18;
var checkage = age => age > min;

//纯的,这很函数式
var checkage = age => age > 18;


In the impure version,

checkage The behavior of this function depends not only on the input parameter age, but also on Based on an external variable min, in other words, the behavior of this function needs to be determined by the external system environment. For large systems, this dependence on external state is the main reason for greatly increasing system complexity.

It can be noticed that the pure

checkage hardcodes the key number 18 inside the function, and the scalability is relatively poor. We can see it in the following currying Learn how to solve this problem using elegant functions.

Pure functions can not only effectively reduce the complexity of the system, but also have many great features, such as cacheability:

import _ from 'lodash';
var sin = _.memorize(x => Math.sin(x));

//第一次计算的时候会稍慢一点
var a = sin(1);

//第二次有了缓存,速度极快
var b = sin(1);

3. Currying of functions

The definition of function curry is simple: call a function with a subset of arguments and have it return a function to handle the remaining arguments.

For example, for the addition function var add = (x, y) => x + y, we can curry it like this:

//比较容易读懂的ES5写法
var add = function(x){
    return function(y){
        return x + y
    }
}

//ES6写法,也是比较正统的函数式写法
var add = x => (y => x + y);

//试试看
var add2 = add(2);
var add200 = add(200);

add2(2); // =>4
add200(50); // =>250

For an extremely simple function like addition , currying is not of much use.

Do you still remember the checkage function above? We can curry it like this:

var checkage = min => (age => age > min);
var checkage18 = checkage(18);
checkage18(20);
// =>true

In fact, currying is a method of "preloading" a function. By passing fewer parameters, you get a new function that already remembers these parameters. In a sense, this is a "caching" of parameters and a very efficient way to write functions:

import { curry } from 'lodash';

//首先柯里化两个纯函数
var match = curry((reg, str) => str.match(reg));
var filter = curry((f, arr) => arr.filter(f));

//判断字符串里有没有空格
var haveSpace = match(/\s+/g);

haveSpace("ffffffff");
//=>null

haveSpace("a b");
//=>[" "]

filter(haveSpace, ["abcdefg", "Hello World"]);
//=>["Hello world"]

4. Function combination

Learned to use pure functions And how to curry it, we can easily write such "package style" code:

h(g(f(x)));

Although this is also functional code , but it is still "inelegant" in a certain sense. In order to solve the problem of function nesting, we need to use "function composition":

//两个函数的组合
var compose = function(f, g) {
    return function(x) {
        return f(g(x));
    };
};

//或者
var compose = (f, g) => (x => f(g(x)));

var add1 = x => x + 1;
var mul5 = x => x * 5;

compose(mul5, add1)(2);
// =>15

The compose we define is like double-sided tape, which can combine any two pure functions together. Of course, you can also extend the "three-sided tape" that combines three functions, or even "four-sided tape" or "N-sided tape".

This flexible combination allows us to combine functional codes like building blocks:

var first = arr => arr[0];
var reverse = arr => arr.reverse();

var last = compose(first, reverse);

last([1,2,3,4,5]);
// =>5

5. Point Free

With currying and function combination The basic knowledge of Point Free is introduced below.

If you are careful, you may notice that in the previous code we always like to convert some of the methods that come with objects into pure functions:

var map = (f, arr) => arr.map(f);

var toUpperCase = word => word.toUpperCase();


## There is a reason for this approach.

Point Free mode currently does not have a Chinese translation. If you are interested, you can read the English explanation here:

http://www.php.cn/

The Chinese explanation is probably, don’t name fleeting intermediate variables, such as:

//这不Piont free
var f = str => str.toUpperCase().split(' ');

In this function, we use str as our intermediate variable, but this intermediate variable not only makes the code become Any longer than that is pointless. Let’s transform this code:

var toUpperCase = word => word.toUpperCase();
var split = x => (str => str.split(x));

var f = compose(split(' '), toUpperCase);

f("abcd efgh");
// =>["ABCD", "EFGH"]


This style can help us reduce unnecessary naming and keep the code simple and versatile. Of course, in order to write a Point Free style in some functions, other parts of the code must be less Point Free, and you need to make your own choices here.

六、声明式与命令式代码

命令式代码的意思就是,我们通过编写一条又一条指令去让计算机执行一些动作,这其中一般都会涉及到很多繁杂的细节。

而声明式就要优雅很多了,我们通过写表达式的方式来声明我们想干什么,而不是通过一步一步的指示。

//命令式
var CEOs = [];
for(var i = 0; i < companies.length; i++){
    CEOs.push(companies[i].CEO)
}

//声明式
var CEOs = companies.map(c => c.CEO);


命令式的写法要先实例化一个数组,然后再对 companies 数组进行for循环遍历,手动命名、判断、增加计数器,就好像你开了一辆零件全部暴露在外的汽车一样,虽然很机械朋克风,但这并不是优雅的程序员应该做的。

声明式的写法是一个表达式,如何进行计数器迭代,返回的数组如何收集,这些细节都隐藏了起来。它指明的是做什么,而不是怎么做。除了更加清晰和简洁之外,map 函数还可以进一步独立优化,甚至用解释器内置的速度极快的 map 函数,这么一来我们主要的业务代码就无须改动了。

函数式编程的一个明显的好处就是这种声明式的代码,对于无副作用的纯函数,我们完全可以不考虑函数内部是如何实现的,专注于编写业务代码。优化代码时,目光只需要集中在这些稳定坚固的函数内部即可。

相反,不纯的不函数式的代码会产生副作用或者依赖外部系统环境,使用它们的时候总是要考虑这些不干净的副作用。在复杂的系统中,这对于程序员的心智来说是极大的负担。

七、尾声

任何代码都是要有实际用处才有意义,对于JS来说也是如此。然而现实的编程世界显然不如范例中的函数式世界那么美好,实际应用中的JS是要接触到ajax、DOM操作,NodeJS环境中读写文件、网络操作这些对于外部环境强依赖,有明显副作用的“很脏”的工作。

这对于函数式编程来说也是很大的挑战,所以我们也需要更强大的技术去解决这些“脏问题”。我会在下一篇文章中介绍函数式编程的更加高阶一些的知识,例如Functor、Monad等等概念。

以上就是JavaScript函数式编程(一)的内容,更多相关内容请关注PHP中文网(www.php.cn)!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn