简体   繁体   English

这个递归 function 如何在 C++ 中工作?

[英]How does this recursive function work in C++?

I'm currently taking a class for c++ and we are learning about recursion and in class my professor used this function as an example of recursion, the function is meant to return the smallest digit in a number and is: I'm currently taking a class for c++ and we are learning about recursion and in class my professor used this function as an example of recursion, the function is meant to return the smallest digit in a number and is:

int smallD(int n) {
    if (n < 10) return n;
    int x = smallD(n / 10);
    if (x < n % 10) return x;
    else return n % 10;
}

I'm confused on how setting x to the recursive call works, would the function not keep running n / 10 until n is < 10, I just really don't understand the concept and could use some pointers as to how this function works.我对将 x 设置为递归调用的工作原理感到困惑,function 会不会一直运行 n / 10 直到 n < 10,我真的不明白这个概念,并且可以使用一些指针来说明这个 function 是如何工作的。

Here's something that helps to understand recursion.这里有一些有助于理解递归的东西。 Add print statements to observe the code as it recursively calls itself and pass and "indent level" to help as well.添加打印语句以观察代码,因为它递归地调用自身并传递和“缩进级别”以提供帮助。

Take the original minified code and expand it to something more readable and add extra debugging information to it.获取原始的缩小代码并将其扩展为更具可读性的内容,并为其添加额外的调试信息。

int smallD(int n, const std::string& indent) {

    cout << indent << "enter: smallD(n=" << n << ")" << endl;

    if (n < 10)  {
        cout << indent << "n < 10 => returning: " << n << endl;
        return n;
    }

    cout << indent << "about to recurse inovking smallD(" << n / 10 << ")" << endl;
    int x = smallD(n / 10, indent+"  "); // grow the indent by 2 spaces
    cout << indent << "return from recursion, result is: " << x << endl;

    cout << indent << "x=" << x << "  n=" << n << " n%10=" << n % 10 << endl;

    if (x < n % 10) {
        cout << indent << "x is less than n%10, returning: " << x << endl;
        return x;
    }

    cout << indent << "x is greater than or equal n%10, returning: " << n%10 << endl;
    return n % 10;
}

Let's try it out by invoking smallD(8942468, "")让我们通过调用smallD(8942468, "")来尝试一下

enter: smallD(n=8942468)
about to recurse inovking smallD(894246)
  enter: smallD(n=894246)
  about to recurse inovking smallD(89424)
    enter: smallD(n=89424)
    about to recurse inovking smallD(8942)
      enter: smallD(n=8942)
      about to recurse inovking smallD(894)
        enter: smallD(n=894)
        about to recurse inovking smallD(89)
          enter: smallD(n=89)
          about to recurse inovking smallD(8)
            enter: smallD(n=8)
            n < 10 => returning: 8
          return from recursion, result is: 8
          x=8  n=89 n%10=9
          x is less than n%10, returning: 8
        return from recursion, result is: 8
        x=8  n=894 n%10=4
        x is greater than or equal n%10, returning: 4
      return from recursion, result is: 4
      x=4  n=8942 n%10=2
      x is greater than or equal n%10, returning: 2
    return from recursion, result is: 2
    x=2  n=89424 n%10=4
    x is less than n%10, returning: 2
  return from recursion, result is: 2
  x=2  n=894246 n%10=6
  x is less than n%10, returning: 2
return from recursion, result is: 2
x=2  n=8942468 n%10=8
x is less than n%10, returning: 2    // <== this is the final result

So hopefully, that will help you understand how the recursion works.因此,希望这将帮助您了解递归的工作原理。

Recursive functions work exactly like non-recursive functions.递归函数的工作方式与非递归函数完全相同。
One common mistake is to try to think about all the recursive calls at once, as if they had a shared state, but one key ingredient to understanding a recursive function is actually to ignore the recursion and just "think locally".一个常见的错误是尝试一次考虑所有递归调用,就好像它们有一个共享的 state,但理解递归 function 的一个关键因素实际上是忽略递归而只是“本地思考”。

Perhaps working through an example would clarify things.也许通过一个例子可以澄清事情。
Let's look at smallD(321) , replacing n in the body of the function with its value.让我们看一下smallD(321) ,用它的值替换 function 主体中的n

smallD(321)

    if (321 < 10) return 321;
    int x = smallD(321 / 10);
    if (x < 321 % 10) return x;
    else return 321 % 10;

The first condition is clearly false, and in order to determine x , we need smallD(321/10) , which is smallD(32) .第一个条件显然是错误的,为了确定x ,我们需要smallD(321/10) ,即smallD(32)

smallD(32)

    if (32 < 10) return 32;
    int x = smallD(32 / 10);
    if (x < 32 % 10) return x;
    else return 32 % 10;

The first condition is false again, so we keep going with smallD(32/10) .第一个条件再次为假,所以我们继续使用smallD(32/10)

smallD(3)

    if (3 < 10) return 3;
    int x = smallD(3 / 10);
    if (x < 3 % 10) return x;
    else return 3 % 10;

Now the first condition is true, so the result here is clearly 3 .现在第一个条件为真,所以这里的结果显然是3
Now we can go back and use the value of x in each call that was left waiting.现在我们可以返回 go 并在每个等待的调用中使用x的值。

smallD(32)

    ...
    if (3 < 32 % 10) return 3;
    else return 32 % 10;

And 3 < 32 % 10 is false, so we return 32 % 10 - 2 to the caller.并且3 < 32 % 10是假的,所以我们将32 % 10 - 2返回给调用者。

