Detailed explanation of reduce fold unfold usage in JS

Detailed explanation of reduce fold unfold usage in JS

fold (reduce)

Let’s talk about reduce. I really like this function. It saves a lot of code and has some declarative prototypes. Some common tool functions, such as flatten, deepCopy, mergeDeep, etc., are implemented elegantly and concisely using reduce. Reduce is also called fold. It is essentially a process of folding an array. It converts multiple values ​​in the array into one value through calculation. Each calculation will be processed by a function. This function is the core element of reduce, called reducer. The reducer function is a two-dimensional function that returns a single value. The common add function is reducer.

const addReducer = (x, y) => x + y;

The add function is a reducer. The most common usage is to use it in conjunction with the array's reduce method.

[1, 2, 3, 4, 5].reduce(addReducer, 0) // 15

To better understand reduce, let's implement this function using different ideas.

Using for...of

const reduce = (f, init, arr) => {
  let acc = init;
  for (const item of arr) {
    acc = f(acc, item);
  }
  return acc
}
// Execute reduceFor(addReducer, 0, [1, 2, 3, 4, 5]) // 15

Using while loop

reduce = (f, init, arr) => {
  let acc = init;
  let current;
  let i = 0;
  while ((current = arr[i++])) {
    acc = f(acc, current);
  }
  return acc;
}

// Execute reduceFor(addReducer, 0, [1, 2, 3, 4, 5]) // 15

More like fold implementation

The above implementation is easy to understand, but it does not seem to reflect the folding process. Folding should be a layer-by-layer squeezing operation on the array. The above implementation actually separates the array and logic, and also introduces more intermediate variables, although there are no side effects internally.
In fact, if you think about it from another perspective, if you pass the state through parameters, you can better reflect the fold process. The parameters here are values ​​that refer to gradually changing arrays and calculated values, and you can make it as stateless as possible. The implementation of a truly pure function has no expressions, only statements, which can be achieved with recursion. The following implementation uses tail recursion to implement reduce. You can see how the array and calculated values ​​change during the implementation process. It fits the name fold very well

function reduce(f, init, arr) {
  if (arr.length === 0) return init;
  const [head, ...rest] = arr;
  return reduceRecursion(f, f(init, head), rest);
}

// Execute reduceFor(addReducer, 0, [1, 2, 3, 4, 5]) // 15

unfold

The reverse of fold is unfold. As the name suggests, unfold generates a series of values ​​based on a reverse reducer. At this point, if the original reducer implementation is similar to (a, b) -> c, then the reverse is c -> [a, b]. Generating a sequence is a very basic operation, but even this basic operation has many implementation ideas. Before introducing unfold, let's take a look at other ways to implement sequences and then make a comparison.

Sequence Implementation

range(0, 100, 5)

Expected results

[0, 5, 10, ... 95]

Array Implementation

I won’t say much about this, everyone should know it.

range = (first, last, step) => {
  const n = (last - first) / step + 1;
  return Array.from({ length: n - 1 })
            .map((_, index) => first + index * step);
}
// You can also use the second parameter of from // Array.from({ length: n }, (_, i) => first + i * step);

Generator Implementation

There is another powerful tool for generating sequences, that is generator, which is used to generate data. The generator returns an iterator, which can also easily generate sequences

function* range(first, last, step) {
  let acc = first;
  while (acc < last) {
    yield acc;
    acc = acc + step;
  }
}
[...range(0, 100, 5)]

Compared with the two, generator focuses more on the generation process, while Array focuses on the data change process.

unfold implementation

Before implementing unfold, let's first sort out the implementation ideas. Like fold, it also uses recursion, and the corresponding data changes must be seen during the implementation process. The general process is as follows

0 -> [0, 5]

5 -> [5, 10]

10 -> [10, 15]

15 -> [15, 20]

...

90 -> [90, 95]

95 -> [95, 100]

It can be seen that the process is exactly the reverse of fold, which conforms to c -> [a, b]. Because the initial value must be an array, unfold only needs two parameters, which is implemented as follows.

function unfold(f, init) {
  const g = (f, next, acc) => {
    const result = f(next);
    const [head, last] = result || [];
    console.log(last);
    return result ? g(f, last, acc.concat(head)) : acc;
  };
  return g(f, init, []);
}

range = R.curry((first, last, step) =>
  unfold(next => next < last && [next, next + step], 0)
)

// Execute range(0, 100, 5)

Summarize

The above is a brief introduction to fold and unfold, two very important concepts in FP programming, combined with reduce and an example of generating a sequence. Of course, their functions are not only to generate sequences, but also have many powerful functions.

The above is a detailed explanation of the usage of reduce fold unfold in JS. For more information about JS, please pay attention to other related articles on 123WORDPRESS.COM!

You may also be interested in:
  • Summary of using the reduce() method in JS
  • js uses the reduce method to make your code more elegant
  • JavaScript array reduce() method syntax and example analysis
  • Basic use of javascript array includes and reduce
  • Detailed explanation of the differences between js array find, some, filter, and reduce
  • 25 advanced uses of JS array reduce that you must know
  • JS uses the reduce() method to process tree structure data
  • Detailed explanation of JavaScript Reduce
  • Detailed explanation of JavaScript Array.reduce source code
  • 8 JS reduce usage examples and reduce operation methods

<<:  How to check whether the graphics driver has been successfully installed in Ubuntu

>>:  Tutorial on migrating mysql from phpstudy to Linux

Recommend

Steps for Docker to build a private warehouse Harbor

Harbor Harbor is an open source solution for buil...

MySQL incremental backup and breakpoint recovery script example

Introduction Incremental backup means that after ...

Common scenarios and avoidance methods for index failure in MySQL

Preface I have read many similar articles before,...

Rendering Function & JSX Details

Table of contents 1. Basics 2. Nodes, trees, and ...

Mysql database recovery actual record by time point

Introduction: MySQL database recovery by time poi...

What should I do if I can't view the source file of a web page?

Q: Whether using Outlook or IE, when you right-cl...

Common usage of hook in react

Table of contents 1. What is a hook? 2. Why does ...

Docker installation Nginx tutorial implementation illustration

Let’s install Nginx and try it out. Please note t...

JavaScript canvas realizes the effect of nine-square grid cutting

This article shares the specific code of canvas t...

HTTP return code list (Chinese and English explanation)

http return code list (below is an overview) for ...

Docker MQTT installation and use tutorial

Introduction to MQTT MQTT (Message Queuing Teleme...

How to view and execute historical commands in Linux

View historical commands and execute specified co...

How CSS affects the white screen time during initial loading

Rendering pipeline with external css files In the...