Programming: Week 2 (cont.)
|
Ex. 10 Write a program that generates a random symbolic game map that contains # represents a wall This one is simple. Since we need to randomly choose one of 3 options, we generate an integer from the interval [0;2] (or [1;3], it doesn’t change anything).
#include
#include
#include
int random()
{
return rand() % 2;
}
int main(int argc, char* argv[]){
std::cout << std::endl;
srand((unsigned int)time(0));
for (int i = 0; i < 9; i++)
{
std::cout << " ";
for (int j = 0; j < 5; j++)
{
if (random() == 0) std::cout << "#";
else if (random() == 1) std::cout << "&";
else std::cout << "_";
};
std::cout << std::endl;
};
std::cout << std::endl;
return 0;
}
Update (2014-11-28). Two errors are said to cancel out. I noticed that a new random number is generated in each if-statement branch. The way the if-statement is constructed makes the chance of printing a character decrease as you go deeper down the if-else hierarchy. And here’s why. I generate either a 0 or a 1. If it’s a 0, I print a #. Otherwise I generate another number and if it’s a 1, I print an &, otherwise an _. So whether & or _ will be printed, depends on whether or not a # will be printed. The chance of producing a # is 0.5, while the chance of producing a & and an _ is 0.25. As a result, the #’s would appear twice as often as the &’s or the _’s. Below is the fixed version:
#include
#include
#include
int random()
{
return rand() % 2;
}
int main(int argc, char* argv[]){
std::cout << std::endl;
srand((unsigned int)time(0));
for (int i = 0; i < 9; i++)
{
std::cout << " ";
for (int j = 0; j < 5; j++)
{
int control = random();
if (control == 0) std::cout << "#";
else if (control == 1) std::cout << "&";
else std::cout << "_";
};
std::cout << std::endl;
};
std::cout << std::endl;
return 0;
}
I introduce a new variable in line 19, But now I noticed that _ weren’t showing up at all. So now I figured out that the generator So here is the final version which also contains the corrected generator:
#include
#include
#include
int random()
{
return rand() % 3;
}
int main(int argc, char* argv[]){
std::cout << std::endl;
srand((unsigned int)time(0));
for (int i = 0; i < 9; i++)
{
std::cout << " ";
for (int j = 0; j < 5; j++)
{
int control = random();
if (control == 0) std::cout << "#";
else if (control == 1) std::cout << "&";
else std::cout << "_";
};
std::cout << std::endl;
};
std::cout << std::endl;
return 0;
}
This was a real example of two errors cancelling out. Ex. 11 Write a program that calculates factorial recursively. Here’s the program:
#include
#include
int multiply(int a)
{
if (a > 1) return (a * multiply(a-1));
else return 1;
}
int main(int argc, char* argv[]){
std::cout << "Please enter an integer n." <> n;
std::cout << n << "!= " << multiply(n) << std::endl;
return 0;
}
The most complicated piece of code for me is the line 6. There is no assignment statement. Assign occurs indirectly, via return. The second term in the product triggers the Ex. 12 Write a program that converts an English word to a word in the Robber language (a “language” invented by Astrid Lindgren where after every consonant in a word an extra syllable is added, consisting of the vowel “o” and that consonant. E.g., “hello” becomes “hohelollolo”).
#include
#include
#include
int main(int argc, char* argv[]){
std::cout << "Please enter a word to be translated to the Robber language." <> word;
for (int i = 0; i < word.length(); i++)
if (word.at(i) != 'a' && word.at(i) != 'e' && word.at(i) != 'i' && word.at(i) != 'o' && word.at(i) != 'u')
{
std::string string = "o";
string += word.at(i);
word.insert(i+1, string);
i += 2;
};
std::cout << "Translation: " << word << std::endl;
return 0;
}
Special thanks to Isak Ekedahl and Emil Elthammar for help, lines 13-16 is their wisdom. Pointers and Dynamic Memory ManagementIn Week 2, we were introduced to pointers and memory management. These topics were completely new to me. There are quite a few new concepts I have never seen, like dealing directly with memory addresses or allocating and de-allocating memory areas. Several following exercises are related to memory management. Ex. 13 Write a program that declares and initializes a variable. Create a pointer for the variable. Output a) the value of the pointer Then change the value of the pointer and output the same data again.
#include
#include
#include
int main(int argc, char* argv[]){
float g = 2.56f;
float* ptr_g = &g;
std::cout << "Pointer value: " << ptr_g << " Pointer address: " << &ptr_g << " Dereferenced pointer: " << *ptr_g << std::endl;
std::cin.get(); // Wait for Enter
ptr_g++;
std::cout << "Pointer value changed!" << std::endl;
std::cout << "Pointer value: " << ptr_g << " Pointer address: " << &ptr_g << " Dereferenced pointer: " << *ptr_g << std::endl;
return 0;
}
Consider line 9. In this case all three values will be different but the third value will be equal to the value assigned to the variable, 2.56. The first two will correspond to the addresses of the When the user presses Enter on the keyboard, the rest of the code is executed. The value of the pointer is changed (“increased”). This means, the address of the variable
Pointer value changed! Ex. 14 Write a program that declares a variable and a reference to that variable. Print out the memory address of both the variable and the reference.
#include "stdafx.h"
#include
int main()
{
float a = 2.0f;
float& b = a;
std::cout << &a << std::endl << &b << std::endl;
return 0;
}
I ask for the memory addresses of both
As can be seen, both the variable and the reference share the same memory address. This indicates that the reference Recall that the Ex. 15 Write a swap function that takes two pointer parameters. I’m going to write a swap function that swaps two variables not directly but by manipulating their pointers. I’m not sure if that’s what is expected in this assignment but I can’t think of any other use for such a swap function.
#include
#include
#include
void Swap(float* ptr_af, float* ptr_bf)
{
float p = *ptr_af;
float* ptr_temp = ptr_af;
ptr_af = ptr_bf;
ptr_bf = ptr_temp;
*ptr_bf = *ptr_af;
*ptr_af = p;
}
int main(int argc, char* argv[])
{
float a = 15.75f;
float b = 20.23f;
float* ptr_a = &a;
float* ptr_b = &b;
Swap(ptr_a, ptr_b);
std::cout << *ptr_a << "t" << *ptr_b << std::endl;
return 0;
}
So in our main function, we declare Then I use a third pointer, Update (2015-02-10). It turns out that in this exercise you don’t need to swap pointers themselves, only the values of the variables the pointers point to. This can be done in a simpler way:
#include
#include
void Swap(float* ptr_a, float* ptr_b)
{
float temp = *ptr_a;
*ptr_a = *ptr_b;
*ptr_b = temp;
}
int main(int argc, char* argv[])
{
float a = 15.75f;
float b = 20.23f;
float* ptr_a = &a;
float* ptr_b = &b;
Swap(ptr_a, ptr_b);
std::cout << a << "t" << b << std::endl;
return 0;
}
Ex. 16 Write a program that declares two variables of type
#include
#include
#include
double* Calc(double* ptr_af, double* ptr_bf)
{
double res;
double* ptr_res = &res;
*ptr_res = 2 * *ptr_af * *ptr_bf / (*ptr_af + *ptr_bf);
return ptr_res;
}
int main(int argc, char* argv[])
{
double a = 15.75;
double b = 20.23;
double* ptr_a = &a;
double* ptr_b = &b;
std::cout << "2a*b/(a+b)= " << *Calc(ptr_a, ptr_b) << std::endl;
return 0;
}
Ex. 17 Write a program that asks the user to enter an integer between 10 and 20. Then allocate an array with that many elements. Fill the array with random numbers and print out those random numbers.
#include "stdafx.h"
#include
#include
#include
int random()
{
return rand() % 20;
}
int main()
{
srand((unsigned int)time(0));
int n;
std::cout << "Enter an integer between 10 and 20." <> n;
if (n 20)
std::cout << "The integer you entered is not between 10 and 20!n";
} while (n 20);
int* array = nullptr;
array = new int[n];
for (int i = 0; i < n; i++)
{
if (i % 5 == 0)
std::cout << "n";
array[i] = random();
std::cout << array[i] << "t";
}
std::cout << std::endl;
delete[] array;
array = nullptr;
return 0;
}
When you declare an array the way I’m used to doing (e.g., When you instead allocate an array dynamically, i.e. outside the stack, the array can grow (or shrink) in size during run-time; the limitation that array element number must be fixed is therefore removed. Dynamic array allocation is what this exercise is about. Lines 22 and 23 allocate an array of type Whenever you allocate something dynamically you have to deallocate it to ensure there will be no memory leaks. This is done in lines 32 and 33 by using the Ex. 18-19 Below is a fragment of a program.
#include "stdafx.h"
#include
void main()
{
char *text = new char[128];
text[127] = '';
std::cout <> text;
std::cout << "text: " << text << std::endl;
//???
}
a) complete what’s missing This program is obviously missing deallocation statements. An array of type
#include "stdafx.h"
#include
void main()
{
char *text = new char[128];
text[127] = '';
std::cout <> text;
std::cout << "text: " << text << std::endl;
delete[] text;
text = nullptr;
}
When I execute the program, the program allows me to input multiple characters (even though no string variable has been declared). This is so because the array of characters stores each typed character as one of its elements. The print statement prints those elements, so the program effectively outputs what the user typed in.
If you replace
NB: notice how the last (128th) element of the character array is set to backslash zero. This is the end of array symbol. If you type in more than 127 characters they will not be output since the 128th element always indicates the end of the array has been reached. Ex. 20 Write a program that takes an input string of arbitrary length from the user (max 1024, only letters a-z). Calculate how many times every character is typed and display these statistics.
#include
#include
#include
void main()
{
std::string input;
std::cout << "Please enter a text, 1024 chars at most, only English letters." << std::endl;
std::getline(std::cin, input);
int counter[26] = {0};
char compare = 'a';
char Compare = 'A';
for (int i = 0; i < input.length(); i++)
{
for (int j = 0; j < 26; j++)
{
if (input.at(i) == compare || input.at(i) == Compare)
{
counter[j]++;
break;
};
compare++;
Compare++;
};
compare = 'a';
Compare = 'A';
}
std::cout << "nt------------------------------------";
compare = 'a';
for (int k = 0; k < 26; k++)
{
if (k % 5 == 0)
std::cout << "nt";
std::cout << compare << ": " << counter[k] << "t";
compare++;
};
std::cout << "nt------------------------------------" << std::endl;
}
The array The outer loop (i-loop) cycles through every character in the string. The inner loop (j-loop) compares the current element in the string (obtained via the method In the first iteration, the variables If the i-th element matches the letter currently checked for, increase counter. For each i-iteration, the counter index cycles through all the values from 0 to 25, increasing counts for matching letters. Look at lines 22 and 23. In each j-iteration, It is important to reset So much for this program. Other stuff is mostly formatting. Check it out with random English text and verify correct letter counts are output. Update (2014-11-29). I got some feedback about optimizing the program. First of all, after line 19, when we hit a matching letter, we know that there will be no other match in that position, so we can break the inner loop. As such, I included a Another advice I received was that I could include an additional if-check after line 13. If the current character is something that will not match any letter (e.g., a space or a punctuation mark), you don’t even need to enforce the inner loop and skip instead to the next position in the string. That would include enclosing the entire inner loop by an if-statement. However, this approach doesn’t feel clean or methodically complete in the sense of doing something that theoretically covers all possible cases. Why space in particular? Then we would also need to check for a “:” and a “;” and a “?” etc. Still, it would save 26 inner loop iterations for any non-letter character, so considering certain characters appeat often in the text, it may be useful including the check for those specific characters. |