Here is an example of a string reverse function with recursion.
I'd like to understand why:
(1) If I switch the return line inner actions like this:
return word.charAt(0) + reversed(word.substring(1))
I get the string again but not reversed.
(2) Where the 'word.charAt()' letters are being saved through the nested calls (and overall if it's the same with a regular function), memory wise what I miss here, what keeps them accumulated with tact and where are they 'going'.
I'd like an explanation of some kind to how the memory concept works in js to understand better the relations as I code ( I think especially in and out of functions in my case).
var reverseArray = reversedItems(arr)
function reverseAll(items) {
return items.map(function reversed(word) {
if (word === "") return ""
return reversed(word.substring(1)) + word.charAt(0)
})
}
You should understand the reversed function separately before concerning yourself with reverseAll -
function reverse(word) {
if (word === "") return ""
return reverse(word.substring(1)) + word.charAt(0)
}
console.log(reverse("hello world"))
Starting with reverse("hello_world") we can easily trace the evaluation. Whenever the input word is non-empty, a new stack frame is opened with the recursive call to the sub-problem reverse(word.substring(1)). The ... + word.charAt(0) portion remains in the calling frame and only resumes after the descendant frame returns -
reverse("hello world") =
reverse("ello world") + "h" =
reverse("llo world") + "e" + "h" =
reverse("lo world") + "l" + "e" + "h" =
reverse("o world") + "l" + "l" + "e" + "h" =
reverse(" world") + "o" + "l" + "l" + "e" + "h" =
reverse("world") + " " + "o" + "l" + "l" + "e" + "h" =
reverse("orld") + "w" + " " + "o" + "l" + "l" + "e" + "h" =
reverse("rld") + "o" + "w" + " " + "o" + "l" + "l" + "e" + "h" =
reverse("ld") + "r" + "o" + "w" + " " + "o" + "l" + "l" + "e" + "h" =
reverse("d") + "l" + "r" + "o" + "w" + " " + "o" + "l" + "l" + "e" + "h" =
reverse("") + "d" + "l" + "r" + "o" + "w" + " " + "o" + "l" + "l" + "e" + "h" =
Here we meet the base case, recursion stops and empty string is returned. Now all of the open stack frames collapse, starting with the deepest frame returning its value to its caller -
"" + "d" + "l" + "r" + "o" + "w" + " " + "o" + "l" + "l" + "e" + "h" =
"d" + "l" + "r" + "o" + "w" + " " + "o" + "l" + "l" + "e" + "h" =
"dl" + "r" + "o" + "w" + " " + "o" + "l" + "l" + "e" + "h" =
"dlr" + "o" + "w" + " " + "o" + "l" + "l" + "e" + "h" =
"dlro" + "w" + " " + "o" + "l" + "l" + "e" + "h" =
"dlrow" + " " + "o" + "l" + "l" + "e" + "h" =
"dlrow " + "o" + "l" + "l" + "e" + "h" =
"dlrow o" + "l" + "l" + "e" + "h" =
"dlrow ol" + "l" + "e" + "h" =
"dlrow oll" + "e" + "h" =
"dlrow olle" + "h" =
And finally we can close the outermost call to reverse and return the result -
"dlrow olleh"
In this program, the stack is used to sequence operations and combine resulting values in the intended order. If the input word was significantly large, you would run into a stack overflow because too many frames would be opened and you essentially break the JavaScript runtime limit for such computations. Memory or heap is only used for all of the intermediate string allocations.
The ever-growing stack in the program above demonstrates a recursive process. This is characteristic of any recursive program that doesn't use a tail call. A tail call is simply the last call in your function, returned directly to its caller -
function reverse(word) {
function loop(r, w) {
if (w == "") return r
return loop(w[0] + r, w.substr(1)) // <- loop is the last called
}
return loop("", word)
}
console.log(reverse("hello world"))
This demonstrates a linear iterative process, so called because the process created by the recursive function stays flat and straight like a line -
reverse("hello world") =
loop("", "hello world") =
loop("h", "ello world") =
loop("eh", "llo world") =
loop("leh", "lo world") =
loop("lleh", "o world") =
loop("olleh", " world") =
loop(" olleh", "world") =
loop("w olleh", "orld") =
loop("ow olleh", "rld") =
loop("row olleh", "ld") =
loop("lrow olleh", "d") =
loop("dlrow olleh", "") =
"dlrow olleh"
Some languages have tail call optimization which means the recursive function like the one above would be safe from the stack overflow problem. The compiler or runtime effectively converts the recursive call into a loop -
function reverse(word) {
function loop(r, w) {
while (true) {
if (w == "") return r
r = w[0] + r
w = w.substr(1)
}
}
return loop("", word)
}
console.log(reverse("hello world"))
Above only 2 frames are used and memory allocations of 3 bindings, word, r and w. Memory allocations to compute + and w.substr(1) are also made and are recaptured by the runtime's automatic garbage collector.
In ECMAScript 6, tail call elimination was added to the specification however it is unsupported in almost every popular runtime and that is unlikely to change. That doesn't mean however we are constrained to writing recursive programs using imperative style while loops. There are various techniques to make recursive programs safe even in JavaScript, even in runtimes that do not support this optimization.
Consider this implementation of reverse using loop and recur -
const reverse = word =>
loop
( (r = "", w = word) =>
w == ""
? r
: recur(w[0] + r, w.substr(1))
)
The non-recursive loop and recur functions are generic and allow us to use them to write most recursive programs that will not cause a stack overflow -
const recur = (...values) =>
({ recur, values })
const loop = run =>
{ let r = run ()
while (r && r.recur === recur)
r = run (...r.values)
return r
}
console.log(reverse("hello world"))
This has a very similar performance profile to the while loop above. Only 2 stack frames and 3 bindings with an small overhead of some immediately garbage-collected values like +, substr and recur -
Expand the snippet below to verify the result in your own browser -
const recur = (...values) =>
({ recur, values })
const loop = run =>
{ let r = run ()
while (r && r.recur === recur)
r = run (...r.values)
return r
}
const reverse = word =>
loop
( (r = "", w = word) =>
w == ""
? r
: recur(w[0] + r, w.substr(1))
)
console.log(reverse("hello world"))
"dlrow olleh"
In fact, any recursive program, tail call or not, can be made stack-safe using various techniques. If this sort of thing sounds interesting to you, see this related Q&A for an in-depth exploration of the topic.
Related
I gave an example of using .tofixed() with math, functions, and arrays, to a beginner coder friend who has been reviewing these topics in his class.
const bananaX = 9;
const bananaY = 2.9768;
bananaArray = [bananaX , bananaY];
console.log("X before array = " + bananaX);
console.log("Y before array = " + bananaY + '\n')
console.log("X,Y after array = " + bananaArray + '\n')
console.log("Value of X in array: " + bananaArray[0]+ '\n')
console.log("Value of Y in array: " + bananaArray[1]+ '\n')
function bananaDivision (bananaArray){
console.log("Value of X after function = " + bananaX);
console.log("Value of Y after function = " + bananaY + '\n')
let bananaDivided = Math.abs(bananaX/bananaY );
console.log (`X divided by Y = + ${bananaDivided}` + '\n')
let bananaFixed = bananaDivided.toFixed(2);
console.log("After using .toFixed(2) : " + bananaFixed + '\n');
};
bananaDivision();
They were understanding and following along no problem.
Then they asked me - "What if we put a decimal in the .toFixed ?"
So I ran:
const bananaX = 9;
const bananaY = 2.9768;
bananaArray = [bananaX , bananaY];
console.log("X before array = " + bananaX);
console.log("Y before array = " + bananaY + '\n')
console.log("X,Y after array = " + bananaArray + '\n')
console.log("Value of X in array: " + bananaArray[0]+ '\n')
console.log("Value of Y in array: " + bananaArray[1]+ '\n')
function bananaDivision (bananaArray){
console.log("Value of X after function = " + bananaX);
console.log("Value of Y after function = " + bananaY + '\n')
let bananaDivided = Math.abs(bananaX/bananaY );
console.log (`X divided by Y = + ${bananaDivided}` + '\n')
let bananaFixed = bananaDivided.toFixed(2);
let bananaFixed1 = bananaDivided.toFixed(.69420);
let bananaFixed2 = bananaDivided.toFixed(1.69420);
console.log("After using .toFixed(2) : " + bananaFixed + '\n');
console.log("After using .toFixed(.69420) : " + bananaFixed1 + '\n');
console.log("After using .toFixed(1.69420) : " + bananaFixed2 + '\n');
};
bananaDivision();
I explained it as that .toFixed is looking at the first number within the () and that the decimals are ignored.
Am I correct? For my own curiousity, is there a crazy way to break .toFixed() so that it actually uses decimals? I'm experimenting atm but wanted to know if someone already figured that out.
I explained it as that .toFixed is looking at the first number within the () and that the decimals are ignored.
This would be correct. That is essentially what happens.
For full correctness, the input of toFixed() will be converted to an integer. The specification states that the argument must first be converted to a number - NaN will be converted to a zero. Numbers with a fractional part will be rounded down.
Which means that if you pass any number, you essentially get the integer part of it.
It also means that non-numbers can be used:
const n = 3;
console.log(n.toFixed("1e1")); // 1e1 scientific notation for 10
You're close, since toFixed() expects an integer it will handle converting decimal numbers before doing anything else. It uses toIntegerOrInfinity() to do that, which itself uses floor() so the number is always rounded down.
Most of Javascript handles type conversion implicitly, so it's something you should really understand well if you don't want to run into problems. There's a free book series that explains that concept and a lot of other important Javascript knowledge very well, it's called You Don't Know JS Yet.
just a demo how .tofixed works !!!!!!
function roundFloat(x, digits) {
const arr = x.toString().split(".")
if (arr.length < 2) {
return x
}else if(arr[1] === ""){
return arr[0]
}else if(digits < 1){
return arr[0]
}
const st = parseInt(x.toString().split(".")[1]);
let add = false;
const rudgt = digits
const fX = parseInt(st.toString().split("")[rudgt]);
fX > 5 ? add = true : add = false
nFloat = parseInt(st.toString().split("").slice(0, rudgt).join(""))
if (add) {
nFloat += 1
}
const repeat0 = (() => {
if (rudgt - st.toString().length < 0) {
return 0
}
return rudgt - st.toString().length
})()
const output = x.toString().split(".")[0] + "." + nFloat.toString() + "0".repeat(repeat0);
return output
}
console.log(roundFloat(1.200, 2))
Im looping over a collection of coordinate values and doing math on the coordinates to see if the calculated values are in a hashmap. if they are in the hash map then I want to run an additional function. since I had multiple cases I wanted to check for each coord in the collection, I figured a switch statement would be cool to use to replace my if statements so all my checks could be visually and logically grouped. When I replaced my if statements with a switch, my code returned bad results. When I debugged, I realized the switch statements would sometimes execute even when the case was false(I added console.logs to output the result of the same switch condition and it would print false, but should only run when true). Here is a small example:
var idm = {0:1, 3:1, 9:1, 10:1, 11:1, 12:1, 20:1, 21:1, 23:1}
var findNeighbors = function(b) {
var u,d,l,r,lRow,rRow;
var currentBuilding = parseInt(b);
var currRow = Math.floor(currentBuilding/column);
//remove value from map so we dont recount it.
delete idm[currentBuilding];
u = currentBuilding - column;
d = currentBuilding + column;
l = currentBuilding - 1;
lRow = Math.floor(l/column);
r = currentBuilding + 1;
rRow = Math.floor(r/column);
console.log("current idx:" + currentBuilding);
console.log("u:" + u + ", d:" + d + ", l:" + l + " r:" + r);
// debugger;
switch(true) {
case (idm.hasOwnProperty(u) === true):
console.log((idm.hasOwnProperty(u)));
console.log("map has " + currentBuilding + " -> u: " + u);
findNeighbors(u);
case (idm.hasOwnProperty(d) === true):
console.log((idm.hasOwnProperty(d)));
console.log("map has " + currentBuilding + " -> d: " + d);
findNeighbors(d);
case (lRow === currRow && idm.hasOwnProperty(l) === true):
console.log((lRow === currRow && idm.hasOwnProperty(l)));
console.log("map has " + currentBuilding + " -> l: " + l);
findNeighbors(l);
case (rRow === currRow && idm.hasOwnProperty(r) === true):
console.log((rRow === currRow && idm.hasOwnProperty(r)))
console.log("map has " + currentBuilding + " -> r: " + u);
findNeighbors(r);
}
console.log("---------------------------");
}
I figured a switch statement would be cool to use to replace my if statements so all my checks could be visually and logically grouped.
Well, write code that works not code that looks cool. You were forgetting break statements, so the execution flow fell through - without evaluating the other case expressions after the first one matched. Btw switching on a constant is a horrible (uncool) practice.
Use standard if/else instead.
I am currently trying to complete an assignment for an intro2Javascript course. The question basically asks me to return a string of multiples of 2 parameters (num, numMultiple). Each time it increments the value i until i = numMultiple. For example:
5 x 1 = 5\n
5 x 2 = 10\n
5 x 3 = 15\n
5 x 4 = 20\n
This was my attempt:
function showMultiples(num, numMultiples) {
var result;
for (i = 1; i <= numMultiples; i++) {
result = num * i
multiples = "" + num + " x " + i + " = " + result + "\n"
return (multiples)
}
}
...and because the assignment comes with pre-written console logs:
console.log('showMultiples(2,8) returns: ' + showMultiples(2, 8));
console.log('showMultiples(3,2) returns: ' + showMultiples(3, 2));
console.log('showMultiples(5,4) returns: ' + showMultiples(5, 4));
console.log('\n');
This is my output:
showMultiples(2,8) returns: 2 x 1 = 2
Scratchpad/1:59:1
showMultiples(3,2) returns: 3 x 1 = 3
Scratchpad/1:60:1
showMultiples(5,4) returns: 5 x 1 = 5
UPDATE
You were doing two things incorrectly:
1) You were returning after the first iteration through your loop
2) You were assigning to multiples instead of appending to it.
Since you want to gather all the values and then show the final result first, I add all of the values to an array, and then use unshift() to add the final element (the result) to the beginning of the array. Then I use join() to return a string representation of the desired array.
function showMultiples(num, numMultiples) {
var result;
var multiples = [];
for (let i = 1; i <= numMultiples; i++) {
result = num * i
multiples.push("" + num + " x " + i + " = " + result + "\n")
}
multiples.unshift(multiples[multiples.length-1]);
return (multiples.join(''))
}
console.log('showMultiples(2,8) returns: ' + showMultiples(2, 8));
console.log('showMultiples(3,2) returns: ' + showMultiples(3, 2));
console.log('showMultiples(5,4) returns: ' + showMultiples(5, 4));
console.log('\n');
You need to declare all variables, because without you get global variables (beside that it does not work in 'strict mode').
The second point is to use multiples with an empty string for collecting all intermediate results and return that value at the end of the function.
For keeping the last result, you could use another variable and append that value at the end for return.
function showMultiples(num, numMultiples) {
var i,
result,
multiples = "",
temp = '';
for (i = 1; i <= numMultiples; i++) {
result = num * i;
temp = num + " x " + i + " = " + result + "\n";
multiples += temp;
}
return temp + multiples;
}
console.log('showMultiples(2,8) returns: ' + showMultiples(2, 8));
console.log('showMultiples(3,2) returns: ' + showMultiples(3, 2));
console.log('showMultiples(5,4) returns: ' + showMultiples(5, 4));
As other answers say, your problem is in multiple.
You are clearing multiple every iteration and storing the new value, but you do not want that, you want to add the new result, and to do so you use this code:
multiples = multiple + "" + num + " x " + i + " = " + result + "\n"
which can be compressed in what the rest of the people answered:
multiples += "" + num + " x " + i + " = " + result + "\n"
Probably you already know, but to ensure:
a += b ---> a = a + b
a -= b ---> a = a - b
a *= b ---> a = a * b
and there are even more.
I am not good at algorithm analysis. The source code is from this place: https://repl.it/KREy/4
Instead of dynamic programming, this piece of code uses a cache to optimize the BigO by sacrificing memory. However, I just don't know how to calculate the BigO mathematically after this cache mechanism is added. May anyone genius give an explanation?
To ease reading I will copy and paste them in the following space:
// using cache to optimize the solution 1 from http://www.techiedelight.com/longest-palindromic-subsequence-using-dynamic-programming/
const cache = {};
var runningtime = 0;
var runningtimeWithCache = 0;
function computeGetLP(x, start, end){
const answer = a => {
runningtime++;
return a;
}
console.log("try to compute: " + x + " " + start + " " + end + " ");
if(start > end)
return answer(0);
if(start == end)
return answer(1);
if(x[start] == x[end])
return answer(2 + computeGetLP(x, start+1, end-1));
return answer(Math.max(computeGetLP(x, start+1, end),
computeGetLP(x, start, end-1)));
}
function computeGetLPWithCache(x, start, end){
const answer = a => {
runningtimeWithCache ++;
console.log("do cache: " + x + " " + start + " " + end + " is " + a);
cache["" + x + start + end] = a;
return a;
}
console.log("try to compute: " + x + " " + start + " " + end + " ");
if(cache["" + x + start + end]){
console.log("hit cache " + x + " " + start + " " + end + " "+ ": ",cache["" + x + start + end]);
return cache["" + x + start + end];
}
if(start > end)
return answer(0);
if(start == end)
return answer(1);
if(x[start] == x[end])
return answer(2 + computeGetLPWithCache(x, start+1, end-1));
return answer(Math.max(computeGetLPWithCache(x, start+1, end),
computeGetLPWithCache(x, start, end-1)));
}
const findLongestPadlindrom1 = s => computeGetLPWithCache(s, 0, s.length-1)
const findLongestPadlindrom2 = s => computeGetLP(s, 0, s.length-1)
const log = (function(){
var lg = [];
var output = function(text){
lg.push(text);
}
output.getRecord = function(){
return lg;
}
return output;
})();
log("Now let's do it with cache")
log("result: "+findLongestPadlindrom1("ABBDCACB"))
log("running time is: " + runningtimeWithCache)
log("Now let's do it without cache")
log("result: "+findLongestPadlindrom2("ABBDCACB"))
log("running time is: " + runningtime)
log.getRecord();
I'm not an expert in algorithms either, but I remember cache techniques like this from Introduction to Algorithms, chapter 15, just beside Dynamic Programming. It has the same big O to DP, which is O(n^2) in your case.
Each call to computeGetLPWithCache() costs O(1) for it does not contain loops. Consider the worst case where x[start] != x[end] in each recursion. How many times are we going to call computeGetLPWithCache()?
Let n = length(x), [start, end] represent a call to computeGetLPWithCache(x, start, end), and F(n) equals the number of calls. In computeGetLPWithCache(x, 0, n), 2 sub calls - [0, n-1] and [1, n] - are issued. The former costs F(n), and when we're doing the latter, we discover that in each iteration, the first call's [start, end] range is a true subset of [0, n-1] whose result is already written to cache during the [0, n-1] call, thus no need for recursing. Only the second call which has the element n in it has to be calculated; there're n such calls [1,n][2,n][3,n]...[n,n] (one in each stack layer), so F(n+1) = F(n) + O(n).
F(n) = F(n-1) + O(n-1) = F(n-2) + O(n-2) + O(n-1) = ... = O(1+2+...+(n-1)) = O(n^2).
Hope I've got the meaning through. Replies are welcome.
This function pretend to read from a csv and populate a table of input field . Having rows and columns I need to nest loops or call function in a loop, or even call function by itself . I tried already all the combinations . In the best case it populates only the first row ( A1,B1,C1, etc.. ) , it knows exactly the starting point in buffer and everything apparently is correct in the numbers it makes , BUT the outer loop is broken after the first iteration (whatever I do with my code about inner function, or inner loop, or nested function ) . I tried "continue", "return", the closure stuff.
function getPage(p) {
var init = buffer.indexOf("Page:" + p + ",");
var init_index = init + 1;
var linea = "";
var r = 1;
for (var x in buffer) {
if (x > init_index) {
linea = buffer[x].split(",");
read_line(linea, r);
r++;
}
}
}
function read_line(l, r) {
var debug = new Array();
var n = 0;
while (n < l.length + 1) {
var colons = ["A" + r, "B" + r, "C" + r, "D" + r, "E" + r, "F" + r, "G" + r, "H" + r, "se_ckbox" + r, "me_ckbox" + r, "sim_ckbox" + r, "I" + r, "J" + r, "K" + r, "L" + r, "M" + r, "N" + r, "O" + r, "P" + r, "Q" + r, "R" + r, "S" + r, "T" + r, "U" + r, "V" + r];
var field = l[n + 1];
debug.push("col:" + colons[n].toString() + "\nVAL:" + field + "\n");
document.getElementById(colons[n].toString()).value = field;
document.getElementById("debug").value = debug.toString();
n++;
}
return;
}