改进递归函数的调试输出
Improve debugging output of a recursive function
我有以下递归函数来计算在给定各种硬币面额的情况下可以返回零钱的方式的数量:
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])
);
但是,我想改进调试的可视化方面,以真正了解递归函数幕后可能发生的情况——几乎就像堆栈的每一层以及如何显示它一样。我改进了它以传递 level
参数,这样我就可以进行缩进,然后我有:
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])
);
但即使这样也有点难以理解,因为它有太多多余的输入。添加各种调试助手以更好地可视化它可能是更好的方法吗?
根据脚本的冗长级别,以下内容似乎对潜在打印有用:
- 堆栈级别。这可以使用选项卡或类似的东西来完成,而不仅仅是“Level=1”。
- 是否达到基本情况。例如,上述函数在 1000 次递归函数调用中可能只达到基本情况 3 次。
- 应用函数的变量。
通过以上,我们可以将功能改进为如下形式:
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)
);
并且 non-verbose 调用将记录:
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
一种可能性是也跟踪 returns 并使用更多格式将事件组合在一个级别。
您可以在此处 运行,但您需要打开浏览器控制台才能查看结果,因为 Whosebug 的行数太多:
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}
它会给你这样的输出:
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
不算漂亮,但也不算太差。
我有以下递归函数来计算在给定各种硬币面额的情况下可以返回零钱的方式的数量:
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])
);
但是,我想改进调试的可视化方面,以真正了解递归函数幕后可能发生的情况——几乎就像堆栈的每一层以及如何显示它一样。我改进了它以传递 level
参数,这样我就可以进行缩进,然后我有:
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])
);
但即使这样也有点难以理解,因为它有太多多余的输入。添加各种调试助手以更好地可视化它可能是更好的方法吗?
根据脚本的冗长级别,以下内容似乎对潜在打印有用:
- 堆栈级别。这可以使用选项卡或类似的东西来完成,而不仅仅是“Level=1”。
- 是否达到基本情况。例如,上述函数在 1000 次递归函数调用中可能只达到基本情况 3 次。
- 应用函数的变量。
通过以上,我们可以将功能改进为如下形式:
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)
);
并且 non-verbose 调用将记录:
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
一种可能性是也跟踪 returns 并使用更多格式将事件组合在一个级别。
您可以在此处 运行,但您需要打开浏览器控制台才能查看结果,因为 Whosebug 的行数太多:
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}
它会给你这样的输出:
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
不算漂亮,但也不算太差。