Recursive Function - RangeError: Maximum call stack size exceeded - JavaScript - javascript

I'm trying to make a set of functions that add the digits of a number. But with giant numbers, the maximum amount of calls is exceeded. I would like to resolve this issue.
My code - (Example of error in calling the function at the end.):
const recursiveSum = (arr, iter) => {
let strNums = arr.reduce((acc, curr) => acc + curr, 0);
let arrNums = strNums.toString().split('').map(Number);
let res = arrNums.reduce((acc, curr) => acc + curr, 0);
let newArr = res.toString().split('');
const resLength = res.toString().split('').length;
if (iter === 0 || resLength === 1) {
return res;
} else {
return recursiveSum(newArr, iter - 1);
}
}
function getTheP(arr, k, p) {
console.log(k);
arr.map(num => {
p.push(num);
});
if (k === 0) {
return p;
} else {
getTheP(arr, --k, p);
}
}
function superDigit(n, k) {
let p;
const splitArr = n.toString().split('');
p = getTheP(splitArr, k, []);
const response = recursiveSum(p, k);
return console.log(response);
}
superDigit('4757362', 10000);

Related

Implement redux compose function but get RangeError

I am trying to re-implement redux compose function, instead of using reduce i use a for loop, here is my code:
function compose(...funcs) {
if (funcs.length === 0) {
return (arg) => arg;
}
if (funcs.length === 1) {
return funcs[0];
}
let result;
for (let i = funcs.length - 1; i > -1; i--) {
result = result
? (...args) => funcs[i](result(...args))
: (...args) => funcs[i](...args);
}
return result;
}
// test
function fn1(x) {
return x + 1;
}
function fn2(x) {
return x * 10;
}
function fn3(x) {
return x - 1;
}
console.log(compose(fn3, fn2, fn1)(10)); // 109
It is expected to log 109 since (10 + 1) * 10 - 1 is 109, however it gives me such error:
RangeError: Maximum call stack size
Looks like i am doing some recursion but all i did is just a for loop, no sure where is the problem of my code?
I think the issue is like the below example:
a = () => 2;
a = () => 3 * a();
console.log(a);
// this prints () => 3 * a() in console
// so when you call a(), it will call 3 * a(), which will again call 3 * a() and so on
// leading to infinite recursion
My solution is slightly different using bind function based on this reference link: https://stackoverflow.com/a/6772648/4688321.
I think bind creates a new copy of the function result and binds it to a new object. Not using bind leads to recursion because then the code becomes like the above example, result calls result.
function compose(...funcs) {
if (funcs.length === 0) {
return (arg) => arg;
}
if (funcs.length === 1) {
return funcs[0];
}
let result;
for (let i = funcs.length - 1; i > -1; i--) {
if (i == funcs.length - 1)
result = (...args) => funcs[i](...args);
else {
let temp = result.bind({});
result = (...args) => funcs[i](temp(...args));
}
}
return result;
}
// test
function fn1(x) {
console.log("fn1");
return x + 1;
}
function fn2(x) {
console.log("fn2");
return x * 10;
}
function fn3(x) {
console.log("fn3");
return x - 1;
}
//console.log(compose(fn3, fn2, fn1));
let ret = compose(fn3, fn2, fn1);
console.log(ret(10)); // 109
Rather than trying to combine functions at the time of compose, it seems much easier to combine them at the time the resulting function is called:
function compose(...funcs) {
if (funcs.length === 0) {
return (arg) => arg
}
return function (...args) {
let result = funcs .at (-1) (...args)
for (let i = funcs.length - 2; i > -1; i--) {
result = funcs [i] (result)
}
return result
}
}
// test
function fn1(x) {
return x + 1;
}
function fn2(x) {
return x * 10;
}
function fn3(x) {
return x - 1;
}
console.log(compose(fn3, fn2, fn1)(10)); // 109
However, again, reduce make for a much cleaner implementation:
const compose = (...fns) => (arg) =>
fns .reduceRight ((a, fn) => fn (a), arg)
or if you want to allow the rightmost function to receive multiple variables, then
const compose = (...fns) => (...args) =>
fns .reduceRight ((a, fn) => [fn (...a)], args) [0]

