I have the following recursive function to count the number of ways change can be returned given various coin denominations:
function makeChange(amount, coins) {
// Note: using Floats here will not work
console.log(`Amount: ${amount}, Coins: ${coins}`);
return (amount === 0) ? 1 :
(amount < 0) ? 0 :
(!coins.length) ? 0 :
makeChange(amount-coins[0], coins)
makeChange(amount, coins.slice(1));
}
console.log(
makeChange(11, [7,3,1])
);
However, I'd like to improve the visual-aspect of the debugging to really see what might be going on behind the scenes of the recursive function -- almost like each level of the stack and how to show that. I've improved it to pass a level
parameter so I can do indentation and I then have:
function makeChange(amount, coins, level=0) {
console.log(`${' '.repeat(level)}Amount: ${amount}, Coins: ${coins}`);
return (amount === 0) ? 1 :
(amount < 0) ? 0 :
(!coins.length) ? 0 :
makeChange(amount-coins[0], coins, level 1)
makeChange(amount, coins.slice(1), level 1);
}
console.log(
makeChange(10, [5,1])
);
But even this is a bit difficult to understand as it has so much superfluous input. What might be a better way to add in various debug helpers to better visualize this?
CodePudding user response:
One possibility is just to also track the returns and use a bit more formatting to group together the events at a single level.
You can run this here, but you will need to open the browser console to see the results, as there are too many lines for StackOverflow's one:
function makeChange(amount, coins, level=0) {
console.log(`${'| '.repeat(level)}makeChange(${amount}, [${coins.join (', ')}])`);
const result = (amount === 0) ? 1 :
(amount < 0) ? 0 :
(!coins.length) ? 0 :
makeChange(amount-coins[0], coins, level 1)
makeChange(amount, coins.slice(1), level 1);
console.log(`${'| '.repeat(Math .max(level - 1, 0))}${level > 0 ? '| ' : ''} --> ${result}`); return result
}
console.log(
makeChange(11, [7, 3, 1])
);
.as-console-wrapper {max-height: 100% !important; top: 0}
It will give you output like this:
makeChange(11, [7, 3, 1])
| makeChange(4, [7, 3, 1])
| | makeChange(-3, [7, 3, 1])
| | --> 0
| | makeChange(4, [3, 1])
| | | makeChange(1, [3, 1])
| | | | makeChange(-2, [3, 1])
| | | | --> 0
| | | | makeChange(1, [1])
| | | | | makeChange(0, [1])
| | | | | --> 1
| | | | | makeChange(1, [])
| | | | | --> 0
| | | | --> 1
| | | --> 1
| | | makeChange(4, [1])
| | | | makeChange(3, [1])
| | | | | makeChange(2, [1])
| | | | | | makeChange(1, [1])
| | | | | | | makeChange(0, [1])
| | | | | | | --> 1
| | | | | | | makeChange(1, [])
| | | | | | | --> 0
| | | | | | --> 1
| | | | | | makeChange(2, [])
| | | | | | --> 0
| | | | | --> 1
| | | | | makeChange(3, [])
| | | | | --> 0
| | | | --> 1
| | | | makeChange(4, [])
| | | | --> 0
| | | --> 1
| | --> 2
| --> 2
| makeChange(11, [3, 1])
| | makeChange(8, [3, 1])
| | | makeChange(5, [3, 1])
| | | | makeChange(2, [3, 1])
| | | | | makeChange(-1, [3, 1])
| | | | | --> 0
| | | | | makeChange(2, [1])
| | | | | | makeChange(1, [1])
| | | | | | | makeChange(0, [1])
| | | | | | | --> 1
| | | | | | | makeChange(1, [])
| | | | | | | --> 0
| | | | | | --> 1
| | | | | | makeChange(2, [])
| | | | | | --> 0
| | | | | --> 1
| | | | --> 1
| | | | makeChange(5, [1])
| | | | | makeChange(4, [1])
| | | | | | makeChange(3, [1])
| | | | | | | makeChange(2, [1])
| | | | | | | | makeChange(1, [1])
| | | | | | | | | makeChange(0, [1])
| | | | | | | | | --> 1
| | | | | | | | | makeChange(1, [])
| | | | | | | | | --> 0
| | | | | | | | --> 1
| | | | | | | | makeChange(2, [])
| | | | | | | | --> 0
| | | | | | | --> 1
| | | | | | | makeChange(3, [])
| | | | | | | --> 0
| | | | | | --> 1
| | | | | | makeChange(4, [])
| | | | | | --> 0
| | | | | --> 1
| | | | | makeChange(5, [])
| | | | | --> 0
| | | | --> 1
| | | --> 2
| | | makeChange(8, [1])
| | | | makeChange(7, [1])
| | | | | makeChange(6, [1])
| | | | | | makeChange(5, [1])
| | | | | | | makeChange(4, [1])
| | | | | | | | makeChange(3, [1])
| | | | | | | | | makeChange(2, [1])
| | | | | | | | | | makeChange(1, [1])
| | | | | | | | | | | makeChange(0, [1])
| | | | | | | | | | | --> 1
| | | | | | | | | | | makeChange(1, [])
| | | | | | | | | | | --> 0
| | | | | | | | | | --> 1
| | | | | | | | | | makeChange(2, [])
| | | | | | | | | | --> 0
| | | | | | | | | --> 1
| | | | | | | | | makeChange(3, [])
| | | | | | | | | --> 0
| | | | | | | | --> 1
| | | | | | | | makeChange(4, [])
| | | | | | | | --> 0
| | | | | | | --> 1
| | | | | | | makeChange(5, [])
| | | | | | | --> 0
| | | | | | --> 1
| | | | | | makeChange(6, [])
| | | | | | --> 0
| | | | | --> 1
| | | | | makeChange(7, [])
| | | | | --> 0
| | | | --> 1
| | | | makeChange(8, [])
| | | | --> 0
| | | --> 1
| | --> 3
| | makeChange(11, [1])
| | | makeChange(10, [1])
| | | | makeChange(9, [1])
| | | | | makeChange(8, [1])
| | | | | | makeChange(7, [1])
| | | | | | | makeChange(6, [1])
| | | | | | | | makeChange(5, [1])
| | | | | | | | | makeChange(4, [1])
| | | | | | | | | | makeChange(3, [1])
| | | | | | | | | | | makeChange(2, [1])
| | | | | | | | | | | | makeChange(1, [1])
| | | | | | | | | | | | | makeChange(0, [1])
| | | | | | | | | | | | | --> 1
| | | | | | | | | | | | | makeChange(1, [])
| | | | | | | | | | | | | --> 0
| | | | | | | | | | | | --> 1
| | | | | | | | | | | | makeChange(2, [])
| | | | | | | | | | | | --> 0
| | | | | | | | | | | --> 1
| | | | | | | | | | | makeChange(3, [])
| | | | | | | | | | | --> 0
| | | | | | | | | | --> 1
| | | | | | | | | | makeChange(4, [])
| | | | | | | | | | --> 0
| | | | | | | | | --> 1
| | | | | | | | | makeChange(5, [])
| | | | | | | | | --> 0
| | | | | | | | --> 1
| | | | | | | | makeChange(6, [])
| | | | | | | | --> 0
| | | | | | | --> 1
| | | | | | | makeChange(7, [])
| | | | | | | --> 0
| | | | | | --> 1
| | | | | | makeChange(8, [])
| | | | | | --> 0
| | | | | --> 1
| | | | | makeChange(9, [])
| | | | | --> 0
| | | | --> 1
| | | | makeChange(10, [])
| | | | --> 0
| | | --> 1
| | | makeChange(11, [])
| | | --> 0
| | --> 1
| --> 4
--> 6
6
undefined
It's not beautiful, but it's not too bad.
CodePudding user response:
It seems like the following would be useful to potentially print depending on the verbosity level of the script:
- The stack level. Of this can be done using tabs or something similar rather than just "Level=1".
- Whether the base case is reached or not. For example, the above function might only reach the base case three times out of 1000 recursive function calls.
- The variables that the function is applied with.
With the above, we can improve the function to something like the following:
function makeChange(amount, coins, verbose) {
console.log(`Changing ${amount}...`);
return (function _makeChange(amount, coins, verbose, debugLevel=0, debugCoinsUsed=[]) {
if (verbose || amount===0)
console.log(`${' '.repeat(debugLevel)}Amount: ${amount}, Coins: ${coins}${ amount===0? ' ==> [' debugCoinsUsed ']' :''}`);
return (amount === 0) ? 1 :
(amount < 0) ? 0 :
(!coins.length) ? 0 :
_makeChange(amount-coins[0], coins, verbose, debugLevel 1, [...debugCoinsUsed, coins[0]])
_makeChange(amount, coins.slice(1), verbose, debugLevel 1, [...debugCoinsUsed]);
})(amount, coins, verbose);
}
console.log(
makeChange(10, [5,1]),
makeChange(10, [5,1], true)
);
And the non-verbose call would log:
makeChange(10, [5,1], false)
Changing 10...
Amount: 0, AvailableCoins: 5,1 ==> [5,5]
Amount: 0, AvailableCoins: 1 ==> [5,1,1,1,1,1]
Amount: 0, AvailableCoins: 1 ==> [1,1,1,1,1,1,1,1,1,1]
3