In mathematics, A function is A set-to-set mapping, that is, the elements in set A, after processing, are mapped to elements in set B. It can be expressed simply as follows:
f(A)->B
Functional programming, as the name suggests, is to program in a functional way. The origin of functional programming can be traced back to the lambda calculus in the early 20th century. In the λ calculus, functions take only one argument, and in order to implement multiple arguments, there were functions called Currying, which are essentially syntactic candy for single-argument functions.
The so-called Corrification can be understood as converting a multi-parameter function into a single-parameter function chain, which is simply expressed as follows:
f(X1, X2, … , Xn)->->g(X1)(X2)… (Xn)
Currification is not often used in daily front-end development, but it is often asked in interviews, and I suspect that the person who originally asked this question was testing the candidate’s understanding of functional programming.
Here’s a classic interview question to start with:
Implement the addition function add() so that it satisfies:
add(1.2.3); / / 6
add(1.2) (3); / / 6
add(1) (2) (3); / / 6
Copy the code
First, to implement chained calls, the return value of add is, of course, a function; Second, because the same addition is being called continuously here, it’s easy to think of recursion calling itself.
Let’s start with a simple version:
let sum = 0;
function add() {
sum += Array.from(arguments).reduce((x, y) = > x + y);
add.toString = () = > sum; // console.log is actually called function.tostring ()
return add;
}
Copy the code
This is fine, but you need to manually reset sum to zero every time you call it, otherwise sum will be contaminated by the previous calculation.
It’s easy to imagine using closures to solve this problem, implementing a purely functional version of add() :
function add() {
// The arguments are summed and saved to total the first time they are called
let total = Array.from(arguments).reduce((x, y) = > x + y);
// Use the closure feature to save total
const _add = function () { // Note that arrow functions cannot be written here
total += Array.from(arguments).reduce((x, y) = > x + y);
return _add;
}
_add.toString = () = > total;
return _add;
}
Copy the code
As an extra note, don’t go looking for something with a hammer in your hand:
Corrified functions can be used in any programming language that supports closures; However, non-Coriolization functions are usually preferred for efficiency reasons, since most function calls can avoid some of the overhead of application and closure creation.
— Wikipedia
The add() function is not elegant enough. Reduce is called twice. We can actually collect all the arguments and call it once:
function add() {
// Save the parameters to an array
const arr = Array.from(arguments);
// Use the closure feature to save total
const _add = function () { // Note that arrow functions cannot be written herearr.push(... Array.from(arguments));
return _add;
}
_add.toString = () = > Array.from(arr).reduce((x, y) = > x + y);
return _add;
}
Copy the code
Let’s take another layer of abstraction and convert an ordinary function into a function of a Corrified function:
function fnToCurry(fn) {
const curry = function () {
// The first time it is called, the arguments are stored in the array
const arr = Array.from(arguments);
// Use the closure feature to save total
const _fn = function () { // Note that arrow functions cannot be written herearr.push(... Array.from(arguments));
return _fn;
}
_fn.toString = () = >fn(... arr);return _fn;
}
return curry; // Finally return the currified function
}
Copy the code
Usage Examples:
function f1() {
return Array.from(arguments).reduce((x, y) = > x + y);
}
// Kerrize f1
const f2 = fnToCurry(f1);
console.log(f2(1.2.3)); / / 6
console.log(f2(1) (2) (3)); / / 6
Copy the code
The above currified function will execute immediately when called. If we want the currified function not to execute immediately, for example, only if the parameter we pass contains “exec”, we can write:
function fnToDelayCurry(fn) {
const curry = function () {
const arr = Array.from(arguments)
// Use the closure feature to save total
const _fn = function () { // Note that arrow functions cannot be written here
// Execute immediately when the input parameter has exec flag
// Check whether the passed parameter is executed
// The parameter containing exec will not be saved in the ARR. To save exec, move the push in else to the outer layer
if (Array.from(arguments).indexOf("exec") > -1) {
_fn.toString = () = >fn(... arr); }else {
_fn.toString = Function.toString; arr.push(... Array.from(arguments));
}
return _fn;
}
return _fn;
}
return curry;
}
Copy the code
Usage Examples:
function delayFn() {
const args = Array.from(arguments);
return args.join("~");
}
console.log(fnToDelayCurry(delayFn)(1) (2) (3) (4.5)); // Print the function _fn
console.log(fnToDelayCurry(delayFn)(1) (2) (3) (4.5) ("exec")); / / 1 ~ 2 ~ 3 ~ 4 ~ 5
Copy the code
The above Demo code is here: jsrun.net/ciUKp/edit
References:
- Corey [wiki] : en.wikipedia.org/wiki/Curryi…
- Closure: developer.mozilla.org/zh-CN/docs/…
- The origin of the lambda calculus – functional language zhuanlan.zhihu.com/p/164700404
The articles
Modern package management tool: PNPM
Hardcore! First bullet: UpdateNotifier
What’s it like to go from front to back
When a programmer meets a product manager who can write code……
Hand write a Webpack Plugin
Hand-write a Webpack loader
This pot I carry……
ES2021 new features
Beat Magic with Magic: front-end code normalization
Hand to hand to teach you to build scaffolding
Build NPM private library by hand
requestAnimationFrame