Stack-safe mutual recursion without leaking implementation details on the call side

I generalized clojure's loop/recur trampoline so that it works with indirect recursion:
const trampoline = f => (...args) => {
let acc = f(...args);
while (acc && acc.type === recur) {
let [f, ...args_] = acc.args;
acc = f(...args_);
}
return acc;
};
const recur = (...args) =>
({type: recur, args});
const even = n =>
n === 0
? true
: recur(odd, n - 1);
const odd = n =>
n === 0
? false
: recur(even, n - 1);
console.log(
trampoline(even) (1e5 + 1)); // false
However, I have to call the trampoline explicitly on the call side. Is there a way to make it implicit again, as with loop/recur?
Btw., here is loop/recur:
const loop = f => {
let acc = f();
while (acc && acc.type === recur)
acc = f(...acc.args);
return acc;
};
const recur = (...args) =>
({type: recur, args});
Clearly since you want the trampolining to be called, it can't be skipped altogether. The simplest thing would simply be to wrap those trampolined calls in the API you want, perhaps something like this:
// Utility code
const trampoline = f => (...args) => {
let acc = f(...args);
while (acc && acc.type === recur) {
let [f, ...args_] = acc.args;
acc = f(...args_);
}
return acc;
};
const recur = (...args) =>
({type: recur, args});
// Private implementation
const _even = n =>
n === 0
? true
: recur(_odd, n - 1);
const _odd = n =>
n === 0
? false
: recur(_even, n - 1);
// Public API
const even = trampoline(_even);
const odd = trampoline(_odd);
// Demo code
console.log(
even (1e5 + 1)); // false
console.log(
odd (1e5 + 1)); // true

How can i mix all of the arithmetical operators to finish my calculator

I had a calculator Project.I can multiply,add,divide and substract all the numbers in the input and get the result like output.
But i need help.How is the solution when the user
in same input wants first to substract,then divide then multiply and and etc. in this case.
A little explanation: I have the buttons with atribbutes and i added event listener when they are clicked they need to be showed on the screen.
After that i pushed them in array and then depen on the arithetic operation,,/,+,- i make search with reg ex and after that i remove them and i make the operation ab, a + b etc depending on that what includes the array when the = EQUAL BTN IS PRESSED. Can include *,/,+,- but in this case just one of that arithmetic operator.
const btns = document.querySelectorAll(".btn");
const screen = document.querySelector(".screen");
const equalBtn = document.querySelector(".btn-equal")
const clearBtn = document.querySelector(".btn-clear")
let get = []
for (let i = 0; i < btns.length; i++) {
btns[i].addEventListener("click", function() {
let number = btns[i].getAttribute("data-num");
screen.value += number;
get.push(number);
console.log(get);
})
}
equalBtn.addEventListener("click", function() {
if (get.includes("*")) {
const res = get.toString()
.match(/[^*]+/g)
.map(v => v.split(',')
.filter(v => v)
.map(v => +v))
var Multiply = res.map(function(el) {
return el.join('');
}).reduce(function(a, b) {
return a * b;
});
screen.value = Multiply;
}
if (get.includes("/")) {
const res = get.toString()
.match(/[^/]+/g)
.map(v => v.split(',')
.filter(v => v)
.map(v => +v))
var Divide = res.map(function(el) {
return el.join('');
}).reduce(function(a, b) {
return a / b;
});
screen.value = Divide;
}
if (get.includes("+")) {
const res = get.toString()
.match(/[^+]+/g)
.map(v => v.split(',')
.filter(v => v)
.map(v => +v))
var Adding = res.map(function(el) {
return el.join('');
}).reduce(function(a, b) {
return parseInt(a) + parseInt(b);
});
screen.value = Adding;
}
if (get.includes("-")) {
const res = get.toString()
.match(/[^-]+/g)
.map(v => v.split(',')
.filter(v => v)
.map(v => +v))
var Substracting = res.map(function(el) {
return el.join('');
}).reduce(function(a, b) {
return a - b;
});
screen.value = Substracting;
}
});
clearBtn.addEventListener("click", function() {
get.length = 0;
screen.value = "";
});

