Search code examples
c++recursiontrace

Tracing a recursive factorial function in C++?


Having trouble with a little exercise, suppose I have:

#include <iostream>
using namespace std;

int factorialFinder(int x) {
    if (x==1) {
        return 1;
    }else{
        return x*factorialFinder(x-1);
    }
}

int main()
{
    cout << factorialFinder(5) << endl;
}

How would you trace this? For example:

Put in 5 into the factorialFinder function.
if (5 == 1) (False, so skip)
returns 5 times factorialFinder(5-1)
this means returns 5 times factorialFinder(4)
this means go back to function
if (4 == 1) (False, so skip)
returns 4 times factorialFinder(4-1)
...etc.

Now if you follow my logic, my question is in my last statement

returns 4 times factorialFinder(4-1)

Does it return 4 or does it return 20 because it multiplies the 5*4 first?


Solution

  • One way to generate a trace is to instrument the code, adding trace output statements. It can be a good idea to create some support for that. E.g.,

    #include <iostream>
    #include <string>
    using namespace std;
    
    auto operator*( int const n, string const& s )
        -> string
    {
        string result;
        for( int i = 1; i <= n; ++i ) { result += s; }
        return result;
    }
    
    class Tracer
    {
    private:
        std::string callspec_;
        std::string result_;
    
        auto call_level()
            -> int&
        {
            static int the_level;
            return the_level;
        }
    
        static auto indent() -> string { return ".  "; }
    
    public:
        template< class Value >
        auto result( Value v )
            -> Value
        {
            result_ = to_string( v );
            return v;
        }
    
        ~Tracer()
        {
            --call_level();
            clog << "<- " << call_level()*indent() << callspec_;
            if( not result_.empty() )
            {
                clog << " returns " << result_;
            }
            clog << endl;
        }
    
        Tracer( string funcname )
            : callspec_( move( funcname ) )
        {
            clog << "-> " << call_level()*indent() << callspec_ << endl;
            ++call_level();
        }
    };
    
    auto factorial( int const x )
        -> int
    {
        Tracer trace( "factorial " + to_string( x ) );
        return trace.result( x == 1? 1 : x*factorial( x - 1 ) );
    }
    
    auto main() -> int
    {
        cout << factorial( 5 ) << endl;
    }
    

    Result:

    -> factorial 5
    -> .  factorial 4
    -> .  .  factorial 3
    -> .  .  .  factorial 2
    -> .  .  .  .  factorial 1
    <- .  .  .  .  factorial 1 returns 1
    <- .  .  .  factorial 2 returns 2
    <- .  .  factorial 3 returns 6
    <- .  factorial 4 returns 24
    <- factorial 5 returns 120
    120
    

    However, just using a debugger to execute the code step by step can be just as enlightening with much less work.