Functional programming

Continue to update, the content of the article is longer, it is suggested to collect food, if you think the content is ok, please like oh. This article summarizes the hook education – big front-end high salary training camp (PS: Class let me add ~)


What is functional programming

Functional Programming (FP), FP is one of the Programming paradigms, other common Programming paradigms are procedural Programming, object-oriented Programming.

  • Procedure-oriented thinking: it is a process-centered programming thought. It’s all about programming with the main goal of what’s happening, as opposed to object oriented who’s being affected.
  • Object Oriented way of thinking: abstract the things in the real world into a class and Object, through encapsulation, inheritance and polymorphism to demonstrate the connection of things.
  • Functional programming thinking: mapping real-world things and their relationships toApplication of world(Abstract the process)
    • The essence of a program: an operation to obtain an output from an input
    • X ->f(relation, mapping)-
    • Functions in functional programming do not refer to functions (methods) in a program, but to mappings
    • The same input always gives the same output (pure function)
// Non-functional
let a = 1;
let b = 2;
let sum = a + b;
console.log(sum);

/ / function type
function add(a, b) {
	return a + b;
}
let sum = add(1.2);
console.log(sum);

Copy the code

Functional expressions have one obvious advantage over non-functional expressions: they are reusable


Higher-order functions

What is a higher-order function

  • Higher-order functions
    • You can pass a function as an argument to another function
    • You can take a function as the return value of another function
  • As a parameter
    • code
// Higher-order functions - as arguments
/ / implementation forEach

function forEach(array, fn) {
    for (let i = 0; i < array.length; i++) {
        fn(array[i]);
    }
}

let arr = [1.2.3.4.5];
forEach(arr, item => {
    console.log(item)
});

/ / filter

function filter(array, fn) {
    let result = [];
    for (let i = 0; i < array.length; i++) {
        if (fn(array[i])) {
            result.push(array[i]); }}return result;
}

let arr2 = [1.2.3.4.5.6];
let result = filter(arr, item => item % 2= = =0);
console.log(result);
Copy the code
  • The output

  • As a return value
// Higher order function - as return value

// Implement the once function
function once(fn) {
    let flag = false;
    return function() {
        if(! flag) { flag =true; fn(... arguments); } } } let pay = once(function(money) { console.log(' paid: ${money}RMB '); }); pay(5);
pay(5);
pay(5);
Copy the code
  • The output

It’s only done once

Meaning of higher order functions

  • It helps us block out the details and just focus on the desired outcome
  • Used to abstract a general problem

Several higher-order functions in common use

Manually implement the map, every, and some functions

// map
const map = (array, fn) => {
    let result = [];
    for (let item of array) {
        result.push(fn(item));
    }
    return result;
}
let arr = [1.2.3.4.5];
let r = map(arr, item => item * 3);
console.log(r); // => [3, 6, 9, 12, 15];
Copy the code
// every
const every = (array, fn) => {
    let result = true;
     for(let value of array) {
         result = fn(value);
         if(! result)break;
         console.log(12112)}return result;
}
let arr = [1.2.3.4.5];
let r = every(arr, item => item > 3);
console.log(r); // => false
Copy the code
// some
const some = (array, fn) => {
    let result = false;
    for(let value of array) {
        result = fn(value);
        if (result) break;
    }
    return result;
}
let arr = [1.2.3.4.5];
let r = some(arr, item => item > 3);
console.log(r); // => true
Copy the code

closure

What is a closure

Closures: Functions that can read variables inside other functions.

  • A function is bundled together with references to its surrounding state (lexical context) to form a closure.
  • You can call an inner function of a function from another scope and access members of that function’s scope.
  • You can extend the scope of a variable.

Closure case

To calculate the salary of different ranks, you can check the existence of closures in the browser at its own breakpoint. (Code below)

<! DOCTYPE html> <html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="Width = device - width, inittial - scale = 1.0">
        <title>closure</title>
    </head>
    <body>
        <script type="text/javascript">
            // Calculate the salary of different ranks
            function getSalara(level) {
                return function(baseSalara) {
                    returnbaseSalara + level; }}const salara1 = getSalara(2000); // The closure can be observed at this break point
            const salara2 = getSalara(3000);
            console.log(salara1(15000));
            console.log(salara1(18000));
            console.log(salara2(20000));
        </script>
    </body>
</html>
Copy the code


Pure and impure functions

What is a pure function?

  • Pure functions: The same input always yields the same output without any visible side effects.
    • Pure function: slice

The code is as follows (example) :

let array = [1.2.3.4.5];

