Sunday, 4 March 2018

Chapter 7 // Exercise 1, 2, 3 - Principles & Practice Using C++

In this exercise I am using Visual Studio Community 2017 and the header file "std_lib_facilities.h" which can be found here:

http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h


Chapter 7 // Exercise 1


1. Allow underscores in the calculator's variable names.


This suffers from the same problem as the drill 10 in chapter 7 in that the default checks to see if it is a letter, if it isn't and the character doesn't match any of the other cases then it will simply return "bad token".

However there is a simple way round this by just adding a check for '_' onto the if statements as a logical 'or':


   default:

   //do this if ch is a letter or underscore

   if (isalpha(ch) || ch == '_') 

   {

    string s;

    s += ch;


     //while there are still chars in cin, read them into s

    while (cin.get(ch) && (isalpha(ch) || isdigit(ch) || ch == '_')) 

     s += ch;

    cin.unget();


     //if string is equal to other commands defined below, return them

    if (s == declKey) 

     return Token(let);

    if (s == quitKey) 

     return Token(quit);

    if (s == sqrtKey)

     return Token(squareR);

    if (s == powKey)

     return Token(findPow);


     return Token(name, s);

   }


    //if the char does not fit any of these parameters return an error message

   error("Bad token");

Chapter 7 // Exercise 2

2. Provide an assignment operator, =, so that you can change the value of a variable after you introduce it using let. Discuss why that can be useful and how it can be a source of problems.

There are many different and ways to implement this. The simplest is to comment out the if statement that checks to see if there is already a variable with the same name. Then you can redefine the same variable as many times as you like. However, I also decided to implement a way to inform the user that the variable was already in use and ask them if they would like to reassign it. This involved flushing out the stream first, then firing off a few if statements. There is probably a quicker way to do this but I seem to just like doing things by brute force.

 
 if (is_declared(name))
 {
  cout << name + ", declared twice. Would you like to reassign? y/n > ";
  cin.clear();
  cin.ignore(10000, '\n'); //clear the buffer
  string ans;
  getline(cin, ans);
  if (ans == "n")
   error(name, ", will not be reassigned; ");
  if (ans == "y")
  {
   cout << "Please enter new value: ";
   int val;
   cin >> val;
   set_value(name, val);
   double d = val; //return value to print to reset calculator
   return d;
  }
 }


If the user does select "y" to reassign, they are prompted to enter their new value. set_value() is then called to set the new value instead of pushing back a new variable into the vector. The value is then returned to 'reset' the calculator of sorts.

It's a good idea because someone may have made a mistake when entering the value for a new variable, however it is mainly a bad idea as it can cause more harm than good; especially if a program saves values. I would stick to only allowing a variable to be assigned upon initialisation as it's easy to just create a new variable with a new value.


Chapter 7 // Exercise 3

3. Provide named constants that you really can't change the value of. Hint: You have to add a member to Variable that distinguishes between constants and variables and check for it in set_value(). If you want to let users define constants, you'll have to add a notation to let the user express that , for example, const pi = 3.14

First I added a new const char and const string to the top of the program:
 
const char isConst = 'C';
const string constKey = "const";

Then a new return had to be added for constkey in token::get():
 
if(s == constKey)
return Token(isConst);

The struct Variable was then modified to accept a bool value to check if the variable has been define as Const or not:
 
struct Variable 
{
 string name;
 double value;
 bool isConst;
 Variable(string n, double v, bool ic) :name(n), value(v), isConst(ic) { }
};

Set_Value() was then changed to check if the variable being set was not a const.
 
//set the Variable named s to d
void set_value(string s, double d)
{
 for (int i = 0; i <= names.size(); ++i)
 {
  //allow redefinitions as long as variable isn't const
  if (names[i].name == s && names[i].isConst == false) 
  {
   names[i].value = d;
   return;
  }
 }

 error("set: undefined name ", s);
}
is_declared() and define_name() were also modified to make sure that the already-defined variable trying to be redefined is not a const variable:
 
//is variable already declared?
bool is_declared(string s)
{
 for (int i = 0; i < names.size(); ++i)
 {
  if (names[i].name == s && names[i].isConst == true)
   error("Cannot reassign const variable");
  else if (names[i].name == s && names[i].isConst == false)
   return true;
 }

 return false;
}

//allow programmers to add (var,val) to variable vector
double define_name(string var, double val, bool isConst)
{
 if (is_declared(var))
  error(var, " declared twice");

 names.push_back(Variable(var, val, isConst));

 return val;
}

Last but not least was the modification to declaration, which gets the next word in the stream. It checks for 'Const' being next, if its not then the Variable being defined is not const.
 
//check for name definintion errors
double declaration()
{
 Token t = ts.get();

 //is const the next word in the stream?
 bool isC;
 if (t.kind == 'C')
 {
  isC = true;
  t = ts.get();  //get the the next word in the stream for the name
 }
 else
  isC = false;

 if (t.kind != 'a') 
  error("name expected in declaration;");

 string name = t.name;
 if (is_declared(name))
 {
  cout << name + ", declared twice. Would you like to reassign? (No need to print with ';') y/n > ";
  cin.clear();
  cin.ignore(10000, '\n'); //clear the buffer
  string ans;
  getline(cin, ans);
  if (ans == "n")
   error(name, ", will not be reassigned; ");
  if (ans == "y")
  {
   cout << "(No need to print with ';') Please enter new value: ";
   int val;
   cin >> val;
   set_value(name, val);
   double d = val; //return value to print to reset calculator
   return d;
} } Token t2 = ts.get(); if (t2.kind != '=') error("= missing in declaration of ", name); double d = expression(); names.push_back(Variable(name, d, isC)); return d; }

No comments:

Post a Comment