Can anyone see a problem with a recursive function written like this:
var recurse = 100;
var recursed = 0;
(function (callback){
callback(callback);
})(function(callback){
recursed++;
console.log('recursed ' + recursed + ' times');
if(recursed < recurse){
callback(callback);
}
});
Is there any reason why this might execute slower? Or might be considered bad practise?
Is this functional programming gone AWOL?
The core principles of functional programming are data immutability and functions being functions in mathematical sense (i.e. take value and return value without any side-effects). Those principles form referential transparency, i.e. given the same arguments a function will always return the same result.
Your function does not satisfy any of those criteria to be referred to as "functional".
The following is an example of how recursion is done in functional style:
var recursiveFunction = function( count, max ) {
if( count < max ){
return recursiveFunction( count + 1, max )
} else {
return count
}
}
var result = recursiveFunction(0, 100) // result == 99
Is there any reason why this might execute slower?
Yes. JavaScript is hardly tail-call-optimized. A loop would be faster.
Or might be considered bad practise?
Yes. Your example code is unreadable, it's hard to grasp what all these callbacks do.
If you want to use recursion for something like this, an IENFE (Named & Immediately Executed Function Expression) would be a better choice:
(function callback(recursed) {
console.log('recursed ' + recursed + ' times');
if (recursed < 100)
callback(recursed + 1);
})(0);
Related
I'm trying to simplify the introduction of loops to children in JS and p5.js with writing some helper functions to ease difficult things. What I'm aiming for is something like repeat(5){ //code to be executed 5 times }
something similar can be achieved with a higher order function, but it's not quite as simple/transparent as I hope it could be.
for example:
function repeat(n = 1) {
let i = 0;
return fn => {
while(n--) {
i++;
fn(i)
}
}
}
// example usage:
repeat(10)(i => {
console.log('floop boop', i)
})
but the usage and curly/parens will confuse children. I'm aware that a while loop is one direction to go, but It's sort of goes in an inverse direction of what a repeat loop might do. It's the direction to push them, but maybe not ideal for day 1 of coding.
ultimately We could eventually use it something like:
let i = 0;
repeat(10){
i++
console.log('we are repeating and we're on iteration number ', i)
}
If you are teaching JavaScript you won't be able to avoid first class functions. I would start by creating a method called repeat, it takes in a number of times to repeat and the function with the behaviour you want repeated.
function repeat(times, fn) {
var i = 0;
while(times--) {
i++;
fn(i);
}
}
repeat(10, function(i) {
console.log('we are repeating and we\'re on iteration number ', i);
});
I agree that a higher order function may be confusing. In both cases you don't need functions to teach loops.
I'm using Coffescript and p5.
for i in range 10
rect 20*i,20,10,10
Range has same defintion as in underscore.
Python indentation without colon
P5dojo.com is my site.
In Javascript, I have a function that sets a variable. If the function tries to set the variable to its current value, is it more "efficient" to break out of the function, or let the function re-set the variable's value?
Example
var a;
function setStuff(x) {
if (a == x) { return; }
a = x;
}
versus
var a;
function setStuff(x) {
a = x;
}
This function will be called on page scroll, so it will be called at a high frequency.
I don't think the issue is "efficiency".
I do however think there's a practice at play here, which is to generally not manipulate values outside the scope of the function. Having many functions like these in your application will drive you nuts, wondering which function is changing what.
Instead return a new value.
var setStuff = function() {
return newValue;
}
var a = setStuff();
I wrote a simple test snippet:
var a;
function setStuffCheck(x) {
if (a == x) { return; }
a = x;
}
function setStuff(x) {
a = x;
}
function benchmark(func){
var startTime = Date.now();
var callCount = 1000000;
for(var i = 0; i < callCount; i++){
func(10);
}
console.log((Date.now() - startTime) + "ms for "+callCount+" calls setting always the same value");
startTime = Date.now();
for(var i = 0; i < callCount; i++){
func(i);
}
console.log((Date.now() - startTime) + "ms for "+callCount+" calls setting always different values");
}
benchmark(setStuffCheck);
benchmark(setStuff);
By copying and pasting it in the console (Firefox 46.0.1), I have something like this:
138ms for 1000000 calls setting always the same value
216ms for 1000000 calls setting always different values
77ms for 1000000 calls setting always the same value
78ms for 1000000 calls setting always different values
So the second way seems to be always better. But the results may be different on each computers. However, the difference is noticable only for 1 millions of calls (try changing it to 1000, there'll be no differences).
The second option makes more sense to me and is more viable as there is very less logic written as compared to the first one as it is checking whether two values are equal or not , whereas in the second option its just re-assigning the variable to a new value.
if condition is always slower compared to when just assigning a new value . So i think you should go with second option.
There are potentially two factors that will likely drown out any practical performance difference between the two options. In practice, I would suggest that you use the version that is the easiest to understand and explain to others. I think that would probably be the unconditional update but it would be more up to you.
The two things that are likely going to obfuscate any real differences are:
What else are you doing in the function
What effect branch prediction has on your conditional.
Now, specifically to the question of what version is faster. I have set up the following test with each option executed a million times with 10 runs of each test. The global is set to itself 1 in 10 times but you can change that to some other frequency by setting aNew
var a = 10;
var ittr = 1000 * 1000;
function getRandomIntInclusive(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; }
function set1(x){
if (a === x){ return; }
a = x;
}
function set2(x){
a = x;
}
for (var j = 0; j < 10; j++){
var start = performance.now();
for (var i = 0; i< ittr; i++){
var aNew = a - 10 + getRandomIntInclusive(1,19);
set1(aNew);
}
console.log("conditional : " + (performance.now() - start));
}
for (var j = 0; j < 10; j++){
var start = performance.now();
for (var i = 0; i< ittr; i++){
var aNew = a - 10 + getRandomIntInclusive(1,19);
set2(aNew);
}
console.log("unconditional : " + (performance.now() - start));
}
Your results may vary but the I see conditional set() averages about 18 after settling down. The unconditional about the same, maybe 17.5.
Note that the vast bulk of the time here is taken by the call to random(). If you consistently just set the global to itself both functions time at around 1.8 rather than 18, suggesting that whatever else you might do in your set() is likely to obfuscate any performance difference.
The two do not have necessarily have identical results. Consider this series of calls:
var a;
function setStuff(x) {
if (a == x) { return; }
a = x;
}
setStuffCheck(0) ;
setStuffCheck('0');
console.log(a);
Output is not '0', but 0.
For a good comparison, the setStuffCheck function should use the strict equality operator ===.
In my tests in FireFox I see the performance of both functions show very little difference. setStuffCheck seems to take slightly more time to execute than setStuff when the argument has a different value than a, but it is the opposite (also slightly) when the values are the same. The difference in either way is in the order of 2%, which is the kind of fluctuations you get anyway on a typical device/PC for other causes, that have nothing to do with the code.
Anyway, this also means that this slight performance difference will depend on how often you expect to call the function with an argument that is equal to a.
However, the difference is only noticeable when you would do hundreds of millions of calls. If you don't have that many calls, then don't even bother and choose for setStuff.
//reverse a string with recursion and a closure
function r(str){
var i = str.length-1,results=[],j=0;
function _r(str){
if(i===0){
return results.join('') + str[0];
}
console.log('i = ' + i);
results[j] = str[i];
i--,j++;
console.log('j = ' + j);
return _r(str);
}
return _r(str);
}
I have two questions about the above code:
does the above code break the (my ignorance is showing) stateless nature of function programming?
what if str was a large string, is this implementation slower/more memory intensive than a solution that did not use a closure?
Yes, you're not using the functional paradigm.
In your case you're using the recursion just to loop and the processing is done using variables that are outside of the function. It's really not much different than using global variables (except that they're not global but locals of the outside function).
To reverse a string using the functional approach you should consider that the reverse of a string is composed by last_char + (reverse of middle part) + first_char.
This definition expands naturally into a recursive function... for example:
function rev(str) {
if (str.length < 2) {
// Empty string or a single char... reverse = input
return str;
} else {
return str[str.length-1] + rev(str.slice(1, -1)) + str[0];
}
}
this uses no explicit state (as you may notice there are no assignments at all).
If you're looking for a tail-call optimizable version instead consider:
function rev(todo, done) {
if (todo === "") {
return done;
} else {
return rev(todo.slice(1), todo[0] + (done||""));
}
}
the idea in this case is that the processing case must be return <recursive-call> (in the previous example this is not happening because the result of recursion is added one char to each end before returning).
If your code ends up returning the unprocessed result of a recursive call then the function can be tail-call optimized as no stack space is needed. In other words the recursive call becomes a simple loop.
Finally this is another version, not purely functional but that seems similar to what you're attempting:
function rev(str) {
function rev1(v, i, j) {
if (i >= j) {
return v.join("");
} else {
var t=v[i]; v[i]=v[j]; v[j]=t;
return rev1(v, i+1, j-1);
}
}
return rev1(str.split(""), 0, str.length-1);
}
This is not purely functional because the vector elements are swapped (and you can see there are assignments) but uses a tail-call optimizable recursion to do the loop. Note that rev1 is not a closure but a function (captures no state and you can also put it outside of rev).
I was fiddling with combinators in JavaScript and was being proud of (hopefully) getting S to work when I stumbled upon Wikipedia saying: "The Y combinator can be expressed in the SKI-calculus as: Y = S (K (S I I)) (S (S (K S) K) (K (S I I)))", so I had to try that:
var I = function (x) {
return x;
};
var K = function (x) {
return function(){
return x;}
};
var S = function (x) {
return function (y) {
return function (z) {
return x(z)(y(z));
}
}
};
var Y = S (K(S(I)(I))) (S(S(K(S))(K)) (K(S(I)(I))));
Y; //evals to:
//function (z) {return x(z)(y(z));}
//And this (lifted from Crockford's Site):
var factorial = Y(function (fac) {
return function (n) {
return n <= 2 ? n : n * fac(n - 1);
};
}); //fails:
//RangeError: Maximum call stack size exceeded
What am I doing wrong? Am I not translating that expression correctly? Is there something wrong with how I'm going about this? Does it even make sense? Most of what's to be read about stuff like this just makes my brain want to explode, so the point of this exercise for me was mainly to see if I understood the notation (and would thus be able to translate it to JavaScript).
Oh, and, by the way: what got me reading & fiddling again was that what prototype.js implements as Prototype.K is actually the I combinator. Has anyone noticed?
The problem here is that you are using a strictly evaluated programming language. The Y-combinator, pretty much like any other fixed point combinator, will only work properly when your functions are called by need, or 'lazily evaluated'.
I know of a way to work around this (one of my professors looked into it a while ago), but it will make your code completely unreadable.
Below I've shown what's going on exactly, hoping you can see why JavaScript can't handle a straightforward implementation of SKI-calculus.
This is what Y looks like after JavaScript evaluated your SKI-expression:
var Y = function (q) {
return (function(p){return q(p(p))})(function(p){return q(p(p))});
};
Now let's see what happens if you feed it your function function (fac) { ... }. Let's call that function f:
var factorial = (function(p){return f(p(p))})(function(p){return f(p(p))});
Since the first anonymous function is applied to an argument, it will be evaluated into this:
var factorial = f(
(function(p){return f(p(p))})(function(p){return f(p(p))})
);
In a lazily evaluated language, the argument to f would now be left alone, and f itself would be evaluated. However, because JavaScript is a strictly evaluated language (or 'call-by-value'), it wants to know what argument it needs to pass to function f before actually running that function. So let's evaluate that argument, shall we?
var factorial = f(f(
(function(p){return f(p(p))})(function(p){return f(p(p))})
)
);
I guess now you're starting to see now where things go wrong, and how the Y-combinator actually works. In any case, your JavaScript machine will run out of stack space, because it's trying to build an infinite stack of calls to f.
I am trying to recursively print out the contents of jQuery. I'm planning on using this to analyze an existing instance of jQuery (loaded in a page) against a small database of known "jQuery signatures", to determine what changes have been made (i.e. what plugins have been loaded, functions modified, etc.).
To do this, I have this small function:
function recurse(obj, iter){
var padding = (new Array(iter + 1)).join(" ") + ">";
for (var i in obj){
document.writeln(padding + i + "<br/>");
if (iter < 5)
recurse(obj[i], iter + 1);
}
}
When I execute this:
recurse(jQuery, 1);
I get something like this:
>prototype
>init
>prototype
>init
>prototype
>selector
>jquery
>0
.... On and on and on .....
My problem is, at the very beginning, you can see that prototype and then init repeat over and over again. The only reason it stopped at 5 deep is because of the if (iter < 5) check. If the limit wasn't there, it would have recurred [sic?] forever. The iteration limit helps, but what if there is a critical function 6 deep? Essentially, I have no idea what I should make this iteration limit, or if there should be one at all.
Instead, I'm thinking there must be some kind of algorithm that can prevent never-ending recursion. Does such an algorithm exist? Or should I change how I go about traversing jQuery? Thanks,
You could keep track of what values you've already seen, and just bail out when you see one again.
function recurse(obj) {
var marker = '__' + new Date().getTime() + '__';
function r(obj, iter) {
if (marker in obj) return;
var padding = (new Array(iter + 1)).join(" ") + ">";
obj[marker] = true;
for (var i in obj) {
if (!obj.hasOwnProperty(i) || i === marker) continue;
document.writeln(padding + i + "<br/>");
recurse(obj[i], iter + 1);
}
}
r(obj, 0);
}
Now this of course has the disadvantage of leaving your traversed object graph littered with extra properties, but for some applications that wouldn't be a problem. Also using the clock to make a "unique" marker is really lame; you might want to just use a counter, or maybe just a fixed nonsense string.
edit — Also, another issue (present in the original code too) is that this really should be checking to see if the "obj" values are really objects. If they're scalars, then there's no point doing anything, really. You'd just need to do a "typeof" check right after checking for the marker, and if you see null, a number, a string, or a boolean, just return.
Your recursion is missing a base case. See the definition of Recursion. You introduced an arbitrary base case of (depth < 5). Maybe instead use the length of the array, or as Pointy pointed out, the hasOwnProperty check to skip the recursive call.
Unless i am missing something, all you need to do is to skip strings (if you use a modern browser that allows indexing of strings, otherwise it doesn't matter) and the init function which is the jQuery self reference that gives you the infinite recursion
function recurse(obj, iter){
var padding = (new Array(iter + 1)).join(" ") + ">";
for (var i in obj){
document.writeln(padding + i + "<br/>");
if (i != 'init' && typeof obj[i] != 'string')
recurse(obj[i], iter + 1);
}
}
As several answers said: You algorithm must contain a proper break case to prevent infinite recursion.
But what happen if you cannot control the input of your algorithm?
For that case, or just for development purposes, you could use this:
function acmeRecursion(){
var count = localStorage.getItem("count");
if(count>50){
throw new Error("recursion limit exceeded");
}
count++;
//put your logic here
}
Building off of Pointy's answer (ideally this would be a comment, but alas, code doesn't work great in those), a better solution might be to just pass along an object to the the recurse function that will keep track of objects you've already seen. Something like this:
var recurse = function(obj)
{
var seen = {};
var inner = function(obj, padding)
{
for (var i in obj)
{
if (!(obj[i] in seen))
{
document.writeln(padding + i + '<br />');
seen[obj[i]] = true;
inner(obj[i], padding + ' ');
}
}
};
return inner(obj, '');
};
Which uses a closure rather than an argument to pass along the seen object, for the sake of simplicity, but the basic concept is the same.
This approach has the advantage of not adding extra attributes to the objects you're traversing.
Edit: I meant to explain this, but forgot. I'm not using hasOwnProperty here because in the case of printing an object graph, you probably do want to see inherited attributes.