Skip to main content

Sleep sort in JavaScript and closures


     There is this "genius" sorting algorithm called "sleep sort". I don't know what came to my mind, but I decided "let's do it in javascript!". I thought that it won't take more than 2 minutes. Starting with such wrong and over confident assumption, it was a journey into the details of javascript which I had never bothered to pay much attention to. I learnt quite a few things on the way and will share the same in this blog.

     Here is the code which I wrote off :

1
2
3
4
5
array = [1, 9, 4, 8, 2, 3, 6];

for (i = 0; i < array.length; i++) {
    window.setTimeout ("console.log(array[i])", array[i]*1000);
}  


     What the code was supposed to do was, for each integer in the array, wait for those many seconds and then print that integer. So, the integer 9 will be logged into the console after 9 seconds while the code will log 2 into the console after 2 seconds.
      Here is what it logged into the console :
undefined
undefined
undefined
undefined
undefined
undefined
undefined
      What?! said I, totally baffled and and taken by surprise. I thought that there is some problem with the array so, in the for loop, I changed array.length to array.length - 1. Now, all the undefineds in the output were replaced by 6s.
      What was happening here? The line 4 in the code was telling the javascript engine that after array[i] number of seconds, evaluate the javascript code console.log(array[i]). So it was here, that all the logic was going for the toss then! In two shakes of a duck's tail, the for loop would come to a stop. Then, after a comparatively long time (1 second) the first timeout will get over and the code console.log(array[i]) will get evaluated. But wait! by this time, the value of i is 7 and each of those timeouts will end up evaluating console.log(array[7]). And we know that as the array contains only 7 elements and array[7] means the 8th element in the array, there is no doubt that undefined will be logged into the console! Mystery solved!
      I read up a bit on the web about the setTimeout function and came to know that it is a bad practice to use a string as the first parameter of setTimeout for various reasons - mostly the association with eval . The syntax of setTimeout as given there is :
setTimeout (function_object, timeout_in_milliseconds, param1, param2, param3.....)
where the arguments after timeout_in_milliseconds are all optional. For example,
window.setTimeout(foo, 5*1000, 99, 42)
will make a function call to foo as foo(99, 42) after 5 seconds.
       So I changed the code to following :
1
2
3
4
5
6
7
8
9
array = [1, 9, 4, 8, 2, 3, 6];

function log_value_in_console (value) {
    console.log (value);
}

for (i = 0; i < array.length; i++) {
    window.setTimeout (log_value_in_console, array[i]*1000, array[i]);
}
       Here, right at the time of setting the timeout, line 8 was telling the javascript engine. "After array[i] seconds, execute the function log_value_in_console with array[i] as its parameter". Mind you, the value of array[i] will be calculated at the same time when the timeout is set. (after all, they are parameters passed to the function window.setTimeout and as usual, they will be evaluated when you are passing them).
       Quite as expected, this is what it logged into the console :
1
2
3
4
6
8
9
       Yey! It worked! But on the same page where I read about setTimeout, there was a note saying "passing additional parameters to the function does not work in Internet Explorer." Alas! Why did you guys at mozilla even add that then? Whatever...
       So now the problem was to do sleep sort with just two parameters to the setTimeout function. Basically, first argument needed to be a function which somehow knows what to log. Enter closures!
       What is this closure anyway? Frankly, I don't know :P. What I understand is "a closure is like a zombie, a भूत!" closure is formed when a function returns (gets done with its execution) but still its scope, that is its internal variables and arguments passed to it (think of it as kind of a stack frame of that function) are not cleaned by the garbage collector. Such a scenario typically happens when the function returns another function and returned function needs to access the internal variables / arguments of the outer function. Apart from the fancy name which notoriously is name of a mathematical property also, there is nothing complex or voodoo about closures.
       Using closures, I rewrote the code in following way, and as you might have guessed, it worked! :P
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
array = [1, 9, 4, 8, 2, 3, 6];

function make_logger_function (value) {
    function log_value_in_console () {
        console.log (value);
    }
    return log_value_in_console;
}

for (i = 0; i < array.length; i++) {
    window.setTimeout (make_logger_function(array[i]), array[i]*1000);
}
       When the execution comes to line 11, it evaluates both its arguments, the second, as we all know returns  the number of milliseconds timeout after which, the first argument (which is function) should be executed. So what does the first argument evaluate to? It returns a function, which knows what value it has to log. Here, we can see closure playing its role. The function make_logger_function takes a value to be logged into the console and returns a function which, upon called, logs that value in the console.

Comments

Popular posts from this blog

ODI KM Adding Order by Option

You can add Order by statement to queries by editing KM.I have edited IKM SQL Control Append to provide Order by.  1) Add an option to KM named USE_ORDER_BY, its type is Checkbox and default value is False. This option determines you want an order by statement at your query. 2)Add second option to KM named ORDER_BY, type is Text. You will get order by values to your query by this option. 3) Editing Insert New Rows detail of KM. Adding below three line code after having clause. That's it! <% if (odiRef.getOption("USE_ORDER_ BY").equals("1")) { %> ORDER BY <%=odiRef.getOption("ORDER_BY" )%> <%} %>  If USE_ORDER_BY option is not used, empty value of ORDER_BY option get error. And executions of KM appears as such below; At this execution, I checked the KM to not get errors if ORDER_BY option value is null. There is no prove of ORDER BY I'm glad.  Second execution to get  Ord

Creating Yellow Interface in ODI

Hello everyone! In Oracle data integrator (ODI), an  interface  is an object which populates one datastore, called the  target , with data coming from one or more other datastores, known as  sources . The fields of the source datastore are linked to those in the target datastore using the concept of  Mapping . Temporary interfaces used in ODI are popularly known as  Yellow Interfaces . It is because ODI generates a yellow icon at the time of creation of a yellow interface as opposed to the blue icon of a regular interface. The advantage of using a yellow interface is to avoid the creation of  Models each time you need to use it in an interface. Since they are temporary, they are not a part of the data model and hence don’t need to be in the Model. So let’s begin and start creating our yellow interface! Pre-requisites : Oracle 10g Express Edition with *SQL Plus, Oracle Data Integrator 11g. Open *SQL Plus and create a new table  Sales  in Oracle. You can use any existing ta

Running Count in Talend Open Studio

Most Talend components keep a count of the records processed using variables like NB_LINE or NB_LINE_OK.  But these are only available after all processing is completed.  Define your own counter variable to keep a running count for use in a tMap. Variables like tFilterRow.NB_LINE or tAccessOutput.NB_LINE_INSERTED can be used to report the number of affected lines after a subjob's processing.  However, it may be of use to get the current line index for use in a tMap.  The index variables used to form NB_LINE aren't available during processing; they're only written out the globalMap at the end of processing. In this example, staging records are loaded from Excel to Access.  The order in which the Excel records are read is preserved in a database column called DISPLAY_SEQ_NB.  Note that there is an auto-increment column used for record ID in the Access table.  This could be used to infer a loading order, but this job uses a separate column to keep the ID as a meaningless surr