This is the 9th day of my participation in the August More Text Challenge

Functional programming

The JavaScript language has been marked by functional programming since its birth. It treats functions as an independent data type on a completely equal footing with other data types. In JavaScript, you can do either object-oriented programming or functional programming. Some even say that JavaScript is the first functional programming language ever to be adopted on a large scale.

The new features in ES6 make functional programming easier and more powerful. This chapter describes functional programming in ES6.

Currie,

Currying refers to the breaking up of a multi-argument function into a series of functions, each of which takes only one argument (unary).

function add (a, b) {
  return a + b;
}

add(1.1) / / 2
Copy the code

In the code above, the function add takes two arguments, a and b.

Corrification is the splitting of the above function into two functions, each of which takes only one argument.

function add (a) {
  return function (b) {
    returna + b; }}// Or use the arrow function notation
const add = x= > y= > x + y;

const f = add(1);
f(1) / / 2
Copy the code

In the code above, function add takes only one argument, a, and returns a function, f. Function f also takes only one argument, b.

Function synthesis

Function composition refers to combining multiple functions into one function.

const compose = f= > g= > x= > f(g(x));

const f = compose (x= > x * 4) (x= > x + 3);
f(2) / / 20
Copy the code

In the code above, compose is a function synthesizer that combines two functions into one.

It can be found that currization is closely related to function synthesis. The former is used to split a function into multiple functions, while the latter is used to combine multiple functions into a single function.

Parameter inversion

Parameter inversion (FLIP) refers to changing the order of the first two arguments of a function.

var divide = (a, b) = > a / b;
var flip = f.flip(divide);

flip(10.5) / / 0.5
flip(1.10) / / 10

var three = (a, b, c) = > [a, b, c];
var flip = f.flip(three);
flip(1.2.3); // => [2, 1, 3]
Copy the code

In the code above, 10 divided by 5 equals 2 if you follow the normal argument order. But the new function, with the arguments inverted, gives you 5 divided by 10, which gives you 0.5. If the original function has three arguments, only the first two arguments are reversed.

The code for parameter inversion is very simple.

let f = {};
f.flip =
  fn= >
    (a, b, ... args) = >fn(b, a, ... args.reverse());Copy the code

Perform the border

An execution boundary (until) refers to a function that executes until a condition is met.

let condition = x= > x > 100;
let inc = x= > x + 1;
let until = f.until(condition, inc);

until(0) / / 101

condition = x= > x === 5;
until = f.until(condition, inc);

until(3) / / 5
Copy the code

In the code above, the condition for the first paragraph is to execute until x is greater than 100, so if x starts at 0, it will execute all the way to 101. The condition for the second paragraph is to execute until it equals 5, so the final value of x is 5.

The implementation of the execution boundary is as follows.

let f = {};
f.until = (condition, f) = >
  (. args) = > {
    var r = f.apply(null, args);
    return condition(r) ? r : f.until(condition, f)(r);
  };
Copy the code

The key to the code above is to return the result if the condition is met, otherwise keep recursively executing.

Queue operations

Queue (list) operations include the following types.

  • head: Retrieves the first non-empty member of the queue.
  • last: Retrieves the last non-empty member of a finite queue.
  • tail: Retrieves non-empty members except the “queue head”.
  • init: Retrieves non-empty members except the tail of the queue.

Here’s an example.

f.head(5.27.3.1) / / 5
f.last(5.27.3.1) / / 1
f.tail(5.27.3.1) / / [27, 3, 1)
f.init(5.27.3.1) / / [5, 27, 3]
Copy the code

These methods are implemented as follows.

let f = {};
f.head = (. xs) = > xs[0];
f.last = (. xs) = > xs.slice(-1);
f.tail = (. xs) = > Array.prototype.slice.call(xs, 1);
f.init = (. xs) = > xs.slice(0, -1);
Copy the code

merge

Merge operations are classified into concat and concatMap. The former is to combine multiple arrays into one, while the latter is to process the parameters and then combine the results into an array.

f.concat([5], [27], [3]) / / [5, 27, 3]
f.concatMap(x= > 'hi ' + x, 1The [[2]], 3) // ['hi 1', 'hi 2', 'hi 3']
Copy the code

The code for these two methods is as follows.

let f = {};
f.concat =
  (. xs) = > xs.reduce((a, b) = > a.concat(b));
f.concatMap =
  (f, ... xs) = > f.concat(xs.map(f));
Copy the code

Pairing operation

The pairing operation is divided into zip and zipWith methods. The zip operation pairs the members of two queues one by one to compose a new queue. If two queues are not equally long, the extra members of the longer queue are ignored. The first argument to the zipWith operation is a function that pairs the following queue members one by one. Enter this function and the return value forms a new queue.

Here’s an example.

let a = [0.1.2];
let b = [3.4.5];
let c = [6.7.8];

f.zip(a, b) // [[0, 3], [1, 4], [2, 5]]
f.zipWith((a, b) = > a + b, a, b, c) / / [9, 12, 15]
Copy the code

In the code above, the first argument to the zipWith method is a summation function that pairs the members of the next three queues.

The implementation of these two methods is as follows.

let f = {};

f.zip = (. xs) = > {
  let r = [];
  let nple = [];
  let length = Math.min.apply(null, xs.map(x= > x.length));

  for (var i = 0; i < length; i++) {
    xs.forEach(
      x= > nple.push(x[i])
    );

    r.push(nple);
    nple = [];
  }

  return r;
};

f.zipWith = (op, ... xs) = >
  f.zip.apply(null, xs).map(
    (x) = > x.reduce(op)
  );
Copy the code