smallD(321)

    ...
    if (2 < 321 % 10) return 2;
    else return 321 % 10;

And 2 < 321 % 10 is false, so we return 321 % 10 , which is 1 .并且2 < 321 % 10是假的,所以我们返回321 % 10 ,即1

Your intuition is not completely off: The function does indeed "keep running n/10 until n is <10" - but it's in different calls to the same function.你的直觉并没有完全消失:function 确实“继续运行 n/10 直到 n <10”——但它在对同一个 function 的不同调用中。

Your program keeps a stack of functions calls.您的程序保留了一堆函数调用。 Each function you call puts a (so-called) "frame" on top of everything that is currently on the stack.您调用的每个 function 都会在当前堆栈中的所有内容之上放置一个(所谓的)“框架”。 Inside that frame "live" all the variables "belonging" to that function call.在该框架内“活动”所有“属于”该 function 调用的变量。 When the function exits, it deletes its own frame from the stack.当 function 退出时,它会从堆栈中删除自己的帧。 The currently executing function's frame is on the top of the stack.当前正在执行的函数的框架位于栈顶。 So, let's see what happens if you call smallD(123) :所以,让我们看看如果你调用smallD(123)会发生什么:

  • You start with something else on the stack, at least your main() .你从堆栈上的其他东西开始,至少是你的main() Your call to smallD(123) puts the frame for smallD(123) on the top of the stack.您对 smallD(123) 的调用将smallD(123) smallD(123)框架放在堆栈的顶部。 This frame contains the variable n = 123 .该帧包含变量n = 123 Lets call this frame stack frame A .让我们将此帧堆栈称为帧A

  • Since n >= 10 , your smallD(123) calls smallD(123 / 10) , ie, smallD(12) (integer division basically truncates in C++).由于n >= 10 ,您的smallD(123)调用smallD(123 / 10) ,即smallD(12) (整数除法基本上在 C++ 中截断)。 So, you put another frame on top of your stack.因此,您将另一个框架放在堆栈顶部。 This stack frame corresponds to smallD(12) and contains the variable n = 12 .此堆栈帧对应于smallD(12)并包含变量n = 12 Let's call this stack frame B .我们称这个堆栈帧B

  • Again, n >=10 , so the call to smallD(12 / 10) (ie, smallD(1) ) happens.同样, n >=10 ,所以调用smallD(12 / 10) (即smallD(1) )发生。 A stack frame (call it C ) is created for this new call, with n = 1 .为这个新调用创建了一个堆栈帧(称为C ),其中n = 1

  • Now n < 10 holds!现在n < 10成立! The last function call (in stack frame C ) returns the value 1 and deletes its own stack frame (frame C ).最后的 function 调用(在堆栈帧C中)返回值1并删除其自己的堆栈帧(帧C )。

  • Stack frame B (where n = 12 ) is now on top and execution of smallD(12) continues.堆栈帧B (其中n = 12 )现在位于顶部,并且smallD(12)继续执行。 Since stack frame C has returned 1 , stack frame B now contains x = 1 .由于堆栈帧C已返回1 ,因此堆栈帧B现在包含x = 1 The comparison (x < n % 10) is true ( 1 < 2 ), and stack frame B return x = 1 .比较(x < n % 10)为真 ( 1 < 2 ),堆栈帧B返回x = 1

  • The same happens in stack frame A again, and stack frame A (which corresponds to our original smallD(123) call) returns 1 .同样的情况再次发生在堆栈帧A中,堆栈帧A (对应于我们最初的smallD(123)调用)返回1

So you see, indeed "division until n < 10" happened, but in different (recursive!) calls to smallD .所以你看,确实发生了“直到 n < 10 的除法”,但是在不同的(递归!)调用smallD

You can break down almost every recursive call into the base case (the simplest) and the recursive case .您可以将几乎每个递归调用分解为基本案例(最简单的)和递归案例 In this case:在这种情况下:

  • The base case is when the number has only one digit : this means that is lesser than 10 (which is the first number with two digits).基本情况是数字只有一个数字:这意味着小于 10(这是第一个有两位数字的数字)。
     if (n < 10) return n;
    There is no other digit to compare it to, so return it directly.没有其他数字可以比较,所以直接返回。
  • The recursive case is any other case that is not the base.递归情况是不是基础的任何其他情况。 Now our digit has two or more digits, so:现在我们的数字有两个或多个数字,所以:
    • And learn what is the smallest digit of all the digits except the last one with the recursive call x = smallD(n / 10)并通过递归调用x = smallD(n / 10)了解除最后一位之外的所有数字中的最小数字是什么
    • Then we compare the result to the last digit, which we didn't include before, and return the smallest.然后我们将结果与我们之前未包含的最后一位数字进行比较,并返回最小值。

To understand better the process behind the calls, you could print some information and observe it.为了更好地理解调用背后的过程,您可以打印一些信息并观察它。 Try something like this:尝试这样的事情:

int smallD(int n) {
    if (n < 10){
      std::cout << "Base case. Return " << n << std::endl;
      return n;
    }
    std::cout << "Recursive case: " << n << std::endl;
    std::cout << "Compare " << n % 10 << " with smallest of " << n/10 << std::endl;
    int x = smallD(n / 10);
    int ret;
    if (x < n % 10) ret = x;
    else ret = n % 10;
    std::cout << "Smallest of " << n << " is " << ret << std::endl;
    return ret;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM