【问题标题】:Stacks and recursive functions堆栈和递归函数
【发布时间】:2023-03-27 03:21:01
【问题描述】:

我希望这不是一个重复的问题——尽管我在这方面找不到太多帮助。

我正在练习递归函数(我是新手),我正在尝试将数组中的每个数字相乘。有点像阶乘。我有这段代码,但它只返回undefined 结果。

代码如下:

 var stack = [];

function countDown(int) {
  stack.push(int);
  if (int === 1) {  
    return 1;
  }
    return countDown(int - 1);
}

function multiplyEach() {
  // Remove the last value of the stack 
  // and assign it to the variable int
  int = stack.pop();
  x = stack.length;
  // Base case
  if ( x === 0 ) {
    return;
  }
  // Recursive case
  else {
    stack[int - 1] = int * stack[x - 1];
    return multiplyEach(int);
  }
}

// Call the function countDown(7)
countDown(7);
// And then print out the value returned by multiplyEach()
console.log(multiplyEach());

非常感谢您提供任何见解。

干杯!

【问题讨论】:

  • 首先,不要使用全局状态为var stack。而且你也不应该使用自己的堆栈结构,除非你必须(见我的answer

标签: javascript arrays recursion


【解决方案1】:

首先要解决的是,从外观上看,您的 multiplyEach() 函数应该有一个名为 int 的参数。您使用的方法可能更适合不同的技术,但我们会解决的。

接下来,在 multiplyEach() 中,有两种可能的路径:

  1. 堆栈仍然有元素,在这种情况下,我们将堆栈上的新顶部值乘以旧值,然后继续执行 multiplyEach 的另一次运行。

  2. 堆栈为空,我们直接返回。

这里的问题是我们实际上并没有返回最终的堆栈值,或者将它留在那里以便以后访问。我们实际上已经丢失了它,那么函数输出的是什么?

在许多语言中,我们会为这个函数定义一个返回类型,void 表示没有值,或者int 表示返回相乘后的值。但是,在 Javascript 中,没有不返回值的函数。 JS 中的“无”表示为undefined。当您返回 multiplyEach() 时,您会将它的另一个调用推入调用堆栈,并等待实际的返回值...最终是 return;,JS 将其解释为 return undefined;。同样,在大多数语言中,都会有某种形式的错误,但在 JS 中不会!让我们看看两种可能的实现:

  1. 您使用的自定义堆栈:

    // your stack is fine, so we'll skip to the meat
    function multiplyEach() {
        var int = stack.pop(), x = stack.length;
        stack[x - 1] *= int;
        if(x < 2) // to multiply, we need at least two numbers left
            return;
        multiplyEach();
    }
    
    //...
    multiplyEach();
    console.log(stack[0]);
    
  2. 带参数,并使用列表:

    function multiplyEach(index) {
        var int = list[index];
        if(index == 0)
            return int;
        return int * multiplyEach(index - 1);
    }
    
    //...
    console.log(multiplyEach(list.length - 1));
    

它们都是递归实现的不同方式;所有递归本质上都需要一个函数调用自己。另一种可能性是让参数存储总数,而不是像选项 2 那样将返回值相乘;这称为尾递归。

编辑:注意到选项 1 的基本情况在错误的位置,所以我移动了它。它现在应该可以工作了。

【讨论】:

  • 这真的很有帮助!感谢您的宝贵时间。
【解决方案2】:

递归你可以这样做:

function multiplyEach(arr, size) {
  return size === 0 ? arr[size] : arr[size] * multiplyEach(arr, size - 1);
}

var array = [1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log(multiplyEach(array, array.length - 1));

但是您可以使用本机方法reduce 来做到这一点,那么您可以简单地这样做:

console.log([1, 2, 3, 4, 5, 6, 7, 8, 9].reduce(function(a, b) { return a * b; }));

ES6 版本:

console.log([1, 2, 3, 4, 5, 6, 7, 8, 9].reduce((a, b) =&gt; a * b));

希望对你有帮助。

【讨论】:

  • 这有帮助!谢谢。
【解决方案3】:

有时递归更适合解决问题,有时迭代更适合解决问题。

递归更适合执行一个动作,直到满足条件然后退出,类似于 while 循环。

迭代更适合执行一个动作N次然后退出,类似于for循环。

在我们的例子中,我们想要输入一个数组并输出该数组所有元素的乘积。换句话说,我们要执行 N - 1 次乘法,其中 N 是数组中元素的数量。由于我们知道要执行的操作数量,我们应该通过下面的迭代来解决我们的问题。

var arr = [1, 2, 3]
var result = multiplyEach(arr)
console.log(result)
// -> 6

function multiplyEach(arr) {
    var result = 1
    arr.map(function(value) {
        result *= value
    })
    return result
}     

请记住,以上只是一个近似的经验法则,因为有时递归会比迭代更优雅。这是真正的阶乘的简单、递归和优雅的实现。

function factorial(value) {
    return 1 === value ? 1 : value * factorial(value - 1)
}

阶乘的迭代实现在while循环形式和for循环形式中都不那么漂亮。

function factorial(value) {
    var result = 1
    while (value > 0) {
        result *= value
        value--
    }
    return result
}

function factorial(value) {
    var result = 1
    for (var i = 0; i < value; i++) {
        result *= value
    }
    return result
}

【讨论】:

  • 很好的概念比较。两点意见:1)map 在底层实现为循环或递归。这意味着map 是循环的抽象,而递归是它们的完全替代(实际上递归比循环更具表现力)2)你的阶乘是天真的,因为它不是尾递归,因此会不可避免地炸毁堆栈.
