How does Babel implement tail recursion?
How does this transpiled es5 code work?
Thought that I would post a detailed explanation, to allow those landing here from Google to hopefully be able to come to a faster understanding more quickly.
Note that the following explanation was derived from what I can tell from the code (no consultation with the author of Babel or other experts in its code was performed), so it is unknown whether the meaning I derived was the intended meaning of #sebmck or others who contributed to this transformation.
"use strict"; // ES2015/ES6 modules are assumed to be in strict mode.
function factorial(_x2) {
// This variable is the list of arguments to factorial.
// Since factorial may be called again from within this function
// (but must be called indirectly to prevent call stack growth),
// a custom arguments-like variable must be maintained.
var _arguments = arguments;
// _again equals true when the original factorial function should be called
// once more.
var _again = true;
// This line creates a labeled while loop, use to allow continuing this
// specific loop, without the requirement of having no nested while loops
// Nested while loops would cause a "continue" statement within them to
// execute them, not this loop.
_function: while (_again) {
// The variable used in the original factorial function was called "n",
// this method allows Babel to not rename it, but simply assign the
// possibly modified first argument of the factorial function to it.
var n = _x2;
// Temporal dead zone (TDZ) mechanic
acc = undefined;
// The "use strict" directive from the original function
"use strict";
// Beginning of user code, this is always inserted to ensure that if
// this is the final run, the while loop will not run again.
_again = false;
// This is Babel's default argument handling. The steps, in order, are:
// 1. Make sure that there will not be an out-of-bounds access for the
// undefined check.
// 2. Check if the second argument to the current iteration is undefined,
// if yes: the default value is `1`, if no, use the value of the first argument.
var acc = _arguments.length <= 1 || _arguments[1] === undefined ? 1 : _arguments[1];
// Input code - no modifications.
if (n <= 1) return acc;
// The following three lines are the call to factorial() within factorial
// in the input code. The first line assigns the new arguments, as well
// as updating the _x2 variable to it's new value. The second line
// overrides the assignment in the beginning of the loop.
// The third line brings the loop back to the beginning.
_arguments = [_x2 = n - 1, n * acc];
_again = true;
continue _function;
}
}
Related
I have an assignment to count repeated strings base on a Heap's Algorithm Permutation.The first thing I want to do is output the swapped strings, I found this code from jake's answer Can someone please help me understand recursion within this code in a loop? The output of this function are swapped strings.
function permAlone(string) {
var arr = string.split(''), // Turns the input string into a letter array.
permutations = []; // results
function swap(a, b) {
debugger; // This function will simply swap positions a and b inside the input array.
var tmp = arr[a];
arr[a] = arr[b];
arr[b] = tmp;
}
function gen(n) {
debugger;
if (n === 1) {
var x =arr.join('');
permutations.push(x);
} else {
for (var i = 0; i != n; i++) { // how does this loop executes within the call stack?
gen(n - 1);
debugger;
swap(n % 2 ? 0 : i, n - 1); // i don't understand this part. i understand the swap function, but I don't get how indexes are swapped here
}
}
}
gen(arr.length);
return permutations;
}
permAlone('xyz'); // output -> ["xyz","yxz","zxy","xzy","yzx","zyx"]
I have been experimenting it on debugger but still can't get what's happening.
I'm not sure what you mean by
understand recursion within this code in a loop
If you mean you want to see the algorithm in a loop form rather than a recursion version you can see them one by side in pseudocode in the wikipedia page here.
For your questions within the code:
how does this loop executes within the call stack?
You are right to refer to the call stack, and this is a general question regarding recursion. If you don't understand how recursion works with the stack you can refer to this really nice and simple video that demonstrates recursive calls using factorial calculation in java (start around min 4:00).
The line you look at is no different than any other line in the recursive function. We start by defining i and assigning the value 0 to it. We continue to check if it satisfies the condition of the for loop. If it does we step into the loop and execute the first line inside the loop which is the recursive call. Inside the recursive call we have a new stack frame which has no knowledge of the i variable we defined before executing the recursive call, because it is a local variable. So when we get to the loop in the new call we define a new variable i, assigning it 0 at first and incrementing it as the loop repeats in this stack frame/call instance. When this call finishes we delete the stack frame and resume to the previous stack frame (the one we started with) where i=0 still, and we continue to the next line.
All the calls have access to the arr and permutations variables since the function is defined in the same scope as the variables (inside the function permAlone) so within each call - no matter what the stack frame we are in, the changes made to those are made to the same instances. That's why every push done to permutations adds to the existing results and will be there when the function returns the variable at the end.
i don't understand this part. i understand the swap function, but I don't get how indexes are swapped here
Indexes are not swapped here. It is merely a call for the swap function with the correct indices.
swap(n % 2 ? 0 : i, n - 1);
is just
swap(a, b);
with
a = n% 2 ? 0 : i;
b = n - 1;
If the a part is what confuses you, then this is a use of the ternary operator for conditional value. That is, it's symbols used to form an expression that is evaluated differently according to the circumstances. The use is by
<<i>boolean epression</i>> ? <<i>value-if-true</i>> : <<i>value-if-false</i>>
to evaluate the above, first <boolean expression> is evaluated. If it's value it true then the whole expression is evaluated as <value-if-true>. Otherwise, the whole expression is evaluated as <value-if-false>.
In the code itself, for a, n % 2 is the boolean expression - js divides n by 2 and takes the remainder. The remainder is either 1 or 0. js implicitly converts those to true and false respectively. So if n is odd we get
a = 0
and if it's even we get
a = i
as the algorithm requires.
I don't know if you've ever felt like you've dived off the stupid tree and hit every branch on the way down, but JavaScript has that effect on me, an experienced PHP programmer but just doesn't get JS sometimes.
so I wrote this function using jQuery.extend({ .. }) as follows:
loadSection: function(obj){
if(typeof obj=='undefined')obj=event.target; //but this doesn't work
if(typeof obj=='string')obj=document.getElementById(obj);
var params=null;
var instr={};
var k=0;
while(true){
..etc..
I want to be able to call it two ways:
$.loadSection($('#employee-data-173'));
//or $loadSection('employee-data-173') for convenience
or:
$('#employee-data-173').loadSection();
Clearly (to you anyway!) I'm not grasping the concept of what's being passed in the second case. And that's not even getting into a case like this:
$('.someElement').click(loadSection);
You want to use the same function in three totally different usecases. In each case different arguments are automatically passed to the function. So first declare and prepare it to handle all possible types of arguments:
function loadSection(index, obj, args) {
var element, params = null;
if (obj && obj.nodeType) { // for usecase 2
element = obj; if (typeof args == 'object') params = args;
} else {
if (typeof obj == 'object') params = obj;
if (typeof index == 'string') { // usecase 1 with selector-string
element = document.getElementById(index);
else if (index.jquery) { // usecase 1 with jQuery object
if (index.length == 1) element = index[0]; // if only one element inside jQuery
// if more than one element there call this function recursively on each
else index.each(loadSection);
}
else if (index.target) element = index.target; // for usecase 3
}
/* your stuff */
}
In your usecase 1 you have to add the function to the global jQuery object and call it by $.loadSection(argument), where argument may be a id-selector-string or an jQuery-object $("selector"). There are two ways with identic result:
$.loadSection = loadSection;
$.extend({loadSection: loadSection});
In usecase 2 you want to call the function as a method of a jQuery object. Therefore you have to add it to the jQuery.prototype like so:
$.fn.loadSection = function( args ) { // $.fn is synonym for jQuery.prototype
return this.each(loadSection, args);
};
If you call now $("selector").loadSection() the function is executed once for each element matched by "selector". Arguments index and obj are automatically passed to loadSection.
In usecase 3 the function is used as callback-function for an event. Since its prepared for this case, the event object is automatically passed to it. So just do:
$('.someElement').click(loadSection);
You can use all cases mixed in the same piece of code.
EDIT "What shall/can the function return?"
It may return whatever you want to. Only the behaviour depends on usecase.
In usecase 1 you can do: var result = $.loadSection("selector") and result gets the returned value.
In usecase 2 there is an iteration over all elements in the jQuery object. The return value of loadSection is simply ignored except you explicitely return false. Then iteration stops. So if you do if (index == 2) return false; the function is executed maximum 3 times even when you have 7 elements inside jQuery object.
The function as a whole always returns the jQuery object, so you can do chaining:
$("selector").loadSection().css({color: 'blue'}).animate(...). ...
In usecase 3 the function is only executed but the return value gets never recognized anywhere, so you can't catch it.
EDIT 2 "How to pass additional params to the function?"
1) Now loadSection is prepared to take additional args (see above). 2) The setup of $.fn.loadSection is modified to take args (see above). 3) args must be an object or array eg {prop: 'color', id: 23} (otherwise it's ignored) but may be omitted.
In usecase 1 pass args as second argument
var result = $.loadSection("selector", args); // you find args inside in ""var params"
In usecase 2 args is the one and only possible argument. jQuery now makes an iteration inside an iteration: the function is called on each element once for each item in args!
The inner iteration is stoppable by return false, but the iteration over all elements no longer.
$("selector").loadSection({prop: 'color', id: 23}) // if $() contains 3 elems, function run six times
In usecase 3 its impossible to pass args since you only point to the function by its name.
I asked this before, but a little vague and poorly worded.
I have a object that takes in Actions and puts them into a stack that it goes through over time. It performs the first Action in the stack until it's done, then it performs the second, and so on. There is a RepeatAction that can take in an array of other Actions and perform them a number of times in a similar fashion. (.repeat() simply puts a new RepeatAction into the objects stack.
Take this for example:
object.repeat(10,[new LogAction(Math.random())]);
Given that LogAction only takes in a parameter and logs it out. When the object's repeat function gets called it will put 10 LogActions into its stack, but they will all log the same number. What I'm looking for is a way that it will log a different number all 10 times.
You may say just pass in Math.random as a function, but then what if I want to pass in 4 * Math.random()?
Any help?
The code needs to invoke a function (later) to get a different value. e.g.
// pass in the `random` function - do not get a random number immediately!
object.repeat(10, [new LogAction(Math.random)])
// create and pass in a custom function that uses `random` itself
var rand4 = function () { return Math.random() * 4 }
object.repeat(10, [new LogAction(rand4)])
Since "LogAction" is not disclosed, I'll make a simple example to work with the above.
function LogAction (arg) {
this.execute = function () {
// If the supplied argument was a function, evaluate it
// to get the value to log. Otherwise, log what was supplied.
var value = (typeof arg === 'function') ? arg() : arg;
console.log(value);
}
}
Reading through How do JavaScript closures work? will likely increase the appreciation of functions as first-class values, which the above relies upon.
You may say just pass in Math.random as a function,
Yes, I would.
but then what if I want to pass in 4 * Math.random()?
Then in place of Math.random, use
function() { return 4 * Math.random(); }
You can pass function which returns result of random:
object.repeat(10,[new LogAction(function(){return Math.random();})]);
And in LogAction function you simply need to check if argument is a function:
function LogAction(arg){
var value = arg&& getType.toString.call(arg) === '[object Function]' ? arg() : arg;
//log value
}
As below, I made a simple high scores array that is saved to local storage and added to with user prompts.
As an independent file by itself it works great. Or at least it seems to be.
However, when I try to integrate this into my larger application I seem to be having scope issues with my global variable, allScores . The length of the array stays at 0. I checked to see if I have any variable duplicates and I do not.
I've been trying to read about function hoisting and scope. What I am not sure about is why the below code works as an independent file, but when I integrate it into my larger application I have scope issues.
How should I be doing this differently? As I am new to JavaScript my best practices are most likely off. Your guidance is appreciated. Thanks.
var allScores = [];
function saveScore() {
if (allScores.length === 0) {
allScores[0]= prompt('enter score', '');
localStorage ['allScores'] = JSON.stringify(allScores);
}
else if (allScores.length < 3) {
var storedScores = JSON.parse(localStorage ['allScores']);
storedScores = allScores;
var injectScore = prompt('enter score', '');
allScores.push(injectScore);
allScores.sort(function(a, b) {return b-a});
localStorage ['allScores'] = JSON.stringify(allScores);
}
else {
var storedScores = JSON.parse(localStorage ['allScores']);
storedScores = allScores;
var injectScore = prompt('enter score', '');
allScores.pop();
allScores.push(injectScore);
allScores.sort(function(a, b) {return b-a});
localStorage ['allScores'] = JSON.stringify(allScores);
}
document.getElementById('readScores').innerHTML = allScores;
}**
I have refactored your code in an effort to display some practices which may help you and others in the future, since you mentioned best practices in the question. A list of the concepts utilized in this refactoring will be below.
var saveScore = (function () { /* Begin IIFE */
/*
The variables here are scoped to this function only.
They are essentially private properties.
*/
var MAX_ENTRIES = 3;
/*
Move the sorting function out of the scope of saveScore,
since it does not need any of the variables inside,
and possibly prevent a closure from being created every time
that saveScore is executed, depending upon your interpreter.
*/
function sorter(a, b) {
return b - a;
}
/*
As far as your example code shows, you don't appear to need the
allScores variable around all the time, since you persist it
to localStorage, so we have this loadScore function which
pulls it from storage or returns a blank array.
*/
function getScores() {
var scores = localStorage.getItem('scores');
return scores ? JSON.parse(scores) : [];
/*
Please note that JSON.parse *could* throw if "scores" is invalid JSON.
This should only happen if a user alters their localStorage.
*/
}
function saveScore(score) {
/* Implicitly load the scores from localStorage, if available. */
var scores = getScores();
/*
Coerce the score into a number, if it isn't one already.
There are a few ways of doing this, among them, Number(),
parseInt(), and parseFloat(), each with their own behaviors.
Using Number() will return NaN if the score does not explicitly
conform to a textually-represented numeral.
I.e., "300pt" is invalid.
You could use parseInt(score, 10) to accept patterns
such as "300pt" but not anything with
leading non-numeric characters.
*/
score = Number(score);
/* If the score did not conform to specifications ... */
if (isNaN(score)) {
/*
You could throw an error here or return false to indicate
invalid input, depending on how critical the error may be
and how it will be handled by the rest of the program.
If this function will accept user input,
it would be best to return a true or false value,
but if a non-numeric value is a big problem in your
program, an exception may be more appropriate.
*/
// throw new Error('Score input was not a number.');
// return false;
}
scores.push(score);
scores.sort(sorter);
/*
From your code, it looks like you don't want more than 3 scores
recorded, so we simplify the conditional here and move
"magic numbers" to the header of the IIFE.
*/
if (scores.length >= MAX_ENTRIES) {
scores.length = MAX_ENTRIES;
}
/* Limiting an array is as simple as decreasing its length. */
/* Save the scores at the end. */
localStorage.setItem('scores', JSON.stringify(scores));
/* Return true here, if you are using that style of error detection. */
// return true;
}
/* Provide this inner function to the outer scope. */
return saveScore;
}()); /* End IIFE */
/* Usage */
saveScore(prompt('Enter score.', ''));
As you can see, with your score-handling logic encapsulated within this function context, virtually nothing could tamper with the interior without using the interface. Theoretically, your saveScore function could be supplanted by other code, but the interior of the IIFE's context is mutable only to those which have access. While there are no constants yet in standardized ECMAScript, this methodology of the module pattern provides a decent solution with predictable outcomes.
IIFE, an Immediately-Invoked Function Expression, used to create our module
DRY, Don't Repeat Yourself: code reuse
Magic numbers, and the elimination thereof
Type coercion of a string to a number
Error handling, and discernment between exception or return code use. Related: 8, 9
Have you considered JS closures?
Here is some piece to give you an idea..
var scoreboard = (function () {
var _privateVar = "some value"; //this is never exposed globally
var _allScores = [];
return {
getAllScores: function() { //public
return _allScores;
},
saveScore: function(value) { //public
_allScores.push(value);
}
};
})();
alert(scoreboard.getAllScores().length); //0
scoreboard.saveScore(1);
alert(scoreboard.getAllScores().length); //1
alert(scoreboard._privateVar); //undefined
alert(scoreboard._allScores); //undefined
This way your variables and functions are never exposed to the window object and you don't need to worry about duplicates or scopes. The only variable that has to be unique is the name of your closure function (scoreboard in this example case).
Without having access to your environment, the best thing you can do is get use the firefox dev tools (or get firebug) to put a breakpoint in your saveScore function. You can step through line-by-line and check values and even evaluate expressions within the current scope in a console window(REPL).
https://developer.mozilla.org/en-US/docs/Tools/Debugger - with firefox
http://getfirebug.com/javascript - with firebug(a firefox plugin)
If you're doing web-development, these are priceless resources, so invest some time into learning how to use them.(They will save you much more time down the road!)
According to my research and googling, Javascript seems to lack support for locale aware sorting and string comparisons. There is localeCompare(), but it has been reported of browser specific differencies and impossibility to explicitly set which locale is used (the OS locale is not always the one wanted). There is some intentions to add collation support inside ECMAScript, but before it, we are on our own. And depending how consistent the results are across browsers, may be we are on our own forever :(.
I have the following code, which makes alphabetical sort of an array. It's made speed in mind, and the ideas are got from https://stackoverflow.com/a/11598969/1691517, to which I made some speed improvements.
In this example, the words array has 13 members and the sort-function is called 34 times. I want to replace some of the letters in the words-array (you don't have to know what replacements are made, because it's not the point in this question). If I make these replacements in sort-function ( the one that starts with return function(a, b) ), the code is inefficient, because replacements are made more than once per array member. Of course I can make these replacements outside of this closure, I mean before the line words.sort(sortbyalphabet_timo);, but it's not what I want.
Question 1: Is it possible to modify the words-array in between the lines "PREPARATION STARTS" and "PREPARATION ENDS" so that the sort function uses modified words-array?
Question 2: Is it possible to input arguments to the closure so that code between PREPARATION STARTS and PREPARATION ENDS can use them? I have tried this without success:
var caseinsensitive = true;
words.sort( sortbyalphabet_timo(caseinsensitive) );
And here is finally the code example, and the ready to run example is in http://jsfiddle.net/3E7wb/:
var sortbyalphabet_timo = (function() {
// PREPARATION STARTS
var i, alphabet = "-0123456789AaÀàÁáÂâÃãÄäBbCcÇçDdEeÈèÉéÊêËëFfGgHhIiÌìÍíÎîÏïJjKkLlMmNnÑñOoÒòÓóÔôÕõÖöPpQqRrSsTtUuÙùÚúÛûÜüVvWwXxYyÝýŸÿZz",
index = {};
i = alphabet.length;
while (i--) index[alphabet.charCodeAt(i)] = i;
// PREPARATION ENDS
return function(a, b) {
var i, len, diff;
if (typeof a === "string" && typeof b === "string") {
(a.length > b.length) ? len = a.length : len = b.length;
for (i = 0; i < len; i++) {
diff = index[a.charCodeAt(i)] - index[b.charCodeAt(i)];
if (diff !== 0) {
return diff;
}
}
// sort the shorter first
return a.length - b.length;
} else {
return 0;
}
};
})();
var words = ['tauschen', '66', '55', '33', 'täuschen', 'andern', 'ändern', 'Ast', 'Äste', 'dosen', 'dösen', 'Donaudam-0', 'Donaudam-1'];
$('#orig').html(words.toString());
words.sort(sortbyalphabet_timo);
$('#sorted').html(words.toString());`
Is it possible to modify the words-array in between the lines "PREPARATION STARTS" and "PREPARATION ENDS" so that the sort function uses modified words-array?
No, not really. You don't have access to the array itself, your function only builds the compare-function that is later used when .sort is invoked on the array. If you needed to alter the array, you'll need to write a function that gets it as an argument; for example you could add a method on Array.prototype. It would look like
function mysort(arr) {
// Preparation
// declaration of compare function
// OR execution of closure to get the compare function
arr.sort(comparefn);
return arr;
}
Is it possible to input arguments to the closure so that code between PREPARATION STARTS and PREPARATION ENDS can use them?
Yes, of course - that is the reason to use closures :-) However, you can't use sortbyalphabet_timo(caseinsensitive) with your current code. The closure you have is immediately invoked (called an IIFE) and returns the compare-function, which you pass into sort as in your demo.
If you want sortbyalphabet_timo to be the closure instead of the result, you have to remove the brackets after it. You also you can use arguments there, which are accessible in the whole closure scope (including the comparefunction):
var sortbyalphabet_timo_closure = function(caseinsensitive) {
// Preparation, potentially using the arguments
// Declaration of compare function, potentially using the arguments
return comparefn;
}
// then use
words.sort(sortbyalphabet_timo_closure(true));
Currently, you are doing this:
var sortbyalphabet_timo_closure = function(/*having no arguments*/) {
// Preparation, potentially using the arguments
// Declaration of compare function, potentially using the arguments
return comparefn;
}
var sortbyalphabet_timo = sortbyalphabet_timo_closure();
// then use
words.sort(sortbyalphabet_timo);
…which just caches the result of executing the closure, if you'd need to sort multiple times.