const writeText = function(word, n) {
if(n >= 5) {
return '';
}
n = n + 1;
return word + writeText(word + n);
};
debugger;
writeText('hello', 1);
I want to write a code which will return 'hello1', 'hello2', ... 'hello5' using recursion.
My code didn't work so I used debugger to see what's wrong. It shows that after the first recursive call, n turns into undefined. Can anyone help me out and show what's wrong with this code?
Some hints:
When using recursive calls, you better use a named function instead of a function expression, because the name does not change with assingment of the function. This is necessary for recursive calling, which might call a unknown function.
Exit function if the value is greater than 5.
You need to display the actual value of n (and maybe a new line).
Call the function with the word and a second parameter of the incremented value.
function writeText(word, n) {
if (n > 5) {
return '';
}
return word + n + '\n' + writeText(word, n + 1);
};
console.log(writeText('hello', 1));
you were almost there - the answer provided by #Nina Scholz is good.
Here an approach you could use using ES6 template literals to keep us up to date.
Since you're new some comments about the syntax which you could google:
=> : arrow functions
`text` : template literals
const writeText = (word, n) =>
`${word+n} ${n < 5 ? writeText(word, n+1) : ''}`;
console.log(writeText('hello',1))
```
Related
I am learning javascript and while I was on Codewars I couldn't figure this problem out so I looked for a solution.
This is the solution I found and I just don't understand why there needs to be a second return statement inside the map method. If anyone could give me some insight it would be much appreciated. Thank you.
let spinWords = (str) => {
return str.split(' ').map(function(string) {
return (string.length > 4) ? string.split('').reverse().join('') : string
}).join(' ');
}
The .map function accepts a callback, and that callback is a function that runs for every item in the array. For a simple example:
const upperCase = str => str.toUpperCase();
console.log(
['a', 'b'].map(upperCase)
);
Above, it's called with both elements of the array: upperCase('a') and upperCase('b'), and the results are put into the new array, which is then console.logged.
You can also define functions inline and pass that to .map:
console.log(
['a', 'b'].map(str => str.toUpperCase())
);
Your code's original
str.split(' ').map(function(string) {
return (string.length > 4) ? string.split('').reverse().join('') : string
}).join(' ');
is doing the same sort of thing, except that
the body of the function is more complicated, and
it's using the function keyword, rather than an arrow function, and thus needs the return keyword in order to return values (unlike arrow functions in some circumstances).
The second return is a callback function. Try looking at it like this:
function reverseWord(string) {
return (string.length > 4) ? string.split('').reverse().join('') : string
}
let spinWords = (str) => {
return str.split(' ').map(reverseWord).join(' ');
}
So reverseWord is your callback function, with its own return. The value it returns is the value used in the mapped array.
I'm basically trying to write a function which when called, performs the action of the endsWith() method
I've tried iterating through the original string and the string to be tested using for loops
function confirmEnding(str, target) {
let strWord = '';
let targetWord = '';
for (let i = (target.length - 1); i >= 0; i--) {
targetWord.concat(target.charAt(i))
for (let j = (str.length - 1); j >= 0; j--) {
strWord.concat(str.charAt(j));
}
}
if (strWord == targetWord) {
return true
} else {
return false
}
}
Now anytime i call the function, it returns true, what's wrong with my code?
concat returns the new string.
Use strWord += whatever; instead.
Primarily, what's wrong with it is that you're using nested loops. There's no need to do that.
The simple, easy way (other than using endsWith!) is to grab the end of str (using the length of target), then compare that substring against target:
function confirmEnding(str, target) {
return str.length >= target.length && str.slice(-target.length) === target;
}
Re your actual code:
concat doesn't modify the string you call it on, it returns a new string (it has to; strings are immutable [unchangeable] in JavaScript)
If you fix #1, your outer loop just copies target to targetWord but in reverse order. Your inner loop copies str to strWord in reverse order repeatedly, so you'll end up with target.length copies of it in strWord.
Nothing in the function tries to take only part of str (the last part, with the same length as target). You end up comparing the full strings (if you fix #1 and #2), not the substring at the end of str.
Finally, just a side note: Any time you find yourself writing:
if (a == b) {
return true;
} else {
return false;
}
you can more concisely and idiomatically write
return a == b;
which does exactly the same thing. :-)
Trying to write a similar recursive solution to the one described on: http://www.geeksforgeeks.org/longest-common-subsequence/ but it does not work. It outputs one. Anyone have an idea of why?
LCS_seq_req = (str1, str2) => {
m=str1.length;
n=str2.length;
str1_cut = str1.slice(0, m-1)
str2_cut = str2.slice(0, n-1)
if (m===0 || n===0) {
return 0
}
else if (str1.slice(m-1, m) === str2.slice(n-1, n) ) {
return LCS_seq_req(str1_cut, str2_cut) + 1
} else {
res_1 = LCS_seq_req(str1_cut, str2)
res_2 = LCS_seq_req(str1,str2_cut)
return Math.max(res_1, res_2)
}
}
LCS_seq_req("AGGTAB", "GXTXAYB")
In JavaScript, unlike (say) Python, assigning to a variable inside a function does not implicitly declare it as a local variable. Instead, you need to explicitly declare it using the var keyword; otherwise you get a global variable.
More specifically, your problem is that this line:
res_1 = LCS_seq_req(str1_cut, str2)
has the side-effect of mutating the global variable str2_cut, causing this line:
res_2 = LCS_seq_req(str1,str2_cut)
to compute the wrong value. If you add var in the right places, you'll get the right answer.
Incidentally, Eric Lippert has written a blog post, https://ericlippert.com/2014/03/05/how-to-debug-small-programs/, which gives very good advice for how to debug this sort of problem on your own.
I looked at the Naive recursive Python implementation of LCS problem you give and converted Python code into JS code. Hope it will help.
LCS_seq_req = (str1, str2, m, n) => {
if(m == 0 || n == 0)
return 0;
else if(str1.charAt(m-1) === str2.charAt(n-1))
return 1 + LCS_seq_req(str1, str2, m-1, n-1);
else
return Math.max(LCS_seq_req(str1, str2, m, n-1), LCS_seq_req(str1, str2, m-1, n));
}
var X = "AGGTAB";
var Y = "GXTXAYB";
console.log(LCS_seq_req(X , Y, X.length, Y.length)); //6
How do you output a new array using recursion without declaring an empty array outside of the function? Another way of doing it will be creating an inner function and then return newFunction(), but it is not allowed as the task is to call the function itself. Here's what I have so far:
var newArr=[];
var range = function(x, y) {
if(x === y-1){
return newArr;
}
if(x < y){
newArr.push(x+1);
newArr = range(x+1,y);
}
else{
newArr.push(x-1);
newArr = range(x-1,y);
}
return newArr;
};
range(2,10) //[3,4,5,6,7,8,9]
So the key to this kind of thinking is understanding that you should be creating a lot of arrays.
Looking at a slightly different example...
A factorial is a number which goes backwards, through positive integers, multiplying each term with the term below it, and is written like 5!.
These are helpful when you find yourself asking questions like:
"How many permutations of ____ are there?"
"Given these 5 things, how many permutations can I arrange them in, from left to right?"
5! // =>
5 x 4 x 3 x 2 x 1 // =>
120
You could see how we could build a loop and set a variable for a counter, and a variable for the total, and multiply the current total by the current value of the counter we're decrementing.
But instead of doing that, we can try to use recursion.
First, think about how we could simplify that 5 x 4 x ... into one repeated step.
Really, 2! is 2 x 1. 3! is 3 x 2 x 1, which happens to be 3 x 2!.
So the general case might be something like: n! == n x (n - 1)!
So I might write a generalized function which does something like this:
// DO NOT RUN THIS FUNCTION!
function factorial (n) {
return n * factorial(n - 1);
}
So if I run factorial(5) and use my imagination, we can see that the program is doing something like:
factorial(5)
=> return 5 * factorial(5-1)
=> return 4 * factorial(4-1)
=> return 3 * factorial(3-1)
=> ...
Can you see any problems with the function as-is?
I said at the beginning that factorials (in this simplified case) are over positive integers.
How does my function know to stop when the integers stop being positive?
It doesn't, currently. Which is why the above implementation attempts to run forever, and will freeze the browser, while it tries to, until it gets thousands or tens of thousands of functions deep, before it says that you've reached the maximum depth of the call stack and explodes.
What we really need is a condition or a set of conditions, which we use to determine when we're done.
This is a base-case.
if (shouldStop(n)) {
return defaultValue;
}
Or in our case:
function factorial (n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}
Now, when we run the function, we have:
factorial(5)
=> 5 * factorial(5 - 1)
=> 4 * factorial(4 - 1)
=> 3 * factorial(3 - 1)
=> 2 * factorial(2 - 1)
=> 1
=> 2 * 1
=> 3 * 2
=> 4 * 6
=> 5 * 24
=> 120
This is recursion.
And because of where the call is (returned at the very end of whatever branch you're in) it's a special kind of recursion (tail recursion), which allows some languages to optimize the code, replacing the function call with the contents of the function call, and thus skip adding to the call-stack like the first version (future versions of JS will support this power).
In more modern JS, I might rewrite it to look something like
const factorial = n => n <= 1 ? 1 : factorial(n - 1);
So now, what about other cases?
Well, sometimes, you need to make sure you're passing more things in.
Think about what your problem is, and what kinds of counters or flags or collectors you need, in order to do your job.
Here's one:
function makeNumberString (current, max, initialString) {
var str = initialString || ""; // maybe I don't have one yet
var currentString = str.concat(current.toString());
if (current > max) {
return initialString;
}
return makeNumberString(current + 1, max, currentString);
}
makeNumberString(0, 9); // "0123456789"
There are other ways of filling that function out, to make it do the same thing.
Note that currentString there is always a brand new string, made by joining the string that I was given with the new value I was passed. I'm not actually modifying the original string, but creating a new copy [HINT!!].
I hope that helps you.
you can simply do like this;
var range = (x,y,a=[]) => (++x < y && (a = range(x,y,a.concat(x))),a),
arr = range(2,10);
console.log(arr);
Note that the returned array is a parameter of the function and is passed to successive recursive calls.
There are many ways to skin this cat.
The simple way: create an array with the first value in it, then
concatenate the remaining values to it.
var range = function(x,y){
return x+1 >= y ? [] : [x+1].concat(range(x+1, y));
}
console.log(JSON.stringify(range(1, 10)));
The array is being constructed from right to left. Notice how the
recursive call to range is not the last thing the function does
before it returns: concatenation of the array follows.
We can also rewrite the function to be tail recursive with an accumulator as a parameter.
var range2 = function(x,y,a){
a = a || [];
return x+1 >= y ? a : range2(x+1, y, a.concat(x+1));
}
console.log(JSON.stringify(range2(1, 10)));
Now the call to range2 is the last thing the function does before
it returns. ES6 compliant JS engines are required to
optimise
calls in tail position (in strict mode) by discarding the execution
context from the stack.
Notice how we're now constructing the array from left to right.
You can avoid the extra parameter by using a helper function.
I've used an inner function, but it doesn't have to be.
var range3 = function(x,y){
var r = function(x,y,a){
return x+1 >= y ? a : r(x+1, y, a.concat(x+1));
}
return r(x, y, []);
}
console.log(JSON.stringify(range3(1, 10)));
Tail recursive using continuation passing style.
var range4 = function(x,y){
var r = function(x,y,c){
return x+1 >= y ? c([]) : r(x+1, y, function(a){
return c([x+1].concat(a));
});
}
return r(x, y, function(a){return a;});
}
console.log(JSON.stringify(range4(1, 10)));
Notice the similarity with the original range: the array is
constructed in reverse. This is trickier to get your head around and
may be something you never need, but it doesn't hurt to be aware of
it.
Try this:
function rangeRecursive(start, end) {
if(start === end){
return end;
} else if(start > end){
return [];
} else {
return [start].concat(rangeRecursive(++start, end));
}
}
console.log(rangeRecursive(4, 15));
I'm trying to use memoization to optimize an explicitly self recursive implementation of the Fibonacci function. The implementation which is fairly standard (a simple and rather naïve implementation though to focus on the actual problem) follows.
Function.prototype.memoize = function () {
var originalFunction = this,
slice = Array.prototype.slice;
cache = {};
return function () {
var key = slice.call(arguments);
if (key in cache) {
return cache[key];
} else {
return cache[key] = originalFunction.apply(this, key);
}
};
};
Now, when creating and memoizing a function as follows, this works1. (Scenario 1)
var fibonacci = function (n) {
return n === 0 || n === 1 ? n : fibonacci(n - 1) + fibonacci(n - 2);
}.memoize();
console.log(fibonacci(100));
However, the following does not.2 (Scenario 2)
var fibonacci = function (n) {
return n === 0 || n === 1 ? n : fibonacci(n - 1) + fibonacci(n - 2);
};
console.log(fibonacci.memoize()(100));
And neither does this.2 (Scenario 3)
function fibonacci(n) {
return n === 0 || n === 1 ? n : fibonacci(n - 1) + fibonacci(n - 2);
}
console.log(fibonacci.memoize()(100));
My assumption is that because of the different ways of calling memoize() on the functions, something is changing. Note that the functions are otherwise identical. I suppose that this could be due to the fact that other than in the first instance, only the first call is memoized, not the recursive calls.
Question
If my supposition above is indeed correct, then why is this happening? Can someone explain in detail how the latter two scenarios differ from the first?
1To work in this instance means to return the 100th Fibonacci number since it is only possible to compute it recursively if memoization is used.
2To not work is to crash the browser.
Yes, it's right that only the first call is memoized in the second and third scenarios.
In the first scenario the reference to the original function only exists as a value, then memoize is applied to that and the fibonacci variable contains the reference to the memoized function.
In the second and third scenario fibonacci is a reference to the original function. The value of the expression fibonaci.memoize() that contains the reference to the memoized function only exist as a value before it is called once.
The memoize method doesn't change the original function, instead it returns a new function that wraps the original function. The original function is unchanged, and to use the memoization you have to call the function returned by the memoize method.
In the first scenario when the function makes a recursive call to fibonacci, it's the memoized function that is used. In the second and third scenarios when the recursive call is made, fibonacci is the original function instead.