【解决方案4】:

我认为主要问题是您在 multiplyEach() 定义中没有参数,但您尝试在函数内部使用一个参数来调用它。

这样的工作:

var stack = [1, 2, 3, 4, 5, 6, 7];

function multiplyEach(arr) {
  if (arr.length == 0) {
    return 1;
  } else {
    return arr.pop() * multiplyEach(arr);
  }
}
console.log(multiplyEach(stack));

【讨论】:

  • 感谢您的洞察!
  • 这种实现可能会产生不想要的结果,因为在同一个数组上两次应用函数会产生不同的结果,因为 JS 对传递给函数的所有对象使用按引用传递
  • 谢谢,但它适用于 Chrome(当然,不适用于任意大小的数组……)。它不会两次调用具有相同数组的函数,因为我 pop() 每次调用函数时都有一个元素。对吗?
【解决方案5】:

递归函数 multiplyEach() 中的基本情况返回未定义。

这就是你的问题所在。

一个简单的解决方法是:

var stack = [];
var multipliedStack = [];

function countDown(int) {
  stack.push(int);
  if (int === 1) {  
    return 1;
  }
    return countDown(int - 1);
}

function multiplyEach() {
  // Remove the last value of the stack 
  // and assign it to the variable int
  int = stack.pop();
  x = stack.length;
  // Base case
  if ( x === 0 ) {
    return multipliedStack.reverse();
  }
  // Recursive case
  else {
    multipliedStack.push(int * stack[x - 1]);
    return multiplyEach(int);
  }
}

// Call the function countDown(7)
countDown(7);
// And then print out the value returned by multiplyEach()
console.log(multiplyEach());

我不完全明白你所说的“有点像阶乘”是什么意思?这个函数到底应该做什么?

要理解递归,您需要了解递归基本情况是什么,而且我认为您需要修改代码中未以正确方式使用的 push() 和 pop()。

如果您习惯于迭代,递归函数通常会有点混乱,继续尝试,您会很快适应它。

【讨论】:

    猜你喜欢
    • 2018-09-24
    • 2014-01-17
    • 2017-09-12
    • 1970-01-01
    • 2017-10-20
    • 2012-06-15
    • 2020-08-27
    • 2011-02-26
    相关资源
    最近更新 更多