So, we all know recursive functions, right? But what exactly is it, that makes a function recursive? I had a small discussion in the comment section of another question (Lightening effect with javascript) that kind of challenged my view of recursive functions but it also left me with a quite unsatisfying lack of proper definition.
My personal definition of recursive functions so far was the following:
A function is recursive if it directly or indirectly invokes itself
Note: I will provide JavaScript code for the following examples, but I'm sure they are pretty universal.
A simple example for such a recursive function could be this:
function a() {
a();
}
but also this:
function a() {
b();
}
function b() {
a();
}
or even this:
function a() {
setTimeout(a, 1000);
}
None of those functions computes anything but I would have still considered them recursive just because they invoke themselves.
One thing that came up was, that the third example is not recursive because it uses setTimeout and therefor the stack is unwound. It also isn't recursive because after returning from the nth invocation it doesn't give back control to the n-1th invocation.
Another point, that was raised, was that none of those functions were recursive because they all don't compute an actual problem in a recursive way. Meaning that a problem has to be solved by dividing it in smaller and smaller instances. Here the quoted Wikipedia article:
Recursion in computer programming is exemplified when a function is
defined in terms of simpler, often smaller versions of itself. The
solution to the problem is then devised by combining the solutions
obtained from the simpler versions of the problem.
So this is recursive:
function fac(n) {
if (n <= 0) {
return 1;
}
return n * fac(n - 1);
}
But this wouldn't be:
function fac(n) {
if (n <= 0) {
return 1;
}
console.log(n);
fac(n - 1);
}
So what is the proper definition of a recursive function? Is there any at all or is it really just a philosophical question? What features must a function have, in order to be classified recursive?
Recursion is simply defining a problem in terms of a simpler case (simpler meaning "closer" to the terminating condition, not necessarily actually simpler) of that problem, down to the point where the simplest case is a known one (the terminating condition alluded to earlier). So, for example, the perennial factorial function has a terminating condition:
f(1) = 1
and the definition of the problem in terms of a simpler one:
f(n) = n * f(n - 1), for n > 1
The best explanation of it that I ever heard was this:
If you're Donald Knuth, you understand what it is.
Otherwise, find someone closer to Donald and ask them.
I wouldn't call the setTimeout one recursion because a is not actually calling itself. Instead, it's asking the "system" to call it at some later date.
It's also important to have the terminating condition in there somewhere. Without that, it's still recursion but it's infinite recursion, no different to infinite loops like:
for (i = 0; i < 10; j++) {}
and hence unlikely to be any good for anything other than testing what happens when your stack overflows :-)
Definition of Recursion? Read this line again until you get it.
(A selfcalling function with an abort criterium to prevent infinite looping).
Recursion is the name that was given to function that calls itself. Now whether the function calls itself infinitely or not.. it is still a Recursion.
It is not necessarily that the problem is divided into sub-problems. However, in computer science; The term Recursion refers to a technique that is used to solve a problem, by breaking the problem into sub-problems and usually the problem is finite.
One more point, Recursion is implemented using Stack. Each function call is piled on top of the other in the stack, until the last call meets the base condition, then the functions in the stack are executed from top to bottom.
However, if there is no base condition or the base condition is never to be satisfied. then infinite calls to the function will be pushed to the stack causing the memory to be filled up and a stackOverFlow exception will be thrown and OS handles it by terminating the program.
In Regard to setTimeout() It is an asynchronous call and is not related to recursion, it is an independent call as the caller function doesn't depend on the called one whether it is the same function being called or another.
From Wikipedia that you have posted:
Recursion in computer programming is exemplified when a function is defined in terms of simpler, often smaller versions of itself. The solution to the problem is then devised by combining the solutions obtained from the simpler versions of the problem.
So. There is a problem, and there is a solution. There is a function that call itself minimizing the main problem. This function is recursive.
Case:
function a() {
a();
}
there is no problem, there is nothing to minimize, there is no solution. It's not recursive for me. It's just an infinite loop.
So another example:
function a(n) {
if(n<.5) {
return n+a(Math.random());
}else {
return n;
}
}
console.log(a(.3));
Is this recursive?
No.
There is maybe a problem, but the solution is not found minimzing the main problem. Simple it call itself randomly until some flag is true. Again, it's a loop.
The same happens with a setTimeout or setInterval (the solution of problem will depend from system call).
Related
I was writing some code for a widget in "Scriptable" that shows random word at a certain time. The widget calls itself with a timeout, on iPad. But it's exceeding the stack size. How do I solve this? I am using Javascript within "Scriptable" framework, so I don't have much freedom.
kanjiObj: Object; kanjiKeys: List; Timer().schedule(timeinterval, repeat, callback)
var randomNum = Math.floor(Math.random()*150)+1
var kanji = kanjiKeys[randomNum]
var kanjiMeaning = kanjiObj[kanjiKeys[randomNum]]
if(config.runsInWidget){
let widget = createWidget()
Script.setWidget(widget)
Script.complete()
function createWidget(){
let widget = new ListWidget()
widget.addText(kanji + "=>" + kanjiMeaning)
widget.wait = new Timer().schedule(1000*60*60, 150, createWidget())
In your case, you are calling the createWidget() function recursively by mistake. Your intention was different, you wanted to pass the function as a parameter to the schedule function, which accepts a function callback as the last parameter.
So you have to change to
widget.wait = new Timer().schedule(1000*60*60, 150, () => createWidget())
or
widget.wait = new Timer().schedule(1000*60*60, 150, createWidget)
But since this question is about "call stack size exceeded" people who have a different kind of this problem could stumble upon it, so I will answer in a general way below as well.
Programming is about recognizing patterns, so what you have in your code is a pattern (structure) of an infinite recursive call
function someFunction() {
// ... code before the recursive call that never returns from this function
// an unconditional call (or a conditional chain that is always true)
// of the function itself in the function scope instead of inside a callback/lambda
someFunction()
// ... could be more code, which does not matter,
// until the pattern above still holds true
}
So a structure (pattern) like this will result in an infinite recursive call, which eventually ends with a maximum call stack size exceeded. Call stack size is limited and an infinite recursive call eventually fills it up.
Javascript does not have built-in tail-recursion optimization (in practice, see compatibility), but in programming languages that do, if the function is tail-recursive, it would be optimized to a loop. In the case of an infinite tail-recursive function it would be optimized to an infinite loop, which would not fill the call stack, it would just loop forever (it would be working, but if not implemented correctly, it could make the application unresponsive).
So to resolve a problem like this in general, one has to break this pattern by breaking any (at least one) of the conditions above. This is the list:
code before the recursive call that never returns from this function
add a conditional return statement (also called a stop condition)
an unconditional call (or a conditional chain that is always true) to the function itself in the function scope instead of inside a callback/lambda
make the call conditional (make sure the condition chain can be false) or put it inside a callback/lambda. When you put it inside a
callback/lambda, then a different pattern applies (you have to check
the logic inside the call that will be calling the callback), but
calling the callback still has to be conditional, it has to have a
limit at some point.
after making a change, the code that is after the call, needs to be checked for the same pattern again, if the pattern is there again, break it in a similar way. Keep repeating this until the whole function does not form the pattern anymore - has stop condition(s) where needed.
In cases when you do need an infinite recursive call and the function is tail-recursive and your language does not support tail-recursion optimization, you would need to do the tail optimization yourself or use a library/framework that lets you do that.
If this does not solve your problem, have a look at the answers in this question that has collected a lot of reasons why a "maximum call stack size exceeded" might happen in Javascript.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I've been doing JS problems on LeetCode and noticed that the recursive solutions run faster than the iterative solutions in almost every case (ex. DFS with a stack vs recursion).
I looked online and found a couple of blog posts that came to similar conclusions around performance.
https://www.c-sharpcorner.com/blogs/performance-of-recursion-vs-loop-using-javascript
The Oreily book, like most formal material on the subject, states that iteration is more performant because of the "lower overhead" which I understand but then why isn't that the case in practice?
https://www.oreilly.com/library/view/high-performance-javascript/9781449382308/ch04.html
I do see that recursion can fail at particularly large stack sizes but it always seems to be more performant than iteration. What's going on? I saw that TCO hasn't been implemented in JS so I'm very confused.
Well, to understand recursion over iteration (or vice versa), we need to understand how both technique works.
What is recursion?
It's function calling it self. so number of calls take place, number of times the properties of function (which includes variables, data-structures, sub-functions that the main function uses, will recreate over and over again). and at the end, compiler or (JIT in case of JavaScript) must face some overheads.
And the process will continue until/unless base case have found, in this case all overheads during recursion will release.
In case, if base-case not found/matched all the stack-frame which was allocated for this particular function will be exhausted, and we will get some thing like: Maximum call stack size exceeded
What is Iteration?
As the name suggest, it's a repetitive instruction which will perform some operation given by the coder.
In compare to Recursion, no other stack-frame will be used without main-stack-frame. and all the properties of the function will be reinitialised with new value over and over again.
Based on the comparison i have made, you can clearly get an idea, which is more and which is less efficient to Compiler or to JIT. (and it is generalised to every language you will work on).
Now coming to the performance, the article link which you provided clearly not showing accurate measurements of all the scenarios. The time to execute a function clearly and mainly depends on the number of instruction the function performs.
Looking at the following snippets you can re-think about the conclusion you have so far:
function nthFibo(a){
if(a <= 2){
return a;
}else{
return nthFibo(a-1)+nthFibo(a-2);
}
}
console.time("looping #1");
console.log(nthFibo(45));
console.timeEnd("looping #1");
and also check the time required for iteration, which will give you same output as recursion gave:
function nthFiboIterative(a){
var first = 0, second = 1;
var result = 0;
var i = 1;
while(i ++ <= a){
result = first + second;
first = second;
second = result;
}
return result;
}
console.time("looping #2");
console.log(nthFiboIterative(45));
console.timeEnd("looping #2");
But this is not a fair test as our first program creates an exponential process that we compare to a linear process in the second program. Let's see a recursive nthFibo that evolves a linear iterative process -
function nthFibo (a, x = 1, y = 1)
{ if (a == 0)
return x;
else
return nthFibo(a - 1, y, x + y)
}
console.time("looping #3");
console.log(nthFibo(45));
console.timeEnd("looping #3");
It's important to know the distinction between process and a procedure (function). A recursive function can evolve a recursive process, an exponential process, or an iterative process. Similarly, a non-recursive function can evolve an iterative process (think simple for loop) or an exponential process (think something like Ackerman function).
In languages such as javascript or (maybe?) lua, all functions by default are treated as if they had a return statement at the end:
function() {
// do
return;
}
Is equal to
function() {
// do
}
I'm wondering if returning from an inner block at the end of the function changes anything in the core, the compiling process, the VM.
function() {
if (condition) {
return;
}
// end of function
}
Same question applies to breaking a loop:
function() {
for ( loop ) {
return;
}
// end of function
}
Does the machine "look" for anything when a loop is broken, or a condition check has ended?
This is not a stylistic question, please don't tell me to make code readable.
TL:DR / optimization advice: you don't need to do anything special to gain performance. if(condition) return inside an inner loop is typically at least as efficient as an if(condition)break; to reach the end of the function.
Putting nested loops inside a function so you can use a return as a multi-level break is a good way of being able to express the logic efficiently without a goto, easy for humans and easy for compilers/interpreters.
Making loop conditions more complicated to avoid multiple return statements in one function is not helpful for performance.
Generally no, a return in the middle of a function is not fundamentally different or more or less efficient than reaching the implicit return at the end. And an explicit return at the bottom of a function isn't special either.
(We're talking about void functions that never return a value, I assume. Obviously returning a value is different from not returning a value.)
Restructuring your code to break out of a loop and reach the implicit return at the bottom of a function is not more efficient (but could easily be less efficient in some interpreted languages, especially if they aren't JITed.) e.g. if the interpreter does a jump within the function and then has to do another jump. (A good ahead-of-time compiler or JIT optimizer could see what was happening and make good machine code anyway.)
Some compilers / interpreters may handle a return by just jumping to a common block of cleanup (epilogue) that all return statements share. But tail-duplication is possible: when compiled to machine code, a function can have multiple copies of epilogue + ret instructions, reachable from different paths.
(JavaScript implementations do typically JIT functions to machine code; IDK about LUA. And of course they can inline functions. return statements in functions that get inlined can be just plain jumps, or could get optimized away entirely.)
I'm not exactly sure whether I correctly understood your question, but I'll try to answer it from my point of view.
The return statement in the end of a function declaration indicates to leave the function and return nothing (void). If you omit a return statement, nothing would happen after the actual function execution. Thus, I think the two functions you declared behave in a different way:
function a() {
// executes until the following statement and then breaks
return;
}
function b() {
// executes all statements and afterwards leaves the context where it was called
}
Regarding your question concerning inner blocks like condition checks or loops, I guess these statements could only be "optimized" by a parser somehow if they consist of static values like numbers or strings. As soon as any dynamic values like variables occur, it would be impossible to optimize anything or have an advantage from the inner result statement.
I hope you can get the point of my explanation.
I would really hear some opinions on which of the following solutions is more efficient in JavaScript/TypeScript:
function whatever(param) {
if (param === x) {
doStuff();
}
}
or
function whatever(param) {
if (param !== x) { return false; }
doStuff();
}
Obviously the second one reduces the number of blocks and improves code readability, but does it offer any benefits or drawbacks compared to the first one?
In assembly you'd write both as:
// first
whatever:
CP x, param
JP nz, whatever2
CALL doStuff
whatever2:
RET
// second:
whatever:
CP x, param
JP z, whatever2
RET
whatever2:
CALL doStuff
RET
So while the first one uses 4 instructions, the second uses 5. So in case the JS engine is able to optimize it down to this optimal bytecode (very unlikely, but it makes estimations easier), the second one will be 1 tick slower, which is less than a nanosecond.
Here is a test where I run the comparison 10000 times.
The difference is pretty much non-existant.
There is no significant difference in efficiency, even if you have more than one of these preconditions in your function. No matter what language you use. Some compilers will probably even generate the same machine instructions from both versions.
Stop thinking about efficiency too much. Think about readability and maintainability!
It depends on the use case here, I don't think there is much difference in the context of performance or code readability.
I would personally go with the second approach as it also reduces indentations. Indentation gives me a clear indication of initialization of newer block. If I notice a new indent, I would assume there should be a for loop here or an if statement.
If the code is part of the main flow or the most common flow, I wouldn't want that to be further indented.
Again, All this is my personal style guide rules which have helped me to be able to deduce the code logic faster.
I've been made the following question during a hiring process skills test:
var x = function(z) {
console.log(z);
if (z > 0) {
x(z-1);
}
};
why this is progressively slower as z get higher? propose a better
version, keeping it recursive.
And I want to know the answer just to learn about it. I answered that it gets slower because as z increases the number of recursive calls increases too, but I could not provide a better version. In addition, I don't know if there is any further reason why the function become slower as z get higher.
The correct answer would have been, "It should not be progressively slower as z gets higher". In fact, it does not in my simple tests. My tests overflow the stack before any slowdown is visible.
I cannot imagine any alternative way to write the function while maintaining its recursive nature.
Since JavaScript doesn't have true tail calls (yet) what you are experiencing isn't a slowdown based on the value of z but a slowdown based on stack growth and the inability of the garbage collector (GC) to cleanup the stack of scopes that are necessary to retain this functionality.
In theory, you can change this behavior by using setImmediate and the callback pattern to solve for this. Using setImmediate allows the scope of the current execution loop to fall out of use and get collected during the next GC cycle.
So, something like:
var x = function x(z){
console.log(z);
if (z > 0) {
setImmediate(function(){
x(z-1);
});
}
};
But this will not work because z is passed down into the scope for setImmediate and thus the previous scope of x can't be GC'd properly.
Instead you have to use IIFE (Immediately Invoked Function Expression) to achieve what you are looking for in combination with using setImmediate to allow the execution cycle to have time to allow for GC:
var x = function x(z){
console.log(z);
if (z > 0) {
setImmediate((function(newZ){
return function(){
x(newZ);
};
})(z-1));
}
};
Barring any typos I think I got the above correct. Of course if your using ES6 you can shorthand this greatly as well.
The other side of this equation is the spooling effect of console.log and the buffer resizes your going to incur for doing this in a browser. In an OS terminal these costs will be minimized as backbuffer scroll is limited and the back buffer is pre-allocated and reused.