To understand what recursion is, you must first understand recursion This is the second assignment for my CS2 course:


  
  Write a recursive C++ function writeLine() that writes a character    repeatedly to form a line of n characters. For example:

writeLine('*', 5)

  
  produces the output:
  
  
*****

  
  Write a recursive [C++] function writeBlock() that uses writeLine    to write m lines of n characters each. For example,

writeBlock('*', 5, 3)

  
  produces the output:
  
  
*****
*****
*****

  
  Implement and test a recursive C++ function writePyramid(). The    function should take in a character to build a pyramid with and the
  height of the pyramid (the number of lines in it). Hint: Each line in 
  the pyramid should be generated by two calls to writeLine: the first
  call generates the spaces and a second call to generate the line of
  characters.
     The ␣ in the output below represents a space character. This output    is generated when the function is asked to generate a
  pyramid of    height 3 with the * character.
  
  
␣␣*
␣***
*****

  
  


Please feel free to rip the code apart!  In my opinion, I think the switch in main() could be DRYed up a bit, but I wasn't sure how.



lab2.cpp:

#include &lt;iostream&gt;
#include &lt;cstdio&gt;
#include "lab2functions.h"

char getSanitizedChar()
{
  // absorb newline character (if existant) from previous input
  if('\n' == std::cin.peek()) std::cin.ignore();
  return std::tolower(std::cin.get());
}

int main()
{
  char type = '*';
  char shape = 'l';
  int rows = 0;
  int cols = 0;
  do
  {
    std::cout &lt;&lt; "Enter shape you would like {(L)ine, (b)lock, (t)riangle}: ";
    std::cin &gt;&gt; shape;

    switch(shape)
    {
    case 'l':
      std::cout &lt;&lt; "Enter number of columns: ";
      std::cin &gt;&gt; cols;
      std::cout &lt;&lt; "Enter character to build with (*): ";
      type = getSanitizedChar();
      writeLine(('\n' == type ? '*' : type), cols);
      std::endl(std::cout);
      break;
    case 'b':
      std::cout &lt;&lt; "Enter number of rows: ";
      std::cin &gt;&gt; rows;
      std::cout &lt;&lt; "Enter number of columns: ";
      std::cin &gt;&gt; cols;
      std::cout &lt;&lt; "Enter character to build with (*): ";
      type = getSanitizedChar();
      writeBlock(('\n' == type ? '*' : type), rows, cols);
      break;
    case 't':
      std::cout &lt;&lt; "Enter number of rows: ";
      std::cin &gt;&gt; rows;
      std::cout &lt;&lt; "Enter character to build with (*): ";
      type = getSanitizedChar();
      writePyramid(('\n' == type ? '*' : type), rows);
      break;
    default:
      std::cout &lt;&lt; "Invalid option";
      break;
    }
    std::cout &lt;&lt; "\nRun a calculation again (y/N): ";  // signify n as default with capital letter
  } while ('y' == getSanitizedChar());
}


lab2functions.h:

#ifndef LAB2FUNCTIONS_H
#define LAB2FUNCTIONS_H

#include &lt;cstdint&gt;

void writeLine(const char, const int8_t);
void writeBlock(const char, const int8_t, const int8_t);
void writePyramid(const char, const int8_t);

#endif


lab2functions.cpp:

#include &lt;iostream&gt;
#include "lab2functions.h" 

void writeLine(const char c, const int8_t num)
{
  if (num &gt; 0)
  {
    std::cout &lt;&lt; c;
    writeLine(c, num - 1);
  }
}

void writeBlock(const char c, const int8_t m, const int8_t n)
{
  if (m &gt; 0)
  {
    writeLine(c, n);
    std::endl(std::cout);
    writeBlock(c, m - 1, n);
  }
}

void writePyramid(const char c, const int8_t h)
{
  static int count;
  count++;
  if (h &gt; 0)
  {
    writeLine(' ', (2 * h - 1) / 2);
    writeLine(c, (2 * count - 1));
    std::endl(std::cout);
    writePyramid(c, h - 1);
  }
  count = 0;
}
Usually we write recursive functions with the exit condition all by itself:

void writeLine(const char c, const int8_t num)
{
  if (num &gt; 0)
  {
    std::cout &lt;&lt; c;
    writeLine(c, num - 1);
  }
}


I would normally write this as:

void writeLine(const char c, const int8_t num)
{
  if (num &lt;= 0)
  {
      return;
  }

  std::cout &lt;&lt; c;
  writeLine(c, num - 1);
}


No real reason apart from tradition I suppose. But it does show tail recursion more clearly. The compiler can easily optimize tail recursion into a loop for you. But it also makes it obvious to the human eye where tail recursion is.

A static member of a recursive function!!!

void writePyramid(const char c, const int8_t h)
{
  static int count;
  count++;


That's a bit strange. You normally pass all values as parameters between iterations. If you need a defaulting starting value then you can either use a wrapper function that calls your recursive function and provides the initial value. Or have a parameter that defaults to the start value but internally you increment it (I choose the second here).

void writePyramid(const char c, const int8_t h, int count = 0)
{
  if (h &lt;= 0)
  {
    return;
  }

  writeLine(' ', (2 * h - 1) / 2);
  writeLine(c, (2 * count - 1));
  std::endl(std::cout);
  writePyramid(c, h - 1, count + 1); 
}


I know that Static storage duration objects are zero initialized. But how many of your peers know this. It would have been nice to give them a hint.

static int count = 0;


Going back to your wrapper code:

All the switch statements contain the same bit of code:

  std::cout &lt;&lt; "Enter number of rows: ";
  std::cin &gt;&gt; rows;
  std::cout &lt;&lt; "Enter number of columns: ";
  std::cin &gt;&gt; cols;
  std::cout &lt;&lt; "Enter character to build with (*): ";
  type = getSanitizedChar();


Keep it DRY. Either move this into a separate function (called from each case). Or move it to before the switch intirally.