// The pure function -slice
console.log(array.slice(0.3));
console.log(array.slice(0.3));
console.log(array.slice(0.3));

Copy the code

The same input (0, 3) always yields the same output [1, 2, 3];

  • Impure function: splice

The code is as follows (example) :

let array = [1.2.3.4.5];

// impure function -splice
console.log(array.splice(0.3));
console.log(array.splice(0.3));
console.log(array.splice(0.3));

Copy the code

The input (0, 3) is the same, but the output is different.

The benefits of pure functions

  • cacheable
    • Because pure functions always have the same output for the same input, the results of pure functions can be cached.
  • testable
    • Pure functions make testing easier because pure functions have input and output, and the same input always has the same output, whereas unit tests are the output of assertion functions.
  • Parallel processing
    • Parallel manipulation of shared memory data in a multithreaded environment is likely to cause unexpected problems.
    • Pure functions do not need to access shared memory data, so they can be run arbitrarily in parallel.

case

Memoize memory function in LoDash

const _ = require('lodash');

function getArea(r) {
    console.log(r);
    return Math.PI * r * r;
}

let getAreaWithMemoize = _.memoize(getArea);

console.log(getAreaWithMemoize(4));
console.log(getAreaWithMemoize(4));
console.log(getAreaWithMemoize(4));
Copy the code

As you can see, only the first call executes the getArea method, and the other two get cached data.

Implement the memoize function manually

// Manually implement memoize

function memoize(fn) {
    let cache = {};
    returnfunction() { let key = JSON.stringify(... arguments);if(! cache[key]) { cache[key] = fn(... arguments); } cache[key] = cache[key] || fn(... arguments);returncache[key]; }}function getArea(r) {
    console.log(r);
    return Math.PI * r * r;
}

let getAreaWithMemoize = memoize(getArea);

console.log(getAreaWithMemoize(4));
console.log(getAreaWithMemoize(4));
console.log(getAreaWithMemoize(4));
Copy the code

The output is the same.

Side effects

Side effects make a function impure. A pure function is one in which the same input always gets the same output, but the same output cannot be guaranteed if the function depends on external variables, causing the function to become impure

// impure function

let min = 18;
function checkAge1(age) {
    return age >= min;
}

/ / pure functions

function checkAge2(age) {
    let min = 18;
    return age >= 18;
}
Copy the code

If mn=18,checkAge1(20) returns true, if mn=22,checkAge1(20) returns false, because checkAge1 depends on the external variable min, if min changes, it will result in the same input, not the same output. Chance makes the function impure. In contrast, checkAge2 is a pure function because it does not depend on external variables.

Sources of side effects

  • The configuration file
  • The database
  • Get user input
  • .

Currie,

What is Corrification

  • Curry
    • When a function has more than one argument, it is called by passing some of the arguments (these arguments never change).
    • It then returns another function to accept the remaining arguments and return the result.
    • Functions of several variables can be treated as functions of one variable
    • If all parameters are passed, they are executed immediately; If some arguments are passed, a function is returned to wait for the rest to be received.

case

function checkAge(age) {
    let min = 18;
    return age > min;
}

// plain pure function

function checkAge(min, age) {
    return age >= min;
}
console.log(checkAge(18.20));
console.log(checkAge(18.22));
console.log(checkAge(18.24));
/ / curry
// If 18 is always used in our function, then we need to pass in 18 continuously. By currizing the function, we just pass in the parameters we need to process.
function checkAge(min) {
    return function(age) {
        returnage >= min; }}/ / es6 writing

const checkAge = min => (age => age >= min)
const checkAge18 = checkAge(18);
console.log(checkAge18(20));
console.log(checkAge18(22));
console.log(checkAge18(24));

// The Currization function in lodash
const _ = require('lodash');

function getSum(a, b, c) {
    return a + b + c;
}

const curried = _.curry(getSum);

console.log(curried(1.2.3)); = >6
console.log(curried(1) (2.3)); = >6
console.log(curried(1.2) (3)); = >6

// The cremation case in Lodash

const _ = require('lodash');

// A normal function

const match = function(reg, str) {
    return str.match(reg);
}

// console.log(match(/\s+/g, 'helloworld')); // => null, two arguments are required

/ / curry

const match = _.curry((reg, str) => str.match(reg));

const haveSpace = match(/\s+/g);

console.log(haveSpace('helloworld')) // => null, through the Currization, only need to pass 1 parameter

const filter = _.curry((func, array) = >array.filter(func));

const findSpace = filter(haveSpace);

