【问题标题】:Javascript - why my function is recursive?Javascript - 为什么我的函数是递归的?
【发布时间】:2020-08-23 04:46:18
【问题描述】:

我不知道我哪里错了。为什么我的函数是递归的?我期望 sum = 3;

function init() {
  let object = {
    firsNum: 1,
    sum: 0,
  };
  add(1, object.firsNum, fun);
  console.log(object.sum);
}

function fun(a) {
  a.firstNum++;
  a.sum += a.firsNum;
  fun(a);
}

function add(a, b, callback) {
  return callback(a + b);
}
init();

【问题讨论】:

  • 什么意思为什么是递归的?它是递归的因为它调用自己
  • 你在fun中调用fun
  • 让你在fun内部调用函数fun有意义
  • a.firstNamea.sum 也将是未定义的,您传递的三个参数不是对象
  • 乐趣(一);正在递归。

标签: javascript recursion callback


【解决方案1】:

您的代码

function init() {
  let object = {
    firsNum: 1,
    sum: 0,
  };
  add(1, object.firsNum, fun);
  console.log(object.sum);
}

function fun(a) {
  a.firstNum++;
  a.sum += a.firsNum;
  fun(a);
}

function add(a, b, callback) {
  return callback(a + b);
}
init();

初始化

定义一个属性为firsNumsum的对象,然后调用add,传递1、1和fun

有趣

接收一个参数。增加参数的firstNum 并将其添加到sum然后它会调用自己

添加

获取三个参数:abcallback。调用callback 并将a + b 传递给它。

流程

init 被调用,然后它调用 add 并将 1 传递给 a,将 1 传递给 bfun 传递给 callback。结果,添加调用callback,即fun,由于ab 都是1,a + b = 2 被传递给funfun 增加了a.firstNum,这似乎是一个错误,因为a 是一个数字,所以在sum 中添加了一些其他奇怪的值。只需在 fun 中删除 fun(a); 即可摆脱递归,但这不是代码中唯一的错误。另外,请确保fun 中有return 语句。

