Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

Today in the little Red Book turned to a noun: function corrification.

explain

An official explanation was found:

Currying, English: Currying, is a technique that converts a function that takes multiple arguments into a function that takes a single argument (the first argument of the original function), and returns a new function that takes the remaining arguments and returns a result.

The basic approach to function curryification is the same as function binding: use a closure to return a function. The difference is that when a function is called, the returned function also needs to set some of the parameters passed in.

A currie function is typically created dynamically by calling another function and passing it the function to be currie and the necessary arguments.Copy the code
// The normal add function
function add(x, y) {
    return x + y
}

/ / after Currying
function curryingAdd(x) {
    return function (y) {
        return x + y
    }
}

add(1.2)           / / 3
curryingAdd(1) (2)   / / 3

Copy the code

Instead of taking the x and y arguments to add, you have a function that takes x and then returns a function that handles the y argument. You call the function by passing it only one of the arguments and then having it return a function that handles the rest.

It takes a long time. Why do you do that?

The benefits of currization

Parameters of reuse

Normal re validation string

// The function is wrapped
function check(reg, txt) {
    return reg.test(txt)
}

check(/\d+/g.'test')       //false
check(/[a-z]+/g.'test')    //true

/ / after Currying

Copy the code

Currization validation

function curryingCheck(reg) {
    return function(txt) {
        return reg.test(txt)
    }
}

const hasNumber = curryingCheck(/\d+/g);
const hasLetter = curryingCheck(/[a-z]+/g);

hasNumber('test1')      // true
hasNumber('testtest')   // false
hasLetter('21212')      // false
Copy the code

Normally, you can just call the check function, but if you need to check for numbers in many places, you actually need to reuse the first parameter reg, so that you can call hasNumber, hasLetter, etc., so that the arguments can be reused, so that it is easier to call.

(Other benefits, I haven’t figured out so far o(╥﹏╥)o)

Currize the general packaging method

// Initial encapsulation
var currying = function(fn) {
    // args gets all the arguments in the first method
    var args = Array.prototype.slice.call(arguments.1)
    return function() {
        // merge all the parameters in the following method with args
        var newArgs = args.concat(Array.prototype.slice.call(arguments))
        // apply the merged parameters as fn parameters and execute them
        return fn.apply(this, newArgs)
    }
}
Copy the code

The first is a preliminary wrapper, storing the initial arguments in a closure, then concatenating them by taking the remaining arguments, and finally executing functions that require currying.

If there are multiple parameters, we need to add recursion and encapsulate another layer:

// Support multiple parameter passing
function progressCurrying(fn, args) {

    var _this = this
    var len = fn.length;
    var args = args || [];

    return function() {
        var _args = Array.prototype.slice.call(arguments);
        Array.prototype.push.apply(args, _args);

        // If the number of arguments is less than the original fn.length, the recursive call continues to collect arguments
        if (_args.length < len) {
            return progressCurrying.call(_this, fn, _args);
        }

        // After parameters are collected, run fn
        return fn.apply(this, _args); }}Copy the code

The performance of the curry

  • Accessing arguments objects is usually slower than accessing named arguments;

  • Some older browsers are quite slow to implement arguments.length;

  • Use fn.apply(…) And fn. Call (…). It’s usually better than calling fn(…) directly. A little slower;

  • Creating lots of nested scopes and closures costs money, both in memory and speed;

The interview questions

Implement an add method that satisfies the following expectations: add(1) (2) (3) = 6;
add(1.2.3) (4) = 10;
add(1) (2) (3) (4) (5) = 15;
Copy the code

Reference answer:

function add() {
    // On the first execution, an array is defined to store all the parameters
    let _args = Array.prototype.slice.call(arguments);
    // Internally declare a function that uses closure properties to hold _args and collect all parameter values
    let sumNum = function() { _args.push(... arguments);return sumNum;
    };
    // Take advantage of the toString implicit conversion feature to implicitly convert when finally executed and calculate the final value returned
    sumNum.toString = function () {
        return _args.reduce(function (a, b) {
            return a + b;
        });
    }
    return sumNum;
}
Copy the code