Shiv ENOW large front end

Company official website: CVTE(Guangzhou Shiyuan Stock)

Team: ENOW team of CVTE software Platform Center for Future Education

The author:

preface

This article uses an example to illustrate how higher-order functions can help us build higher-level abstractions.

A function is an abstract way of describing a collection of operations that provides the ability to work directly at the level of abstraction. For example: to take the cube of a particular number, the implementation of the function could be:

function cube(x) {
  return x * x * x;
}
Copy the code

Here the function describes the idea of cubing, rather than relying on a specific number. Thus, the cube function can be conveniently used anywhere. Instead of defining such a function, we can use the basic expression:

3 * 3 *3
a * a * a
b * b * b
Copy the code

But then we’ll always be working at the basic operational level of the programming language. Creating abstractions and then working directly at a higher level is the ability that functions provide, as well as an analytical and problem-solving skill. Abstraction is a very powerful tool. To some extent, abstraction determines programming ability. So how do higher-order functions help us build higher-level abstractions?

Let’s start with the sum

Let’s start with a simple requirement: summation.


Consider the following function:

  1. Calculate the sum of integers from a to B:
function sumInteger(a, b) {
  const term = x= > x;

  let res;
  if (a > b) res = 0;
  else res = term(a) + sumInteger(a + 1, b);
  return res;
}
Copy the code
  1. Find the sum of squares of each integer from a to b:
function sumSquare(a, b) {
    const term = x= > Math.pow(x, 2);

    let res;
    if (a > b) res = 0;
    else res = term(a) + sumSquare(a + 1, b);
    return res;
}
Copy the code
  1. Compute the sum of the following sequences:


1 1 3 + 1 5 7 + 1 9 11 + \ frac {1} {1, 3} + \ frac {1} {5, 7} + \ frac {1} {Sept. 11} +…

It says that this will converge to PI /8\ PI /8 PI /8(I won’t prove it):

function piSum(a, b) {
  const term = x= > 1 / (x * (x + 2))

  let res;
  if (a > b) res = 0;
  else res = term(a) + piSum(a + 4, b);
  return res;
}
Copy the code

For the sake of observation, I’ll name the evaluation functions in all three examples term. You can see that the above three functions contain the same calculation mode, but the specific functions used in the process are different

  1. Apply a function (term) to A and calculate the spare
  2. Add the spare to the current result
  3. Find the next value of A and start a new sum based on the current result

The existence of the same pattern is a reminder that abstraction can be established here.

This process is summed abstractly by higher-order functions

We can get each of the above functions by filling the following template:


function <name> (a, b) {
  let res;
  if (a > b) {
    res = 0;
  } else {
    // term(a) returns the addend corresponding to a
    // next returns the next a
    res = term(a) + name(next(a), b)
  }
}
Copy the code

If we translate the name/next of the above template into formal parameters, we can get the higher-order function sum_HOF:

/** * sum *@param {number} A: the starting point of the range *@param {number} B: the end point of the range *@param {function} Next, let's figure out the next a star@param {function} Term evaluates the value of the function to be accumulated */
function sum_HOF(a, b, next, term) {
  let res;
  if (a > b) {
    res = 0;
  } else {
    res = term(a) + sum_HOF(next(a), b, next, term);
  }
  return res;
}
Copy the code

Here we answer two questions:

  1. sum_HOFWhat makes it special?
    • Is that it expressessumThis concept.
  2. Why special?
    • Let’s look backsumInteger,sumSquareandpiSumThese three functions, essentially, they say the same thing: sum over a sequence. But because they rely on a different evaluation relationship (think of it as term), they have different code implementations. whilesum_HOFI’m abstracting this evaluation as a formal parameter, so that no matter what evaluation you do, if you pass in the corresponding function, you get the sum that you want.

Now we can rewrite the above three examples using sum_HOF:

Find the sum of the natural numbers in the range

// term
const term = x= > x;
// next
const inc = (a) = > a + 1;
const res = sum_HOF(1.4, inc, term);
console.log(res) // Output: 10
Copy the code

Find the sum of products of natural numbers in the range

// term
const squareTerm = (x) = > x * x;
// next
const inc = (a) = > a + 1;
const res = sum_HOF(1.4, inc, squareTerm);
console.log(res) // Output: 30
Copy the code

Three, piSum

const piTerm = (x) = > 1 / (x * (x + 2));
const next = (x) = > x + 4
const piSumRes = sum_HOF(1.1000, next, piTerm)
console.log(piSumRes)   // Output: 0.7490014975044914
Copy the code

