The use of a function called Currying

Series of the opening

Establish clear, precise, essential concepts and clear, precise, necessary connections between them as you enter the front end, so that you can stay calm in whatever interview you’re going to be doing. There is no catalog, but a web of knowledge formed through the association of concepts, as you can see below. When you encounter a relation concept, you can use the parentheses (strong/weak) to determine whether the relation is strongly related to the concept you are understanding (you need to understand before you can proceed) or weakly related (knowledge expansion) to improve your reading efficiency. I also update related concepts regularly.

The interview questions

  • What is Corrification
  • What are the application scenarios of Cremation
  • Do you know anything about functional programming
  • Implement a curring

What’s this for?

Take a look at the various definitions quoted by other officials:

Mostly adequate Guide: Currying — a functional way of calling a function by passing it only a few arguments and making it return a function that handles the rest.

Kristina Brainwave: Currization is the process of converting functions that have more arity(the number of arguments) into functions that have fewer arity.

For example: it means converting a function from callable f(a, b, c) to callable f(a)(b)(c). Cremation does not call a function, it just transforms the function.

Let’s start with a quick summary

  • Corrification is a kind ofThe technique of functional programming.
  • It’s only passed to functionsPart of the argument to callIt andReturn a functionGo ahead and process the rest of the arguments.
  • It is used not only in JavaScript but also in other programming languages.

Analysis to understand

Above we have a definition, but I guess you still don’t understand this thing, see the analysis you will understand.

A function call in javascript looks like this:

let add = function(a, b) { 
  return a + b 
}
add(1.2)                 / / = 3
Copy the code

A function takes a number of arguments and returns a value after execution. When we pass too few or too many arguments (different from the number we defined anyway), it results in undesirable results (too few) or the extra arguments being ignored.

add(1)                    //= NaN
add(1.2.'ignore args')  / / = 3
Copy the code

Let’s use Curry’s method to turn multi-argument addition into a single argument function

var curry = require('curry')  // Assume that the curry method is imported externally
// We currified this function by passing it to curry() and returning a curried function, sum3
var sum3 = curry(function(a, b, c) { 
  return a + b + c
})
// Then we can call them like this
sum3(1.2.3)      //= 6
sum3(1) (2) (3)      //= 6 can be called argument by argument
sum3(1) (2.3)      / / = 6
sum3(1.2) (3)      //= 6 can also pass in part, and then pass in the rest
Copy the code

Sum3 (1)(2, 3) is called partial or partial Application.

This means taking a function and applying one or more, but not all, arguments to it, creating a new function in the process.

What’s the benefit of that? What scenarios are available

Let’s talk about the benefits in general before we explain

In a word: make functions more readable, flexible, and reusable.

Other potential benefits:

  • Allows you to generate a small, easily configurable library of functions that behave consistently. (Pure function with no side effects)
  • It gives you a good habit of naming functions.

Here are some examples:

1. Parameter multiplexing, forming some partial functions, flexible application

For example, we have a function log(level, message) that formats and outputs logs of information. Let’s say it looks like this

  • Level Sets the log warning level'warn', 'error', 'info'Etc.
  • Message Indicates the log content
function log(level, message) {
  console.log(` [${level}] ${message}`);
}
Copy the code

A very simple function. Think about how it’s usually called

if (exp) {
  log('warn'.'sth... warn')
}

log('error'.'... message')...Copy the code

Now let’s see what happens

import _ from 'loadsh'
var log = _.curry(log)
Copy the code
// Log is still running after currization:
log("warn"."some warn");  // "[warn] some warn"
// But it can also run in currified form:
log("warn") ("some warn");  // "[warn] some warn"
Copy the code

This way we can create more “convenience functions” or partial functions

let warnLogger = log('warn');

/ / use it
warnLogger("message");      // [warn] message
Copy the code

WarnLogger is a partial function of the log with a fixed first argument, which is part of the original function with a fixed argument.

So now we call the method

if (exp) {
  warnLogger('sth... warn')
}
errLogger('... err message')
Copy the code

Isn’t it much more readable, and more flexible, because you have these little functions that can be combined with each other. Here’s an example where you can’t see the benefits because it’s so simple. Try it on complex projects, and you’ll find that this programming habit will help you think more clearly.

2. Atomize operations for unit testing

To put it simply, the tool functionalizes small operations to increase readability and reuse.

It is also important that these functions are very easy to unit test [correlation concepts].

A lot of people say that front-end unit testing doesn’t work because the logic you’re dealing with is so simple! When your logic is complicated to a certain extent, with strong connections and many branches, and you find a bug a week later, or are asked to add a new feature immediately, do you dare to change (add), and you are sure that the change will not affect the previous function? If the logic division is clear, the function’s responsibility is clear, input and output is certain, first of all, your change, add convenient (extensible, flexible), secondly, you just run through their own unit test, psychology is not panic, change up is also very assured, at least will not affect the previous logic.

An example: Just an example, please draw inferences

var objects = [{ id: 1 }, { id: 2 }, { id: 3 }]
let idList = objects.map(function(item) { 
  return item.id 
})
Copy the code

All we need to do is walk through the array of objects and get their ids

So now we can do a wave with currization

import _ from 'loadsh'

var get = _.curry(function(prop, object) { 
  return object[prop] 
})

// Map accepts a function to get each object's 'prop' where prop is 'id'
objects.map(get('id'))    / / = [1, 2, 3]
Copy the code

The partially configurable function that we actually create in the get function. Let’s see if we usually use this to create a method that gets the ID of an array of objects

let getIDs = function(objects) {
    return objects.map(get('id'))
}
getIDs(objects)        / / = [1, 2, 3]
Copy the code

