Recursive call of anonymous functions in JavaScript and javascript Recursion

Source: Internet
Author: User
Tags intel core i7

Recursive call of anonymous functions in JavaScript and javascript Recursion

No matter what programming language, I believe that those who have written a few lines of code will not be unfamiliar with recursion. Take a simple factorial calculation as an example:

function factorial(n) {   if (n <= 1) {    return 1;  } else {    return n * factorial(n-1);  }}

We can see that recursion refers to calling itself within a function. So the question is, we know that in Javascript, there is a type of function called anonymous function and there is no name. How can we call it? You can assign an anonymous function to a constant:

const factorial = function(n){    if (n <= 1) {    return 1;  } else {    return n * factorial(n-1);  }}

Of course this is acceptable. However, when you write a function that does not know that you want to assign a value to a specific variable, you may be in trouble. For example:

(Function (f) {f (10) ;}) (function (n) {if (n <= 1) {return 1 ;} else {return n * factorial (n-1); // too dependent on the context variable name}) // Uncaught ReferenceError: factorial is not defined (...)

So there is no such method for giving accurate function names (function reference variable names?

arguments.callee

We know that any function can access a variable named arguments.

(function(){console.dir(arguments)})(1,2)

Screenshot

Print out the details of the arguments variable. We can see that it is an instance of Arguments, and it is a class array in terms of data structure. In addition to the element member and length attribute of the class array, the callee method also exists. So what is the callee method? Let's take a look at MDN.

Callee is the attribute of the arguments object. In the function body of the function, it can point to the function currently being executed. This is useful when a function is an anonymous function, for example, a function expression without a name (also called an anonymous function ").

Haha, obviously this is what we want. Next:

(function(f){  console.log(f(10));})(function(n){   if (n <= 1) {    return 1;  } else {    return n * arguments.callee(n-1);  }})//output: 3628800

But there is another problem, which is clearly stated in the MDN document.

Warning arguments. callee () is not allowed in strict mode of ECMAScript Fifth Edition (ES5 ().

Oh, we didn't use it in ES5's use strict;, so in ES6, let's change the arrow function of ES6 and write it as follows:

((f) => console.log(f(10)))(  (n) => n <= 1? 1: arguments.callee(n-1))//Uncaught ReferenceError: arguments is not defined(…)

If you have some knowledge about ES6, you may have long wanted to say that the arrow function is a short function expression, it also has the this value of the lexical scope (that is, it will not generate objects under its own scope such as this, arguments, super and new.tar get), and is anonymous.

What should we do? Hey, we need to use the idea of FP.

Y combination child

There are countless articles about Y Combinator, Haskell B, a well-known logologist Based on Hilbert. curry (the Haskell language is named after him, and the Curry method in functional programming language is also named after him) the composite operator invented (Haskell is studying combinatory logic) seems to have a magical magic. It can calculate the fixed point of a given lambda expression (function. This makes recursion possible.

Here we need to inform you of a conceptFixed Point combination child:

Fixed point combinator (English: Fixed-point combinator, or Fixed point operator) is a higher-order function for calculating the Fixed point of other functions.

The fixed point of function f is a value x, which makes f (x) = x. For example, 0 and 1 are the fixed points of the function f (x) = x ^ 2, because 0 ^ 2 = 0 and 1 ^ 2 = 1. Given that the fixed point of a first-order function (a function on a simple value such as an integer) is a first-order value, the fixed point of a higher-order function f is another function g that makes f (g) = g. Then, the fixed point operator is any function fix so that any function f has

F (fix (f) = fix (f). The fixed point combination sub-allows anonymous recursive functions to be defined. They can be defined using non-recursive lambda abstraction.

The (probably the simplest) fixed point combination in the non-type lambda algorithm is called the Y combination.

Next, we will push down the Y combination child through a certain number of computations.

// First, we define a recursive function const fact = (n) => n <= 1? 1: n * fact (n-1) console. log (fact (5) // 120 // since this function is not named, we will first give this recursive method a code called self // first, a high-level function const fact_gen = (self) => (n) that accepts this recursive function as a parameter) => n <= 1? 1: n * self (n-1) console. log (fact_gen (fact) (5) // 120 // we pass the recursive method and parameter n to the recursive method to obtain the const fact1 = (self, n) => n <= 1? 1: n * self (self, n-1) console. log (fact1 (fact1, 5) // 120 // describe fact1 to obtain fact2const fact2 = (self) => (n) => n <= 1? 1: n * self (self) (n-1) console. log (fact2 (fact2) (5) // 120 // The pleasant surprise happened. If we convert self (self) as a whole // pass in a new function as a parameter: (g) => n <= 1? 1: n * g (n-1) const fact3 = (self) => (n) => (g) => n <= 1? 1: n * g (n-1) (self) console. log (fact3 (fact3) (5) // 120 // another problem with fact3 is that the new extracted function is context-related // It depends on n, so we use n as the new parameter // re-construct such a function: (g) => (m) => m <= 1? 1: m * g (S-1) const fact4 = (self) => (n) => (g) => (m) => m <= 1? 1: m * g (S-1) (self) (n) console. log (fact4 (fact4) (5) // obviously (g) => (m) => m <= 1? 1: m * g (S-1) is fact_gen // This is very interesting. The fact_gen context is irrelevant. You can use it as a parameter to pass in const weirdFunc = (func_gen) => (self) => (n) => func_gen (self) (n) console. log (weirdFunc (fact_gen) (5 )) // 120 // at this time, we get a form of Y combination sub-const Y _ = (gen) => (f) => (n) => gen (f) (n) // It is easy to construct a factorial recursion const factorial = Y _ (fact_gen) console. log (factorial) (5) // 120 // but the above factorial is not what we want. // It is just a fact2, fact3, fact4 format // We certainly hope that the call of this function is factorial (5) // no problem, we just need to define a F' = f (f) = (f) => f (f) // eg. const factorial = fact2 (fact2) const Y = gen => n => (f => f (f) (gen) (n) console. log (Y (fact2) (5) // 120 console. log (Y (fact3) (5) // 120 console. log (Y (fact4) (5) // 120

Now, do I feel that my backbones are getting cooler? I was the first time I came into contact with the article "Conway, Godel, And Turing-the eternal golden diagonal, the whole person was instantly impressed by this mathematical language to represent the program.

Let's recall whether we finally get a non-fixed-point operator. This operator can find the fixed point f (Y (f) = Y (f) of a higher-order function ). Pass a function into an operator (function) to obtain a function that is the same as its own function, but is not its own function. This statement is a bit tricky but full of flavor.

Now, let's go back to the initial question: how can we implement recursion of anonymous functions? With the Y combination, it is easy:

(f => f(f))(fact => n => n <= 1 ? 1 : n * fact(fact)(n - 1)) (5)// 120

I have seen some saying that "the most frustrating thing is that when you export it (Y combination, there is no way to say what it really wants by just looking at it. "I think this is the charm of functional programming, the charm of mathematics, and the streamlined and elegant formula, there is a complex and interesting derivation process hidden behind it.

Summary

Practically speaking, recursive calls to anonymous functions are rarely used in daily js development. I want to explain this question mainly to introduce some explanations of arguments and to popularize the concept of Y combination sub-statement.

But since we have already talked about it, how can we choose it if we really use it? Next, let's test the benchmark:

// fact fact(10) // Y(f => f(f))(fact => n => n <= 1 ? 1 : n * fact(fact)(n - 1))(10)// Y'const fix = (f) => f(f) const ygen = fix(fact2) ygen(10) // callee(function(n) {n<=1?1:n*arguments.callee(n-1)})(10)

Environment: Macbook pro (2.5 GHz Intel Core i7), node-5.0.0 (V8: 4.6.85.28) Result:

fact x 18,604,101 ops/sec ±2.22% (88 runs sampled)Y x 2,799,791 ops/sec ±1.03% (87 runs sampled)Y' x 3,678,654 ops/sec ±1.57% (77 runs sampled)callee x 2,632,864 ops/sec ±0.99% (81 runs sampled)

It can be seen that Y and callee have similar performance. Due to the need to build functions temporarily, there is an order of magnitude difference between them and direct fact recursive calls. After calculating the function, save it, performance will be improved about twice.

The above is all the content of this article. I hope this article will help you in your study or work. I also hope to provide more support to the customer's home!

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.