In comparison, the two versions are implemented in markedly different ways. You decide which is better. Here’s an example of what we can do when we have this higher-order sum function.

Use higher order functions to find definite integrals

One of the formulas for calculating definite integrals:


a b f = [ f ( a + d x 2 ) + f ( a + d x + d x 2 ) + f ( a + 2 d x + d x 2 ) + ] d x \ int \ limits_a ^ b f = [f (a + \ frac {dx} {2}) + f (a + dx + \ frac {dx} {2}) + f (a + 2 dx + \ frac {dx} {2}) +…] dx

Let’s see, the form of this formula mainly consists of two parts:

  1. Sum of a bunch of terms
  2. Common coefficientdx

We already have a powerful tool for expressing the concept of summation, and all we have to do is pass in the parameters one by one

function integral(f, a, b, dx) {
    const addDeltaX = (x) = > x + dx;    // next, used to calculate the next a
    // term is the function f
    return dx * sum_HOF((a + dx / 2), b, addDeltaX, f);
}
Copy the code

There’s another way to do it: Simpson’s rule, which is more accurate than the above rule, is stated as follows: [y0 h3 + 4 y1 y2 + 2 + 4 y3 + 2 y4 + ⋅ ⋅ ⋅ yn – 2 + 2 + 1 + 4 yn – yn] \ frac {h} {3} [y_0 + 4 y_1 y_2 + 2 + 4 y_3 +… + 2 + 2 y_4 y_ {2} n – + 4 y_ {}, n – 1 + y_n] 3 h [y0 y + 4 y1 y2 + 2 + 4 + 2 + 3 + 2 y4 ⋅ ⋅ ⋅ yn – yn – 1 + 2 + 4 yn] which h = nh (b – a) = \ frac {(b – a)} {n} h = n (b – a), n is an even number, and yk = f (a + kh) y_k = f (a + kh) yk = f (a + kh).

Simpson’s rule may seem complicated at first glance, but it has the same characteristics as the formula above:

  1. A sum
  2. A common coefficient.

So the only thing we have to do is figure out the parameters that sum_HOF needs.

function simpson(f, a, b, n) {
  // Is it even
  const isEven = (num) = > num % 2= = =0;

  const h = (b - a) / n;

  const yk = (k) = > f(a + (k * h));

  const getCoefficient = (k) = > {
    if (k === 0 || k === n){ 
      return 1;
    } else if (isEven(k)) {
      return 2;
    } else {
      return 4; }}const term = k= > getCoefficient(k) * yk(k);

  const inc = x= > x + 1;
  return (h * (sum_HOF(0, n, inc, term))) / 3;
}
Copy the code

One step closer to

Sum_HOF is a good way to solve summation problems. But if you think about it, there’s room for improvement in our abstract work. Why is that? Because the addition operator can actually be expressed as a function:

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

In addition to addition, there are actually other combinations, such as multiplication and division. We can express these combinations as a function. Therefore, we can further obtain an accumulator function to represent the process of combining a series of items using some accumulator functions:

/ * * * *@param {function} Combiner cumulative function *@param {number} Null_value The default value *@param {function} Term applies to the evaluation object and evaluates the target value *@param {*} A the starting value of the range *@param {*} Next evaluates the starting value of the next operation *@param {*} B The end value of the range */
function accumulate(combiner, null_value, term, a, next, b) {
  if (a > b) {
    return null_value;
  } else {
    returncombiner( term(a), accumulate(combiner, null_value, term, next(a), next, b) ); }}Copy the code

Sum_HOF is simply a special form of accumulate, and if we want to write functions such as multiplication, division, and decay, we simply replace them with a combiner.

In a further step

You taste, you fine taste. The accumulate function above has one disadvantage: it is too inflexible to do the calculation and accumulate on every number in the range. Can it be extended to operate only on numbers that satisfy this condition? Aha, of course! Just add a filter:

function filterAccumulate(combiner, nullValue, term, a, next, b, filter) {
  if (a > b) {
    return nullValue;
  } else if (filter(a)) {
    return combiner(
      term(a),
      filterAccumulate(combiner, nullValue, term, next(a), next, b, filter)
    );
  } else {
    returncombiner( nullValue, filterAccumulate(combiner, nullValue, term, next(a), next, b, filter) ); }}Copy the code

So far, we have seen briefly the usefulness of higher-order functions in establishing abstractions. The paper come zhongjue shallow, and must know this to practice. Good luck writing your own code.