Programming: Week 2 (cont.)

Ex. 10

Write a program that generates a random symbolic game map that contains 10times5 characters. Use the following characters:

# represents a wall
& represents a tree
_ represents walkable area

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, control, which allows me to save the random value which is only generated once. I then make three comparisons, which guarantee that all of #, & and _ will be uniformly distributed with the chance of 0.33.

But now I noticed that _ weren’t showing up at all. So now I figured out that the generator random() % 2 would only generate 0 or 1, instead of the expected 0, 1 or 2. This meant it was impossible to get to line 16 in the code and so no _’s were printed.

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 multiply function over and over again until the if-statement is no longer executed and the else-statement returns 1. The key point is to keep decreasing parameter value (a-1) so that the recursive function is eventually terminated.

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 Management

In 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
b) the address of the pointer
c) the value of the variable the pointer points to

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 g variable and its pointer, ptr_g. The addresses will be given in the hexadecimal system and they will be different each run-time.

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 g will change (but the address of the pointer will remain the same!). As a result, the variable g will take on a new value, that existed in that other address. Line 13, while identical to line 9, outputs different results, displaying both the address of the variable and its value changed:

Pointer value: 0046FE60 Pointer address: 0046FE54 Dereferenced pointer: 2.56

Pointer value changed!
Pointer value: 0046FE64 Pointer address: 0046FE54 Dereferenced pointer: -1.07374
e+008
Press any key to continue . . .

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 a and b. When I execute the program, I get the following output:

0069FC8C
0069FC8C
Press any key to continue . . .

As can be seen, both the variable and the reference share the same memory address. This indicates that the reference b does not exist as a separate variable or object in the program; instead it is just an alternative way of referring to the original object, a.

Recall that the & operator obtains the address of the variable it is written before. The whole expression &b can be understood as an address. The line &b = a “copies” the address of a; then by referring to b you are actually referring directly to a‘s address.

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 a and b. We then create pointers for them, ptr_a and ptr_b. We pass these pointers to the swap function and have the swap function accept them as pointers (this is ensured by the parameter definition float* ptr_af, float* ptr_bf).

Then I use a third pointer, ptr_temp, to “swap“ ptr_af and ptr_bf, i.e. make them point to the opposite variables. Finally, I use dereferenced pointers to manipulate the variables and switch the values they hold; a third variable, p, is still necessary when switching values.

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 double and a pointer to each one of them. Then write a function that calculates and returns the value of
dfrac{acdot b}{frac{1}{2}(a+b)}=dfrac{2ab}{a+b}.

#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., int array[20]) the array is allocated in the stack; an important property of such an array is that it has to have fixed size — the number of elements in the array must be known in advance (must be provided when declaring the array) and may not be changed afterwards. So int array[n] will result in a compilation error.

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 int dynamically. First, we create a pointer of that type (int) which is initially set to nullptr so that if dynamic allocation fails (which can happen) we will be sure the pointer is invalid. The next line does the actual allocation of the array of desired size. Allocation is dynamic because the keyword new is used. The array is tied to the previously created pointer, array. Other than that we refer to array elements in the same way we do in non-dynamically allocated arrays, e.g. array[5].

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 delete keyword and setting the pointer back to nullptr.

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
b) describe what the program outputs
c) replace the std::cin part with std::cin.getline(text, 127, ‘n’) and describe what happens

This program is obviously missing deallocation statements. An array of type char is allocated dynamically but is not deallocated. It is necessary to always deallocate dynamically allocated resources to avoid memory leaks. So the complete program should look like this:

#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.

std::cin stops reading input at the first whitespace it encounters. So if you type a string of characters separated by whitespaces, everything after the first whitespace will be ignored and not output, e.g.

Type some text : aaa bbb
text: aaa
Press any key to continue . . .

If you replace std::cin with std::cin.getline(text, 127, ‘n’), the read statement will read to the first line break instead of the first whitespace. In this case, everything you typed before pressing the Enter key on your keyboard will be output:

Type some text : aaa bbb
text: aaa bbb
Press any key to continue . . .

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 counter[26] holds the number of times each letter is typed (the English alphabet has 26 letters, and we are only counting occurrences of these 26 letters, so we know in advance the size of the array). Line 10 initializes the array, filling it with zeroes.

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 input.at(i)) with each of the 26 letters.

In the first iteration, the variables compare and Compare have the values of a and A, respectively. Comparisons are joined by logical OR making them case-insensitive.

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, compare and Compare values are increased. Recall that char types accept integer values and represent characters that have corresponding numbers encoded in the ASCII table. Conveniently enough, both capital and lower case letters are listed in alphabetical order in the ASCII table, their numbers increasing. So we can make the char variables compare and Compare refer to different letters by correspondingly increasing their integer values. This is done in lines 22 and 23.

It is important to reset compare and Compare to their start values after each complete run of an i-iteration, as shown in lines 25 and 26.

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 break in the if-statement (line 20).

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.

About Rokas Paulauskas

2014  Programming