Search code examples
javascriptrecursionfunctional-programmingsicp

Improve debugging output of a recursive function


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?


Solution

  • 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
    

    It's not beautiful, but it's not too bad.