Very big recursion javascript

I have this code, which is counting array elements sum with recursion, but when the array is too big it throwing Maximum call stack size exceeded error.
var a = new Array(10 ** 7);
a.fill(0);
function sum(arr, i = 0) {
if(i === arr.length) {
return 0;
}
return arr[i] + sum(arr, i + 1);
}
sum(a);
So I need to change it somehow to work normally for all cases and I thought I can make it work asynchronously with Promises, but it always returning Promise pending.
var a = new Array(10 ** 7);
a.fill(0);
var sum = (arr, i = 0) => new Promise((resolve) => {
setTimeout(() => {
if(i === arr.length) {
return resolve(0);
}
return sum(arr, i + 1).then(el => el + arr[i]);
}, 0);
});
sum(a);
How can I fix it?
Any help would be appreciated!
You are only resolving the case where i is arr.length, so all the chained promises remain pending forever. Return won´t automatically resolve it for us, so need to be explicit:
var a = new Array(10);
a.fill(0);
a[0] = 3;
var sum = (arr, i = 0) => new Promise((resolve) => {
setTimeout(() => {
if(i === arr.length) {
resolve(0);
} else {
resolve(sum(arr, i + 1).then(el => el + arr[i]));
}
}, 0);
});
sum(a).then(console.log)
There are some solutions for your issue with using native tasks. this one is using Promise to schedule a microtask:
(function() {
function sum(arr, i = 0) {
if(arr.length === i) {
return Promise.resolve(0);
}
return Promise.resolve(null)
.then(() => sum(arr, i + 1))
.then((x) => x + arr[i]);
}
sum(a).then(s => console.log(s));
}());
but this will force the engine to wait until the execution is completed. So for huge arrays, I wouldn't recommend you doing this on the main thread.
You can also do the following:
(function() {
function sum(arr, i = 0) {
if(arr.length === i) {
return Promise.resolve(0);
}
return new Promise(resolve => {
setTimeout(() => {
sum(arr, i + 1).then(x => resolve(x + arr[i]));
});
});
}
sum(a).then(s => console.log(s));
}());
then with make a few changes to this code and making it more elegant, we can do the following:
(function() {
const defer = () => new Promise((resolve) => setTimeout(resolve));
async function sum(arr, i = 0) {
if(arr.length === i) {
return 0
}
await defer();
return await sum(arr, i + 1) + arr[i];
}
sum(a).then(s => console.log(s));
}());
and if your environment supports tail recursion you can change this to use it: http://2ality.com/2015/06/tail-call-optimization.html
UPDATE
actually, there is one more way to do this. rxjs library provides a queue scheduler which can help you make a recursive logic to be iterative without making many changes in your code. I've created an example of your sum method here.
I have no idea why you would want to use promises and make the function async. But if you do you need to resolve both cases:
const sum = (arr, i = 0) => new Promise((resolve) => {
setTimeout(() => {
if(i === arr.length) {
return resolve(0);
}
return sum(arr, i + 1).then(el => resolve(el + arr[i]));
}, 0);
});
Now this also returns a promise. Once you've gone async you can never go back. You need to use the returned promise to get the return value:
sum([1, 2, 3, 4]).then(return => console.log(return));
Its better to not make it async. ES6 supports tail recursion so you can just make it like this:
function sum(arr) {
function helper(acc, i) {
if (i<0) {
return acc;
}
return helper(arr[i]+acc, i-1);
}
return helper(0, arr.length - 1);
}
Since the order doesn't matter I took the liberty to sum the values in reverse. Now believe it or not, but that helper already exists in the language as an abstraction that gives you the value of each element and the acc in each iteration. Thus you can do this instead:
function sum(arr) {
return arr.reduce((acc, val) => acc + val, 0)
}
setTimeout is not a solution for the stack overflow problem. Managing your stack is a matter of sequencing your function calls. You can do this in a variety of ways, the most intuitive being the loop/recur trampoline.
const loop = f =>
{ let acc = f ()
while (acc && acc.tag === recur)
acc = f (...acc.values)
return acc
}
const recur = (...values) =>
({ tag: recur, values })
const sum = (xs = []) =>
loop ((result = 0, i = 0) => // initial state
i >= xs.length // exit condition
? result // return value
: recur (result + xs[i], i + 1)) // next state
const tenMillionNumbers =
Array.from (Array (1e7), (_,n) => n)
console.time ('recursion is not slow')
console.log (sum (tenMillionNumbers))
console.timeEnd ('recursion is not slow')
// => 49999995000000
// recursion is not slow: 2171ms
I cover many other techniques for this problem here.
Stack-safe recursion is something I write about a lot, with almost 30 answers on the topic
var a = new Array(1000);
a.fill(1);
function sum(arr, i = 0, res = 0, resolve) {
if(!i) return new Promise((resolve) => sum(arr, i + 1, res + arr[i], resolve), 0);
if(i === arr.length) return resolve(res);
setTimeout(function(){
sum(arr, i + 1, res + arr[i], resolve);
}, 0);
}
sum(a).then(console.log);
You need to use an iterative process instead of recursive one. So instead of accumulating calls, you'd compute your sum on each iteration call. This can be achieved by using a helper function which will get (sum, array, currentValue)
If for whatever reason you need to use recursion and don't mind this taking a long, you could use a timeout; however I would absolutely not recommend using this. I'm mostly posting it because it's possible.
var arr = new Array(10 ** 7);
arr.fill(0);
var len = arr.length,
idx = 0,
sum = 0,
sumFn = () => {
setTimeout(() => {
sum += arr[idx];
idx++;
if (idx < len) {
sumFn();
} else {
//complete
}
}, 1);
}
sumFn();

