0

I was trying to make a program that finds us the sum of the cubes of the digits of a number using the concept of recursion.

But something weird happens when I try to add the numbers and update the variable sum. When I write

sum = sum + pow(sumCube(num), 3);

the values aren't added and stored in sum. Instead, the values in sum are overwritten as if the value of sum were reset to 0 every time.

On the other hand, when I write

sum += pow(sumCube(num), 3);

the program seems to run perfectly. Here is the program for your reference.

#include <iostream>
#include<math.h>
using namespace std;

int num = 51, dgt = 0, sum = 0;

int sumCube(int)
{
int dgt = num % 10;
while (num > 10)
{
    num = num / 10;
    //sum = sum + pow(sumCube(num), 3);    //doesn't work properly
    sum += pow(sumCube(num), 3);         //works properly
}
cout << sum << endl;
return(dgt);
}
int main()
{
    num = num * 10;
    sumCube(num);
    return 0;
}

I think the syntax is what's causing the problem. But I've also viewed a few posts that say both the syntaxes behave the same way. I never had such a problem before, both the versions seemed to work the same way.

Any help is appreciated.

user8718165
  • 127
  • 7
  • 10
    The *real* problem is the unnecessary use of global `sum` coupled with evaluation order. Recursed `sumCube` modifies `sum` before returning to the caller. Consider how that affects the *caller's* value of sum when procuring each of those evaluations. That you're not even using your unnamed `int` argument is solid code smell. – WhozCraig Jun 26 '22 at 22:21
  • 1
    So, your **reproducible** example contains code with a comment that says it works correctly, and has code that's commented out, with a comment that it doesn't work? Post the code that doesn't work. Don't expect readers to rewrite it for you just to see what the problem is. – Pete Becker Jun 26 '22 at 22:24
  • 1
    Also, this is supposed to be a recursive solution. [There should be no while-loop](https://godbolt.org/z/hYWh1roWf). There should only an if-test that either triggers the base case and stops the recursion, or triggers a recursive descent. – WhozCraig Jun 26 '22 at 22:29
  • 1
    No, you don't need to use `+=`. But the alternative that you propose would certainly produce a different result. `int p = pow(sumCube(num), 3); sum = sum + p;` would produce the same result. – Drew Dormann Jun 26 '22 at 22:46
  • 2
    You need to learn the difference between parameters, return values and (when unavoidable) global variables, and when to use them. – Paul Sanders Jun 26 '22 at 22:47
  • 2
    I don't understand why you want to use recursion to solve the problem. Try to explain the intended algorithm, step by step, in full English sentences. Draw a flowchart, even. You should find that it simply doesn't make any sense. If you are set on using recursion, then *first* try to think: how does recursion help to break down the problem? What value can be returned from the recursive call, that *represents part of the calculation* and is useful for computing the entire result? – Karl Knechtel Jun 26 '22 at 23:01
  • 1
    *using the concept of recursion.* -- This does not require any recursive solution, in fact recursion may obfuscate the goal of the program and thus would make understanding recursive routines much more difficult (because simply, it makes little sense to use recursion). It seems that too many teachers or schools want to introduce recursion into programs that have no benefit from recursion -- I have seen questions posted where the poster wants to add an array of numbers using recursion, as crazy as that sounds. A tree search or similar makes a *lot* more sense for recursive solutions. – PaulMcKenzie Jun 26 '22 at 23:09
  • 1
    @PaulMcKenzie the pedagogic theory is that using recursion for trivial tasks is supposed to make it easier to understand, by allowing focus on the act of recursion itself and minimizing the effort needed to decompose the problem recursively. However, everyone *still* gets stuck - partly because basically nobody teaches *what functions are and how they work* properly, so the necessary mental model to understand recursion isn't there - because the mental model of a call stack, or a stack frame, isn't there. – Karl Knechtel Jun 26 '22 at 23:26
  • Thank you all for your valuable suggestions. Really helpful. I'll try to improve my skills. – user8718165 Jun 27 '22 at 03:47

2 Answers2

4

The overall approach to the recursion is completely wrong.

  1. Mutating global state is a bad idea even in the best conditions. It's especially important to avoid that when using recursion, because it makes it much harder to reason about the code correctly.

  2. Functions should normally signal the result of the calculation by returning it. This is especially important for recursive functions. That's what return is for.

  3. The point of using recursion here (admittedly a trivial example) is that it already decomposes and simplifies the problem at each step: we use the recursion to find the sum of all the other cubes, and then add the cube for the current digit. Therefore, the correct recursive code should not have a while loop.

  4. Please make sure you have a solid understanding of how calling a function works. Writing int sumCube(int) { means that the function accepts a parameter, but can't use it because there is no name for it. We should write a name: int sumCube(int num) {. Then, only inside the function, num is a name for whatever was passed in as an argument. This does not have to be another variable named num; it does not have to be a variable at all. We do not pass variables to functions. We pass values to functions.

You know, exactly like how when you write pow(sumCube(num), 3), the 3 didn't have to be a variable at all, and didn't have to have a name dictated by the pow function. Exactly like how when you use the pow function, it returns a value that you can use on the right-hand side of +, and doesn't change any global variable.


With that background in mind, we can approach the problem fresh. Think about it this way: imagine that you had someone else's function, sumCube2, which solves the same problem in some other way. We want to use it to help solve the problem. What is our general approach?

  1. We want to give sumCube2 a simpler problem than the one that we got (or else we could have just used it directly). We have a decomposition step already: we use division and modulo to extract the last digit.

  2. We want to combine the result from calling sumCube2, with some local calculation. It's easy to see how to do this: we cube the last digit, and add the result from the function call to that cubed value. Then we can return that result.

  3. We need to guard against the problem of running out of room for simplification. This is simple: if we're given 0, then we know the result is 0, and we don't have to do any more work. (There are other ways to implement this step, but why make it any harder?)

So: We need a function that accepts a parameter named num:

int sumCube(int num) {
    int last, rest;

We will first check our special case:

    if (!num) return 0;

Otherwise, we extract the last digit, and separate the rest of the number:

    last = num % 10;
    rest = num / 10;

We compute and return the result, by making use of the sumCube2 result:

    return sumCube2(rest) + pow(last, 3);
}

Notice that we don't need to modify any global variables, use a while loop, or anything like that.

We can condense the code a little bit, since we don't really need names for those intermediate calculation results (that was just to be able to talk about the logic one step at a time):

int sumCube(int num) {
    if (!num) return 0;
    return sumCube2(num / 10) + pow(num % 10, 3);
}

(Or we could use a ?: conditional expression, or we could use multiplication explicitly instead of calling pow, you get the idea.)

Now, how do we make this use recursion, and not have to depend on the mythical sumCube2 that we didn't write?

Simple: sumCube is a function that is intended to do exactly what sumCube2 was going to do. So we just call it instead - i.e., make the recursive call there.

int sumCube(int num) {
    if (!num) return 0;
    return sumCube(num / 10) + pow(num % 10, 3);
}

It works because every call to the function gets its own parameters and its own local variables, and returns back to where it was called from - exactly like calling any other function. Because we only expect the recursive call to do part of the work, and because we only recurse when there is a valid "part of" the work to do recursively, we avoid infinite recursion.

Notice that I do not have any cout logic in this code. That is because the purpose of sumCube is to compute the sum of the cubes. Displaying that result is a completely separate and unrelated task. It is the responsibility of main:

int main()
{
    cout << sumCube(51) << endl;
    return 0;
}
Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
3

The basic problem is that in this expression:

sum + pow(sumCube(num), 3)

the value of sum might be read either before or after calling sumCube -- there's no sequence point here and no dependency between them. Since sumCube also modifies sum (it's a global variable), you get indeterminate behavior.

When you use the += operator, there's a dependency between its left operand and its right operand (as there is with any assignment), so the call to sumCube will come before the addition which reads the value of sum, so you'll always get the value of sum after it has been modified by sumCube

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226