Javascript 跟踪递归函数被调用的次数

Javascript 跟踪递归函数被调用的次数,javascript,recursion,Javascript,Recursion,函数单位数(num){ 设计数器=0 让number=[…num+'').map(number).reduce((x,y)=>{return x*y}) 如果(number您应该在函数定义中添加一个反参数: 函数单位数(num,计数器=0){ log(`called${counter}times`) //... 返回单位数(数字,计数器+1) } 一位数(39) 您可以为此使用闭包 只需将计数器存储到函数的闭包中即可 下面是一个例子: 函数singleDigitDecorator(){ 设计数

函数单位数(num){
设计数器=0
让number=[…num+'').map(number).reduce((x,y)=>{return x*y})

如果(number您应该在函数定义中添加一个反参数:

函数单位数(num,计数器=0){
log(`called${counter}times`)
//...
返回单位数(数字,计数器+1)
}
一位数(39)

您可以为此使用闭包

只需将
计数器
存储到函数的闭包中即可

下面是一个例子:

函数singleDigitDecorator(){
设计数器=0;
返回函数singleDigitWork(num,isCalledRecursive){
//如果使用新参数调用,则重置
如果(!粗略调用){
计数器=0;
}
计数器+++;//*
log(`called${counter}times`);
让number=[…(num+“”).映射(number).减少((x,y)=>{
返回x*y;
});

if(number传统的解决方案是将计数作为参数传递给另一个答案所建议的函数

然而,js中还有另一个解决方案。其他一些答案建议只在递归函数之外声明count:

let counter = 0
function singleDigit(num) {
  counter++;
  // ..
}
这当然有效。但是这会使函数不可重入(不能正确调用两次)。在某些情况下,您可以忽略此问题,只需确保不调用两次
singleDigit
(javascript是单线程的,因此不太难实现)但是,如果您稍后将
个位数
更新为异步,这将是一个等待发生的错误,而且感觉很难看

解决方案是在外部而不是全局声明
计数器
变量。这是可能的,因为javascript有闭包:

function singleDigit(num) {
  let counter = 0; // outside but in a closure

  // use an inner function as the real recursive function:
  function recursion (num) {
    counter ++
    let number = [...num + ''].map(Number).reduce((x, y) => {return x * y})

    if(number <= 9){
      return counter            // return final count (terminate)
    }else{
      return recursion(number)  // recurse!
    }
  }

  return recursion(num); // start recursion
}
函数单位数(num){
let counter=0;//外部但在闭包中
//使用内部函数作为实际递归函数:
函数递归(num){
柜台++
让number=[…num+'').map(number).reduce((x,y)=>{return x*y})

如果(number另一种方法是使用生成器,因为您生成了所有的数字

最后一个元素是数字
n
减少为一个位数,要计算迭代次数,只需读取数组的长度即可

const digits=[…到单个数字(39)];
控制台日志(数字);
//=>[27,14,4]

函数*到单个数字(n){
做{
n=[…字符串(n)].reduce((x,y)=>x*y);
产氮量;
}而(n>9);
}

如果你只是想计算它减少了多少次,而不是特别关心递归…你可以删除递归。下面的代码仍然忠实于原始帖子,因为它不计算
num,这里有很多有趣的答案。我想我的版本提供了一个额外的有趣的答案另类

您可以对所需的函数执行几项操作。您可以递归地将其减少到一位数。您可以记录中间值,并需要进行递归调用的计数。处理所有这些操作的一种方法是编写一个纯函数,该函数将返回一个数据结构,其中包含最终结果、所采取的步骤和调用计数ll in one:

{
数字:4,
步骤:[39、27、14、4],
电话:3
}
然后,如果需要,您可以记录这些步骤,或者存储它们以供进一步处理

这里有一个版本可以做到这一点:

const singledigital=(n,steps=[])=>
n a*b),[…步骤,n])

log(单位数(39))
这几乎是一个纯学术的变体,但您可以使用

让我们稍微缩短和改进您的原始功能:

function singleDigit(n) {
    let digitProduct = [...(n + '')].reduce((x, y) => x * y, 1);
    return digitProduct <= 9 ? digitProduct : singleDigit(digitProduct);
}

// singleDigit(123234234) == 0
其中我们有
Ynormal(singleDigitF,123234234)==0

现在是诀窍。既然我们已经分解了Y组合子的递归,我们可以计算其中的递归数:

function Ycount(f, ...args) {
    let count = 1;
    let Y = (g) => g(() => {count += 1; return Y(g);});
    return [Y(f)(...args), count];
}
节点REPL中的快速检查提供:

> Ycount(singleDigitF, 123234234)
[ 0, 3 ]
> let digitProduct = (n) => [...(n + '')].reduce((x, y) => x * y, 1)
undefined
> digitProduct(123234234)
3456
> digitProduct(3456)
360
> digitProduct(360)
0
> Ycount(singleDigitF, 39)
[ 4, 3 ]
这个组合器现在可以计算以
singleDigitF
风格编写的任何递归函数中的调用数


(请注意,将零作为一个非常常见的答案有两个来源:数字溢出(
12334545699999999
变成
12334545700000000
等),以及当输入的大小增加时,几乎肯定会在某个地方将零作为中间值。)

这是一个Python版本,它使用包装函数简化计数器,正如slebetman的回答所建议的那样-我写这篇文章只是因为在这个实现中核心思想非常清楚:

from functools import reduce

def single_digit(n: int) -> tuple:
    """Take an integer >= 0 and return a tuple of the single-digit product reduction
    and the number of reductions performed."""

    def _single_digit(n, i):
        if n <= 9:
            return n, i
        else:
            digits = (int(d) for d in str(n))
            product = reduce(lambda x, y: x * y, digits)
            return _single_digit(product, i + 1)

    return _single_digit(n, 0)

>>> single_digit(39)
(4, 3)
从functools导入reduce
def单位数字(n:int)->元组:
“”“获取一个大于等于0的整数,并返回一个单位数乘积的元组。”
以及执行的减少次数
def单位数字(n,i):
如果n>>单个数字(39)
(4, 3)
为什么不在函数中调用

编辑:要在浏览器中尝试的代码段:

函数单位数(num){
控制台。计数(“单位数”);
设计数器=0
让number=[…num+'').map(number).reduce((x,y)=>{return x*y})

如果(数字太棒了。看起来我的计数器不工作了,因为我在function@chs242作用域规则规定,在函数中声明它将在每次调用时创建一个新的。@chs242不是在函数中声明它。从技术上讲,所有默认参数也都在这样做——在您的情况下,它只是该值从未结转到下一次递归调用函数时。a.e.每次函数运行时
计数器
都会被废弃并设置为
0
,除非您像Sheraff那样在递归调用中明确地结转它。a.e.
单位数(数字,++计数器)
right@zfrisch我现在明白了。感谢您花时间解释。请将
++counter
更改为
counter+1
。它们在功能上是平等的
function Ynormal(f, ...args) {
    let Y = (g) => g(() => Y(g));
    return Y(f)(...args);
}
function Ycount(f, ...args) {
    let count = 1;
    let Y = (g) => g(() => {count += 1; return Y(g);});
    return [Y(f)(...args), count];
}
> Ycount(singleDigitF, 123234234)
[ 0, 3 ]
> let digitProduct = (n) => [...(n + '')].reduce((x, y) => x * y, 1)
undefined
> digitProduct(123234234)
3456
> digitProduct(3456)
360
> digitProduct(360)
0
> Ycount(singleDigitF, 39)
[ 4, 3 ]
from functools import reduce

def single_digit(n: int) -> tuple:
    """Take an integer >= 0 and return a tuple of the single-digit product reduction
    and the number of reductions performed."""

    def _single_digit(n, i):
        if n <= 9:
            return n, i
        else:
            digits = (int(d) for d in str(n))
            product = reduce(lambda x, y: x * y, digits)
            return _single_digit(product, i + 1)

    return _single_digit(n, 0)

>>> single_digit(39)
(4, 3)