javascript least amount of elements from an integer array that can be used to get to a total value

please can somebody help?
If i have a total or a sum for instance 91
How can I create an array of the least amount of elements needed to get to the total value?
[50, 20, 10 , 5, 3, 2, 1] totaling this array will provide 91.
I know how to perform the opposite function using reduce or like so:
<script>
var numbers = [65, 44, 12, 4];
function getSum(total, num) {
return total + num;
}
function myFunction(item) {
document.getElementById("demo").innerHTML = numbers.reduce(getSum);
}
</script>
Greedy algorithm
Here is a solution using greedy algorithm. Note that this solution will work correctly in case when all the smaller numbers are divisors of all the bigger numbers such as in case [50, 10, 5, 1]. (see dynamic algorithm below this one for solution that can handle any input)
50 mod 10 = 0
50 mod 5 = 0
50 mod 1 = 0
10 mod 5 = 0
10 mod 1 = 0
5 mod 1 = 0
const sum = xs => xs.reduce((acc, v) => acc + v, 0);
function pickSubset(options, total, currentPick) {
if (sum(currentPick) === total) { return currentPick; }
if (options.length === 0) { return null; }
const firstVal = options[0];
let res = null;
if (sum(currentPick) + firstVal > total) {
res = pickSubset(options.slice(1), total, currentPick);
} else {
let opt1 = pickSubset(options, total, currentPick.concat(options[0]));
let opt2 = pickSubset(options.slice(1), total, currentPick.concat(options[0]));
if (opt1 && opt2) {
opt1.length < opt2.length ? res = opt1 : res = opt2
} else if (opt1) {
res = opt1;
} else {
res = opt2;
}
}
return res;
}
const total = 73;
const options = [50, 25, 10, 5, 2, 1];
console.log(pickSubset(options, total, []));
To handle unsorted input you can wrap it in another function and sort it prior to passing it to the main function.
const sum = xs => xs.reduce((acc, v) => acc + v, 0);
function pickSubset(options, total, currentPick) {
const sortedOptions = options.sort((a, b) => b - a);
function _pickSubset(options, total, currentPick) {
if (sum(currentPick) === total) { return currentPick; }
if (options.length === 0) { return null; }
const firstVal = options[0];
let res = null;
if (sum(currentPick) + firstVal > total) {
res = pickSubset(options.slice(1), total, currentPick);
} else {
let opt1 = pickSubset(options, total, currentPick.concat(options[0]));
let opt2 = pickSubset(options.slice(1), total, currentPick.concat(options[0]));
if (opt1 && opt2) {
opt1.length < opt2.length ? res = opt1 : res = opt2
} else if (opt1) {
res = opt1;
} else {
res = opt2;
}
}
return res;
}
return _pickSubset(sortedOptions, total, currentPick);
}
const total = 73;
const options = [50, 25, 10, 5, 2, 1].reverse();
console.log(pickSubset(options, total, []));
Dynamic programming (bottom-up natural ordering approach)
This solution works correctly for any type of input.
function pickSubset(options, total) {
function _pickSubset(options, change, minNums, numsUsed) {
for (let i = 0; i < change + 1; i++) {
let count = i;
let newNum = 1;
let arr = options.filter(v => v <= i);
for (let j of arr) {
if (minNums[i - j] + 1 < count) {
count = minNums[i - j] + 1;
newNum = j;
}
}
minNums[i] = count;
numsUsed[i] = newNum;
}
return minNums[change];
}
function printNums(numsUsed, change) {
const res = [];
let num = change;
while (num > 0) {
let thisNum = numsUsed[num];
res.push(thisNum);
num = num - thisNum;
}
return res;
}
const numsUsed = [];
const numsCount = [];
_pickSubset(options, total, numsCount, numsUsed);
return printNums(numsUsed, total);
}
const options = [50, 10, 5, 2, 1];
console.log(pickSubset(options, 73));
Dynamic programming (top-down memoization approach)
// helper function that generates all the possible solutions
// meaning, all the possible ways in which we can pay the provided amount
// and caches those solutions;
// returns the number of possible solutions but that is not neccessary
// in this case
const _pickSubset = (toPay, options, currentPick, cache) => {
if (toPay < 0) { return 0; }
if (toPay === 0) {
cache.add(currentPick);
return 1;
}
if (options.length === 0) { return 0; }
return _pickSubset(toPay - options[0], options, currentPick.concat(options[0]), cache)
+ _pickSubset(toPay, options.slice(1), currentPick, cache);
};
// memoize only with respect to the first two arguments - toPay, bills
// the other two are not necessary in this case
const memoizeFirstTwoArgs = fn => {
const cache = new Map();
return (...args) => {
const key = JSON.stringify(args.slice(0, 2));
if (cache.has(key)) { return cache.get(key); }
const res = fn(...args);
cache.set(key, res);
return res;
};
};
// uses memoized version of makeChange and provides cache to that function;
// after cache has been populated, by executing memoized version of makeChange,
// find the option with smallest length and return it
const pickSubset = (toPay, options) => {
const cache = new Set();
const memoizedPickSubset = memoizeFirstTwoArgs(_pickSubset);
memoizedPickSubset(toPay, options, [], cache);
let minLength = Infinity;
let resValues;
for (const value of cache) {
if (value.length < minLength) {
minLength = value.length;
resValues = value;
}
}
return resValues;
}
const options = [50, 25, 10, 5, 2, 1];
const toPay = 73;
console.log(pickSubset(toPay, options));

Categories