【讨论】:

    【解决方案2】:

    我认为您并没有完全理解您在代码中使用的每个编程概念。

    除此之外,您的原始代码包含一个错字:a.firstNum++;,它应该像在所有其他地方一样显示为 firsNum

    让我们将您的代码分解为不同的关注点。

    递归

    Recursion 表示函数调用自身。递归是一种计算、迭代或遍历数据的方法。另一种是使用whilefor 之类的循环进行迭代。

    让我们看看这个简化的例子:

    function recurse(counter) {
       console.log(counter);
       recurse(counter + 1);
    }
    recurse(0);
    

    这将无限期地调用recurse(),因为没有什么可以阻止它调用自己。递归和迭代都需要一些停止条件来中断递归。

    如果您想在某个值处停止,您只需要在满足该条件之前调用该函数:

    function recurse(counter) {
       console.log(counter);
       if (counter < 42) {
          recurse(counter + 1);
       }
    }
    recurse(0);
    

    这只会递归和递增,直到达到 42。

    您甚至可以使用函数本身传递停止条件:

    function recurse(counter, maxValue) {
       console.log(counter);
       if (counter < maxValue) {
          recurse(counter + 1, maxValue); // maxValue gets passed along
       }
    }
    recurse(0, 42);
    

    要使您的递归函数在某个值处停止,您必须添加这样一个条件:

    function fun(a) {
      a.firstNum++;
      a.sum += a.firstNum;
      if (a.sum < a.maxNum) {
        fun(a);
      }
    }
    

    然后您必须确保您的对象指定条件:

    let object = {
      maxNum: 3, // stop condition added
      firsNum: 1,
      sum: 0,
    };
    

    虽然这本身并不能解决问题。继续阅读。

    回调

    回调是作为函数参数、对象属性或数组元素传递给其他函数的函数。

    回调允许您决定在运行时调用什么函数,而无需使用 ifswitch 语句实现分支行为,并且在编写时需要某些函数名称。

    function someCallback() { /* ... */ }
    
    function callTheCallback(callback) {
       callback(); // Execute the parameter as a function
    }
    
    callTheCallback(someCallback);
    
    let someCallbackReference = someCallback;
    
    // Call the same function but indirectly via a variable
    callTheCallback(someCallbackReference);
    

    在上面的示例中,someCallback 可以间接源自其他对象。

    您甚至可以直接将函数表达式指定为回调:

    callTheCallback(function() {
       // ...
    });
    

    对于回调,掌握传递函数调用结果和函数本身之间的区别至关重要:

    callTheCallback(someCallback); // Callback function passed
    callTheCallback(someCallback()); // Result of a function call passed
    

    请注意,如果返回值本身是一个函数,则传递函数调用的结果可能是完全有效的。这在编程中也很常见。

    回调通常用于inversion of control (IoC),特别是asynchronous programming

    回调函数示例:

    在您的情况下,fun() 作为第三个参数回调传递给add()

    既然您知道如何使用递归和回调,您的代码中仍然存在语义错误...

    语义错误

    与句法错误(错误的代码语法)相反,语义错误是与代码含义有关的错误。

    add()的接口

    分析你的函数API:

    function add(a, b, callback) {
      return /* some value - callback() call removed for simplicity */;
    }
    

    add() 接受三个参数返回一个值。第三个参数名为“回调”,因此应该是一个函数。

    您对add() 的调用看起来很合理,尽管您没有使用返回值(这不一定是错误)。

    add(1, object.firsNum, fun);
    

    add()的实现

    由于add() 的参数没有记录,所以你的意图是模棱两可的。

    名称以及有两个参数ab 的事实导致人们假设应该添加这两个值。你做得很好:

    return callback(a + b);
    

    但是!您作为回调函数传递的内容 - fun() - 需要不同的参数,而不是数字。 fun() 需要一个对象。

    正确记录它看起来像这样:

    /**
     * Recursively sum values until a maximum value is reached.
     * 
     * @param {Object} a object containing the sum, the increment and the max value
     * @param {Number} a.firsNum start value
     * @param {Number} a.maxNum the maxium value up to which to add "firsNum" to "sum"
     * @param {Number} a.sum the summed value, shall be 0 initially
     * @returns {undefined} nothing is returned
     */
    function fun(a) {
      // ...
    }
    

    上面的评论风格叫做JsDoc:https://jsdoc.app

    注意为函数、变量和参数使用正确的名称。正确的名称有助于记录代码,而无需编写明确的文档 cmets。

    修复错误

    由于对您真正想要达到的目标了解有限,因此只能推测。我假设add(),虽然它的名称和参数看起来很合理,但实现错误。您的问题可以通过正确调用回调 (fun()) 来解决:

    function add(obj, callback) {
      return callback(obj);
    }
    

    还有:

    add(object, fun);
    

    由于我们需要对象add(),现在期望它作为单个参数。

    现在这已经没有多大意义了。 fun() 可以直接从 init() 内部调用,而不是通过回调引用,add() 的名称令人困惑。

    我不会进一步推测您的意图是什么以及如何实施该问题的替代解决方案。

    你学到了什么

    • 关于Mozilla developer network
    • 递归的工作原理
    • 函数可以作为参数传递并分配给变量或属性
    • 读码
    • 记录代码和正确命名函数和变量的好处

    【讨论】:

    • 非常感谢您抽出宝贵的时间来写所有这些
    【解决方案3】:

    刚刚注释掉了递归代码

    function init() {
                let object = {
                    firsNum: 1,
                    sum: 0,
                };
                add(1, object.firsNum, fun);
                console.log(object.sum);
            }
    
            function fun(a) {
                a.firstNum++;
                a.sum += a.firsNum;
                //fun(a);
            }
    
            function add(a, b, callback) {
                return callback(a + b);
            }
            init();
    

    【讨论】:

    • 我有输出:0。我想传递 add() 方法三个参数,最后我想要 sum=3。
    猜你喜欢
    • 1970-01-01
    • 2021-11-13
    • 1970-01-01
    相关资源
    最近更新 更多