Pure function
Concept of pure function
Pure function: the same input will always get the same output without any observable side effects
Pure functions are similar to functions in Mathematics (used to describe the relationship between input and output), y = f(x):
lodash is a pure function library, which provides some methods for array, number, object, string, function and other operations. slice and splice of array are pure function and impure function respectively
- slice returns the specified part of the array without changing the original array
- splice operates on an array and returns the array, which will change the original array
let numbers = [1, 2, 3, 4, 5]; // Pure function numbers.slice(0, 3); // => [1, 2, 3] numbers.slice(0, 3); // => [1, 2, 3] numbers.slice(0, 3); // => [1, 2, 3] // Impure function numbers.splice(0, 3); // => [1, 2, 3] numbers.splice(0, 3); // => [4, 5] numbers.splice(0, 3); // => []
Functional programming does not retain the intermediate results of calculation, so the variables are immutable (stateless). We can hand over the execution results of one function to another function for processing
Benefits of pure functions
- Cacheable. Because pure functions always have the same results for the same input, the results of pure functions can be cached: const_= require("lodash"); Function getarea (R) {console.log ("executed"); return Math.PI * r * r;}let getAreaWithMemory = _.memoize(getArea);console.log(getAreaWithMemory(4)); // The content console log(getAreaWithMemory(4)); // Console is not printed log(getAreaWithMemory(4)); // Simulate a memo function without printing: function memo (f) {let cache = {}; return function () {let arg_str = JSON.stringify(arguments);cache[arg_str] = cache[arg_str] || f.apply(f, arguments);return cache[arg_str];};} * Testable. Pure functions make testing easier
- Convenient parallel processing * in a multi-threaded environment, parallel operation of shared memory data may cause unexpected situations, while pure functions do not need to access shared memory data, so pure functions (web workers) can be run arbitrarily in a parallel environment
side effect
Pure function: for the same input, you will always get the same output without any observable side effects
// Impure (when you enter 20, will it always return true? The answer is No. when the global variable mini changes, the return value will change.) let mini = 18; function checkAge(age) {return age >= mini; } // Pure (hard coded, i.e. 18 is fixed and can be solved by coriolism later) function checkAge(age) {let mini = 18;return age >= mini; }
Side effects make a function impure (as in the above example). Pure functions return the same output according to the same input. If the function depends on the external state, the output cannot be guaranteed to be the same, which will bring side effects.
Source of side effects:
- configuration file
- database
- Get user input
- ...
All external interactions may bring side effects, which also reduce the universality of the method and make it unsuitable for expansion and reusability. At the same time, the side effects may bring security risks to the program and uncertainty to the program. However, the side effects cannot be completely prohibited, and they can be controlled within a controllable range as much as possible.
Haskell Brooks curry
Currying:
When a function has multiple parameters, first pass some parameters to call it (these parameters will never change in the future), then return a new function to receive the remaining parameters and return the result
Use coriolism to solve the hard coding problem in the previous case
function checkAge(age) {let min = 18;return age >= min; } // Ordinary pure function, change min to parameter function checkAge(min, age) {return age >= min; } checkAge(18, 24); checkAge(18, 20); checkAge(20, 30); // currying function checkAge(min) {return function (age) {return age >= min;}; } // ES6 writing method let checkAge = (min) => (age) => age >= min; let checkAge18 = checkAge(18); // You can see that 18 can be reused let checkAge20 = checkAge(20); checkAge18(24); checkAge18(20); checkAge20(30);
Coriolis function in lodash
The concept of coriolism has been known here. The coriolism case in the previous section is not very general. Let's introduce the general Coriolis in lodash
_.curry(func)
- Function: create a function that receives the parameters of one or more FuncS. If all the parameters required by func are provided, execute func and return the execution result. Otherwise, continue to return to the function and wait to receive the remaining parameters.
- Parameter: function requiring coriolism
- Return value: function after coriarization
The curry method in lodash basically uses:
const _ = require("lodash"); // Functions to be coriarized function getSum(a, b, c) {return a + b + c; } // Function after coriolism let curried = _.curry(getSum); // test curried(1, 2, 3); // After passing all the parameters, the execution result is directly returned curried(1)(2)(3); // The first time a parameter is passed, the function returns and waits to receive the remaining parameters curried(1, 2)(3);
Coriolis case
Scenario: you can use the match method to determine whether there are white space characters in a string or to eliminate the white space characters.
// Parameter reuse const _ = require("lodash"); const match = _.curry(function (reg, str) {return str.match(reg); }); const haveSpace = match(/\s+/g); const haveNumber = match(/\d+/g); console.log(haveSpace("hello world")); console.log(haveNumber("25$")); const filter = _.curry(function (func, array) {return array.filter(func); }); console.log(filter(haveSpace, ["John Connor", "John_Donne"])); const findSpace = filter(haveSpace); console.log(findSpace(["John Connor", "John_Donne"]));
Coriolis principle simulation -- simulation _ Implementation of curry()
Core: pass in a function and return a function after coritization. For the Coriolis function, first judge the number of parameters passed in. If all parameters are passed in, the execution result will be returned. Otherwise, the Coriolis function will be returned.
- call
function curry(func) {return function curriedFn(...args) {// Determine the number of arguments and formal parameters if (args. Length < func. Length) {return function() {return currentfn (... Args. Concat (array. From (arguments));};}// The number of arguments and formal parameters are the same. Call func and return the result return func(...args);}; }
for instance
// sum(1, 2)(3).sumOf(); //6 // sum(1)(2)(3).sumOf(); //6 // sum(1, 2, 3).sumOf(); //6 function sum() {const args = Array.prototype.slice.call(arguments);const add = function () {args.push(...arguments);return add;};add.sumOf = () => args.reduce((a, b) => a + b);return add; }
Coriolism summary
- Coriolism allows us to pass fewer parameters to a function to get a new function that has memorized some fixed parameters
- This is a 'cache' of function parameters
- Make the function more flexible and make the granularity of the function smaller
- It can convert multivariate functions into unary functions, and can combine functions to generate powerful functions