console.log(findSpace(['helloworld'.'John Mary'.'Lebron James'])); // => ['John Mary', 'Lebron James']
Copy the code

Simulation implementation of lodash curryization function curry

Here’s what Curry did. First, Curry takes an argument, which is a function (let’s say func), and returns function. If function takes more than or equal to the number of arguments (parameters) defined by func, it simply executes func and returns the result. If the number of arguments received by function is less than the number of arguments defined by func, then a function is returned and waits to receive the remaining arguments until the number of arguments passed in before + the number of arguments passed later = the number of arguments in func, then func is executed and returns the result.

function curry(func) {
    returnfunction curried(... args) {if (args.length < func.length) {
            return function() {
                return curried(...args.concat(Array.from(arguments)));
            }
        }
        return func(...args);
    }
}
const match = curry((reg, str) => str.match(reg));
// Determine if the string contains Spaces
const haveSpace = match(/\s+/g);

console.log(haveSpace('helloworld')) // => null
Copy the code

Function composition

  • Functions are like conduits of data, and function composition can combine these functions and output the final value
  • Function composition is executed from right to left
  • The combination of functions should satisfy the associative law f((a, b), c) ==f(a, (b, c))

Pure functions and currization are easy to write onion code, and what onion code is, is a layer of functions nested on top of each other, one layer on top of the other. For example

  • Gets the last element of the array and uppercase it_.toUpper(_.first(_.reverse(array))). Flipping the array, taking the first element of the array, converting it to uppercase, layer by layer, that’s the Onion code. By combining functions, you can avoid writing onion code. Function composition is the ability to recombine fine-grained functions to generate a new function.

The pipe

A, after being processed by the fn function, becomes B. So this f sub n can be understood as a pipe. However, when the pipe is too long, it can be difficult to locate the problem if the processing error occurs. For example, if the water pipe in your home is too long and leaks, you can’t quickly locate the leak. The same goes for debugging code. Would you rather debug 10, 000 lines of code, or 10, 100 lines? Function composition allows you to break up long code, break it up into smaller functions and then link them together so that when something goes wrong with the code, it’s easier to debug. Let’s use a combination of functions to implement the above example: uppercase the first element of the array.

// Function combination
function compose(f, g) {
    return function(value) {
        return f(g(value))
    };
}

// Flip the array
function reverse(array) {
    return array.reverse();
}

// Get the first element
function first(array) {
    return array[0]}const last = compose(first, reverse);

console.log(last(['a'.'b'.'c'.'d'])); // => D
Copy the code

This is just a very simple case, but we can actually combine functions, combine more functions, and achieve more complex functions.

Combinatorial functions in Lodash

Next, we use loDash to combine functions to uppercase the first element of the array

_.flowRight()

_.flowRight()

const _ = require('lodash');

const reverse = arr => arr.reverse();

const first = arr => arr[0];

const toUpper = str => str.toUpperCase();

const fn = _.flowRight(toUpper, first, reverse);

console.log(fn(['a'.'b'.'c'.'d'])); // => D

// Associative

const fn1 = _.flowRight(_.flowRight(toUpper, first), reverse);
const fn2 = _.flowRight(toUpper, _.flowRight(first, reverse));

console.log(fn1(['a'.'b'.'c'.'d'])); // => D
console.log(fn2(['a'.'b'.'c'.'d'])); // => D
Copy the code

Simulate the implementation of flowRight in LoDash

Define a function, pass in the functions we need to combine, and return a function. The return value of this returned function is what we want. And then executed in sequence by accumulator function reduce incoming function, is due to the reduce will decrease the values in the array from left to right, and calculate the final value, and function composition from right to left, so we need to do a reverse the incoming parameters, then introduced two parameters in the reduce, the first parameter is a function, This function takes two arguments: the first is the value after each processing, the second is each function in args, and the second argument to reduce is the value we need to process.

// Simulate the implementation of flowRight in LoDash

const reverse = arr => arr.reverse();

const first = arr => arr[0];

const toUpper = str => str.toUpperCase();

// function compose(... Args) {// Because the number of parameters passed in the function combination is not fixed, so we use es6... To expand the input parameter
// return function(value) {
// return args.reverse().reduce(function(acc, fn) {
// return fn(acc)
// }, value)
/ /}
// }
constcompose = (... args) => (value => args.reverse().reduce((acc, fn) => fn(acc), value));/ / es6 writing

const fn = compose(toUpper, first, reverse);

console.log(fn(['a'.'b'.'c'.'d'])); // => D
Copy the code

This article summarizes the hook education – big front-end high salary training camp (PS: Class let me add ~)