We can even go a step further and curry map as well

let curriedMap = curry(function(fn, value) { 
  return value.map(fn) 
})
var getIDs = curriedMap(get('id'))

getIDs(objects)        / / = [1, 2, 3]
Copy the code

It’s a lot clearer to read, isn’t it, when you’ve accumulated a lot of atomic manipulation in your day job, like building blocks, you’ll be amazed at how quickly you can solve problems.

Code accumulation is compound interest, don’t underestimate a little bit of accumulation, can start with functions, method utility classes, build your own toolset. Take it to the next level, build your own systems and services (open source by others), and when it comes to real problems, you’ll have something in your pocket that surprises others, and your development will be n times faster than others, not because you’re smart, but because you’ve done it before and accumulated it. The master coder, the master of assembling what he has accumulated, the master of solving real problems, is very pragmatic.

How do I actually write it

If you want to use Curry in a real world project, it is recommended that you learn functional programming first and use it naturally. So, Curry also suggests going for lodash (don’t write it yourself, and someone else’s handles this and other special handles, so it works fine, too)

_.curry

Of course, you can also choose to implement one of your own, according to the following principles, simple implementation.

It is the basic quality of a programmer to consider the problem and choose the most appropriate solution according to the reality.

How does it work?

After we understand the concept, to explore its implementation under the (interview) are often asked about the source code, can someone feel futile, I think it is useful for developing the other associated concepts, will know 】 【 can also take a look at your hard coding ability, to assemble to see how your memory is good. (^ – ^)

To understand the implementation principle, it is best to understand the concept of closure [relevance concept (strong)]. This is a prerequisite for understanding the implementation of the following principle.

function myCurry(func) {
  // Our myCurry call should return a wrapper curried to curryize the function
  return function curried(. args) {
    // The use of curry depends on the number of arguments
    return args.length >= func.length ?
      // If the length of the args passed is the same or longer than that defined by the original function (func.length),
      // Then just pass the call to it. Call it now and return the result of the function
      func.call(this. args) :// Otherwise, return another wrapper method, recursively calling Curried, concatenating the previously passed argument with the new one.
      // Then, in a new call, again, we get a new partial function (if the arguments are insufficient), or the final result.
      (. rest) = > {
        return curried.call(this. args, ... rest); }; }; }Copy the code

Note that each time a wrapper function is called with insufficient arguments, the previous round of arguments are saved in the lexical environment, usingclosureFor the next round of judgment.

I think there is no need to explain in the comments section.

You can test it out with this example

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

let curriedSum = myCurry(sum);

console.log( curriedSum(1.2.3));// 6, can still be called normally
console.log( curriedSum(1) (2.3));// 6, currization of the first argument
console.log( curriedSum(1) (2) (3));// 6, full cremation
Copy the code

other

A function that only determines the length of an argument

Currization requires a function to have a fixed number of parameters.

Note functions that use rest arguments, such as f(… Args), cannot be currified in this way.

Why use functional programming style

Pointfree is the answer to how to use functional programming

  • This is called Pointfree: you don’t use the values you’re working with, you just compose the operation. The Chinese can be translated as “no value” style.
  • The essence of Pointfree is to combine complex operations using common functions. The upper level operations do not manipulate the data directly, but process it through the underlying functions. This requires that some common operations be encapsulated as functions.

example

Here is a string, how many characters is the longest word in it?

var str = 'Lorem ipsum dolor sit amet consectetur adipiscing elit';
Copy the code

So let’s define some basic operations.

// Separate words with Spaces
var splitBySpace = s= > s.split(' ');

// The length of each word
var getLength = w= > w.length;

// An array of words is converted to an array of dimensions
var getLengthArr = arr= > R.map(getLength, arr); 

// Return a larger number
var getBiggerNumber = (a, b) = > a > b ? a : b;

// Return the largest number
var findBiggestNumber = 
  arr= > R.reduce(getBiggerNumber, 0, arr);
Copy the code

The basic operation is then synthesized into a function

var getLongestWordLength = R.pipe(
  splitBySpace,
  getLengthArr,
  findBiggestNumber
);

getLongestWordLength(str) / / 11
Copy the code

As you can see, the whole operation consists of three steps, each step has a semantic name, very clear. This is the strength of the Pointfree style.

memoization

In computing, memoization is an optimization technique primarily used to speed up program computations by allowing functions to avoid repeating previously processed inputs and return cached results. — wikipedia

function memoizeFunction(func) {
    var cache = {};
    return function() {
        var key = arguments[0];
        if (cache[key]) {
            return cache[key];
        } else {
            var val = func.apply(this.arguments);
            cache[key] = val;
            returnval; }}; }var fibonacci = memoizeFunction(function(n) {
    return (n === 0 || n === 1)? n : fibonacci(n -1) + fibonacci(n - 2);
});

console.time('start1');
fibonacci(100)
console.timeEnd('start1')
// There is cache for the second time
console.time('start2');
fibonacci(100)
console.timeEnd('start2')
Copy the code

Caching significantly improves computing speed


Keep going and you’ll get something.

The above sentence gives you, as well as me, the motivation to move forward.

I am Moore, majoring in mathematics. I have done Internet research and development, testing, and products. I am committed to changing the lives of others with technology, and changing my own life with dreams. I will try my best to help you, but pay attention to the way of asking questions. It is suggested to read this article first: the wisdom of asking questions

reference

  • www.sitepoint.com/currying-in…
  • medium.com/@kevincenni…
  • Hughfdjackson.com/javascript/…
  • Juejin. Cn/post / 684490…
  • www.ruanyifeng.com/blog/2017/0…
  • Useful. Javascript. The info/currying – pa…