当前位置: 首页 > 知识库问答 >
问题:

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

陶超
2023-03-14
 function singleDigit(num) {
      let counter = 0
      let number = [...num + ''].map(Number).reduce((x, y) => {return x * y})

      if(number <= 9){
          console.log(number)
      }else{
          console.log(number)
          return singleDigit(number), counter += 1
      }
   }
singleDigit(39)

上面的代码接受一个整数,并通过将其乘以自己的数字将其减少为一个数字。

例如39。

3 x 9 = 27.
2 x 7 = 14.
1 x 4 = 4.

控制台将记录:

27 
14 
4

如何跟踪递归函数被调用了3次?

我尝试添加计数器,但无法更新。非常感谢您的帮助

共有3个答案

尹光辉
2023-03-14

这几乎是一个纯粹的学术变体,但您可以为此使用修改后的定点组合器。

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

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

// singleDigit(123234234) == 0

从这个变量中,我们可以计算出递归调用并进行curry:

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

此函数现在可以与定点组合器一起使用;具体来说,我实现了一个适用于(严格)JavaScript的Y组合器,如下所示:

function Ynormal(f, ...args) {
    let Y = (g) => g(() => Y(g));
    return Y(f)(...args);
}

其中,我们有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风格编写的任何递归函数中的调用数。

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

谭昕
2023-03-14

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

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

let counter = 0
function singleDigit(num) {
  counter++;
  // ..
}

这当然有效。但是这使得函数不可重入(不能正确调用两次)。在某些情况下,您可以忽略这个问题,只需确保您不会调用singleDigit两次(javascript是单线程的,所以并不难做到),但如果您稍后更新singleDigit以异步,这是一个等待发生的错误,而且感觉也很难看。

解决方案是在外部而不是全局声明计数器变量。这是可能的,因为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
}

这与全局解决方案类似,但每次调用单位数(现在不是递归函数)时,都会创建计数器变量的新实例。

邬宏扬
2023-03-14

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

function singleDigit(num, counter = 0) {
    console.log(`called ${counter} times`)
    //...
    return singleDigit(number, counter+1)
}
singleDigit(39)
 类似资料:
  • Project Euler问题14给出以下问题: 为正整数集定义以下迭代序列: n→n/2(n为偶数) n→3n 1(n为奇数) 使用上述规则,从13开始,我们生成以下序列: 13→ 40→ 20→ 10→ 5.→ 16→ 8.→ 4.→ 2.→ 1. 可以看出,该序列(从13开始,到1结束)包含10个术语。虽然这还没有被证明(科拉兹问题),但人们认为所有的起始数字都以1结束。 100万以下的哪个

  • 简介 Logify能够接受一个.h头文件作为输入,然后输出.xm文件(MobileSubstrate扩展),这个.xm文件hook这个类的所有方法,当这些方法被调用的时候打印log。这有助于你发现哪些方法被调用了。Logify在安装了Theos之后就有。 用法 在命令行下输入类似的命令: /opt/theos/bin/logify.pl MomoLocationManager.h > tweak.

  • 问题内容: 我可以在变量中创建一个递归函数,如下所示: 这样,将输出 。假设我做了以下事情: 将输出 如上。如果我再更改如下: 然后将给出,如预期的那样。 现在给出 它所指的,而不是函数(它本身指向的)。在某些情况下这可能是理想的,但是有没有一种方法可以编写函数以便它调用自身而不是保存它的变量? 也就是说,是否可以 仅 更改线路,以便 在调用时仍能完成所有这些步骤?我试过了,但这给了我错误。 问题

  • Xdebug允许你记录所有的函数调用数据(包括参数和返回值)到不同格式的文件。 之所以称之为函数跟踪,是因为它可以帮助你在使用新的应用程序,或者当你试图找出应用程序运行时到底发生了什么。函数跟踪还可以选择显示传递给函数和方法的变量的值,并返回值。在默认跟踪中,这两个元素不可用。 输出格式 有三种输出格式。第一种是人类可读的调用栈,第二种更适合计算机程序,因为它更容易解析,最后一种使用HTML格式化

  • 问题内容: 我有一个异步函数,要连续多次调用。问题是“多个”可以是几十万或数百万… 显而易见的方法是从回调中调用相同的函数,如下所示: 当然,涉及一些逻辑来停止递归。问题是堆栈是否充满了调用,并可能在某些时候导致堆栈溢出? 问题答案: 问题是堆栈是否充满了调用,并可能在某些时候导致堆栈溢出? 否。 如果调用回调是异步传递的,则不会堆积堆栈。 在您的代码中: 这是逐步发生的事情: 首先被称为。 然后

  • 第二个构造函数应该调用第一个构造函数,但却给了我“递归构造函数调用”错误。 我明白这个错误的意思,只是不明白递归在哪里。第一个contructor将作为参数,而应该是该类型的数组。我错过了什么? 多谢了。