Home  >  Article  >  Web Front-end  >  Compose implementation in JavaScript functional programming

Compose implementation in JavaScript functional programming

黄舟
黄舟Original
2017-03-02 14:49:291723browse

The previous article introduced the implementation of curry (currying) in javascriptfunctional programming. Of course, that currying has limited parameters. Currying, the kind of currying that allows you to add infinite parameters when you have the opportunity. This time I am mainly talking about javascriptanother very important function in functional programmingcompose , compose The function of the function is to combine functions, execute functions in series, and combine multiple functions. The output result of one function is the input parameter of another function. Once the first function starts to execute, It will be deduced and executed like dominoes.

Introduction

For example, if you have such a requirement, you need to enter a name. This name is composed of firstName, lastName, and then put this Change all names to uppercase and output them. For example, if you enter jack, smith, we will print it out, 'HELLO, JACK SMITH'.

We consider using function combination to solve this problem, which requires two functions greeting, toUpper

var greeting = (firstName, lastName) => 'hello, ' + firstName + ' ' + lastName
var toUpper = str => str.toUpperCase()
var fn = compose(toUpper, greeting)
console.log(fn('jack', 'smith'))
// ‘HELLO,JACK SMITH’

This is the rough use of compose , to summarize, the following points should be noted:

  • The parameters of compose are functions, and what is returned is also a function

  • Because except for the parameters accepted by the first function, the parameters accepted by other functions are the return values ​​of the previous function, so the parameters of the initial function are multiple, while the accepted values ​​​​of other functions are The

  • compsoe function of one yuan can accept any parameters. All parameters are functions, and the execution direction is from right to left, the initial function must be placed on the rightmost side of the parameter

After knowing these three points, it is easy to analyze the execution process of the previous example. , when executing fn('jack', 'smith'), the initial function is greeting, the execution result is passed as a parameter to toUpper, and then executed toUpper, to get the final result, let me briefly mention the benefits of compose. If you want to add a processing function, you don’t need to modify fn, you only need to execute a compose, for example, if we want to add another trim, we only need to do

var trim = str => str.trim()
var newFn = compose(trim, fn)
console.log(newFn('jack', 'smith'))

. It can be seen that it is very convenient for maintenance and expansion.

Implementation

After analyzing the examples, in line with the fundamental principle, we still need to explore how compose is implemented. First, I will explain how I Implemented, and then explore how the two major libraries of javascript functional programming, lodash.js and ramda.js are implemented, among whichramda.jsThe implementation process is very functional.

My implementation

My idea is that since the function executes like a domino, I first thought of recursion. Let’s implement this step by stepcompose , First, compose returns a function. In order to record the execution of the recursion, the length of the parameters len must also be recorded, and a name must be added to the returned function f1.

var compose = function(...args) {
    var len = args.length
    return function f1() {

    }
}

What needs to be done in the function body is to continuously execute the functions in args, and use the execution result of the previous function as the input parameter of the next execution function, which requires a cursorcount to record the execution of the args function list.

var compose = function(...args) {
    var len = args.length
    var count = len - 1
    var result
    return function f1(...args1) {
        result = args[count].apply(this, args1)
        count--
        return f1.call(null, result)
    }
}

This is the idea. Of course, this is not possible. There is no exit condition. The recursive exit condition is when the last function is executed, that is, count is 0, at this time, one thing to note is that when the recursion exits, the count cursor must return to the initial state, and finally add the code

var compose = function(...args) {
        var len = args.length
        var count = len - 1
        var result
        return function f1(...args1) {
            result = args[count].apply(this, args1)
            if (count <= 0) {
                count = len - 1
                return result
            } else {
                count--
                return f1.call(null, result)
            }
        }
    }

This will achieve this compose function. Later, I discovered that recursion can be achieved using iteration. It seems easier to understand using the while function. In fact, lodash.js is implemented in this way.

lodashThe idea of ​​implementing

lodash is the same as above, but it is implemented iteratively. I will post its source code to take a look

var flow = function(funcs) {
    var length = funcs.length
    var index = length
    while (index--) {
        if (typeof funcs[index] !== &#39;function&#39;) {
            throw new TypeError(&#39;Expected a function&#39;);
        }
    }
    return function(...args) {
        var index = 0
        var result = length ? funcs[index].apply(this, args) : args[0]
        while (++index < length) {
            result = funcs[index].call(this, result)
        }
        return result
    }
}
var flowRight = function(funcs) {
    return flow(funcs.reverse())
}

It can be seen that the original implementation of lodash is from left to right, but it also provides from right to leftflowRight, there is an additional layer of function verification, and what is received is an array, not a parameter sequence, and from this linevar result = length? funcs[index].apply(this, args): args[0]It can be seen that the array is allowed to be empty, and it can be seen that it is still very strict. What I wrote lacked this rigorous exception handling.

Conclusion

This time I mainly introduce the principle and implementation method of the compose function in functional programming. Due to space reasons, I will analyze the ramda The .js source code implementation will be introduced in the next article. It can be said that the compose implemented by ramda.js is more functional and needs to be analyzed separately.

The above is the content implemented by compose in JavaScript functional programming. For more related content, please pay attention to the PHP Chinese website (www.php.cn)!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn