Problems dealing with a grading program

I have encountered a problem when I was dealing with a grading program. The program contains a header file and grading.cpp file.

Below is the code of the grading.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include <iostream>
#include "grading.h"

using namespace std;

void Problem::readTypeAndAnswer(std::istream &input)
{
    string type;
    string standard_answer;
    string keywords [10];
    int num_keywords;

    int i = 0;

    input >> type;

    if (type == "S")
    {
        input >> num_keywords;
        input >> keywords [num_keywords - 1];
    }

    if (type == "M" || type == "F")
    {
        input >> standard_answer;
    }

}

// An array to store all students
Student students[50];
// The number of students
int num_students;
// An array to store all problems
Problem problems[50];
// The number of problems
int num_problems;

int main(void)
{
	int i;
	// Read students' names and numbers from "studnets.txt"
	ifstream if_students;
	if_students.open("students.txt");
	if_students >> num_students;
	for (i = 0; i < num_students; i++)
		students[i].readNameAndNumber(if_students);
	if_students.close();

	// Read problem specifications from "problems.txt"
	ifstream if_problems;
	if_problems.open("problems.txt");
	if_problems >> num_problems;
	for (i = 0; i < num_problems; i++)
		problems[i].readTypeAndAnswer(if_problems);
	if_problems.close();

	// Read students' answers from "<student number>.txt"
	for (i = 0; i < num_students; i++)
		students[i].readAnswers(num_problems);

	// Calculate scores of students
	for (i = 0; i < num_students; i++)
		students[i].calculateScore(problems, num_problems);

	// Sort the students, and calculate the ranks
	sortByScore();

	// Output sorted list of students to "ranking.txt"
	ofstream of_students;
	of_students.open("ranking.txt");
	for (i = 0; i < num_students; i++)
		students[i].write(of_students);
	of_students.close();

	return 0;
}


The program also includes a file named problems.txt, which includes the total number of problems in the first line. In the subsequent lines, it first indicates the type of problem by one character, and followed by the standard answer of the problem. When it comes to problems that has more than one keyword in standard answer, it will first indicate the number of keywords followed by the keywords.

Example of problems.txt:
6
M F
M B
F Apple
F FB3536
S 4 apples oranges banana berries
S 2 eyes ears

When I was about to define the function void readTypeAndAnswer in the grading.cpp, I find that I don't know how to include the Type and Standard Answer in the inpupt file in the function. Since the function is already declared with only one parameter which is the input file.

How can I successfully define the function with only one parameter? I have tried writing the code but I don't know if it is correct or not.Thanks for your help:D
Last edited on
Hello paulpaul,

After working with the program for awhile i found two problems.

1. The local variables in the "readTypeAndAnswer()" function need to be removed. These are local variables and override the variables of the "Problem" class. Also when the function ends these variables and values are lost. Once I put a comment on these local variables the input from the file was stored for later use.

2. In the if" statement you are inputting the correct number of key words, but only inputting one key word. You need a for loop to read all available key words.

3. In lines 2 -5 of the "problems.txt" file each has a type and answer whereas the last two lines only have a type, number of key words and the key words, but no answer. Not sure if that is what you want.

That is all I could because I had to comment out most of main because there was so much missing.

There is a start.

Hope that helps,

Andy
Hi Andy,

Actually that is an assignment, so I don't quite really know how to interpret the preset main function. Where the readTypeAndAnswer will help to read the type and standard answer of each question. However, I am not sure about how to define the function such that the type and standard answer which are predeclared will be stored in each class array problem [i]. So actually I can only start with this function and stopped at this function also :( For the question type of S, I think it does not include any standard answer, but only the number of keywords and the keywords themselves.

For the if loop, I will try to make changes to the current function. But I'm still not sure how can I execute the function to store the question type and the standard answer in the function. Could you help to see how I can do that? Much thanks :D
Hello paulpaul,

Refer to point 1 of my previous post and I will try this:

"readTypeAndAnswer()" function
string type;
string standard_answer;
string keywords [10];
int num_keywords;

ll these variables are defined at the beginning of the "readTypeAndAnswer()" function. They are also defined in the "Problem" class.

"Problem class"
std::string type;
std::string standard_answer;
std::string keywords[10];
int num_keywords;

As you can see they are identical. Now the variables in the "readTypeAndAnswer()" function are local variables thus blocking out or over riding the class variables that need to be used. Removing the local variables will use the class variables since the function is a member function of the class. In main the line problems[i].readTypeAndAnswer(if_problems); will store the read in the "problems" array for later use.

When you get a chance post the specs for the assignment, so I might have a better idea of what needs to be done.

Hope that helps,

Andy
Hi Andy,

Really thanks for the explanation :) At first, I actually did not redefine the variables, however, since the variable are private (and it is preset as private), the function can't get access to the variables as said by the debugger, and thus I don't know what I can do to store the value outside the class.

Actually, the assignment requires us to read two files, which are problems.txt and students.txt.

In problems.txt, it contains the information as mentioned in the previous comments. It includes the total number of problems in the first line (not more than 50). In the subsequent lines, it first indicates the type of problem by one character (M/F/S), and followed by the standard answer of the problem. When it comes to problems that has more than one keyword in standard answer, it will first indicate the number of keywords followed by the keywords.

Example of problems.txt:
6
M F
M B
F Apple
F FB3536
S 4 apples oranges banana berries
S 2 eyes ears

In students.txt, the first line includes the number of students (not more than 50). The subsequent number of lines include name of a student and his/her student number in each line.
Example of student.txt:
3
Coke 10234
Water 345213
Coffee 98652

The students' answers are saved as (studentnumber).txt (eg 10234.txt). And each line is answering to the corresponding questions.
Example of 10234.txt of answer to problems.txt:
D
B
apple

the fruits include apple, banana, orgnes.
Organs include Eyes, Ears.

Where the second and third answer gain 1 points each, the 5th answer will get 2 points and the 6th answer will get 2 points.
There can be blank answers to the problems. And the capitalized letter does not make a difference to the answer as long as it is same as the standard answer/keywords. For the S question type, as long as the answer has the keywords inside, the student will gain 1 point/keyword.

After grading all the answers, the result will be exported to a new ranking.txt, that rank students from the highest to lowest mark. In case two students gain same marks, they will be ranked according their alphabetical order of names, with the same rank. Each line will contain rank, student name, student number and points gained.

This is the specs for the assignment :D Hope it helps :D

Much thanks,
paulpaul :)
Hello paulpaul,

Thank you for the extra information. That will help when I get more into the program.

since the variable are private (and it is preset as private), the function can't get access to the variables

No only a "public" member function can access the "private" variables or functions of a class. The general program can not access the "private" area of a class, so the geeneral proram can not change the "private" variables of the class. Since "readTypeAndAnswer" is a "public" member of the class "Problem" it has access to the "private" variables of the class.

You might find this some useful reading
http://www.cplusplus.com/doc/tutorial/classes/
http://www.cplusplus.com/doc/tutorial/templates/

with the second link being more advanced, but useful for the future.

This is how I changed the function. The changes are in bold with comments:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void Problem::readTypeAndAnswer(std::istream &input)
{
	//  These variables do not allow you to use the "private" variables of the class.
	//  This function has full access to the "private" variables of the class.
	//string type;
	//string standard_answer;
	//string keywords[10];
	//int num_keywords;

	int i = 0;

	input >> type;

	if (type == "S")
	{
		input >> num_keywords;
		for (size_t lp = 0; lp < num_keywords; lp++)  // <--- Added for loop to read all key words.
		{
			input >> keywords[lp];
		}
	}

	if (type == "M" || type == "F")
	{
		input >> standard_answer;
	}
	std::cout << std::endl;
}


These changes allow the information read to be properly stored in the "problems" array defined and used in main. Once you get the information read from the file and stored you can move on with the program.

Hope that helps,

Andy
Hi Andy,

Thanks for the explanation and the sample code :D But did the size_t lp act likes simple int like int i=0...? And if I do not output the result in the program, can I simply write <return;> in the last line of the program? Thanks once again for answering me :)

Best,
paulpaul
Hello paulpaul,

Sorry about that it is just the way my Visual Studio sets up a for loop. "size_t" is an alias for an unsigned int, but as I understand it the actual storage space set aside for this kind of unsigned int depends on the compute it is used on.

If "return;" is at the end of a void function it is not necessary. The function will automatically return after the last line of code is processed.

If "return;" is at the end of main it will work, but the it is preferred as "return 0;"

Hope that helps,

Andy

P.S. Forgot to mention, there are many occasions when the rhs of < will return a "size_t", i.e.,
; i < string.length(); or ; i < vector.size();.
Last edited on
Hi Andy,

Really thanks for your help :) Now I understand the use of return better :)
Last edited on
Hello paulpaul,

I have a question for you. Since this is an assignment have parts of this program along with files like "10234.txt" been provided for you? If so, it does not mean that functions with one parameter need to be that way. You might have to add more parameters to a function to make it work.

As I read through the code you have posted I see some problems like:
"Student::readAnswers(int num_problems)" at line 107. line 114 if your compiler is C++11 compliant or better the ".c_str()" is not needed, a regular string will work. The function as is will, but answers of type "S" will require more work later.

In line 44 "Problem::grade(std::string student_answer)" my first question is where did you get "type" from? As I look at the code for type "S" I do not believe the find will work. Still have to test this and rework some of the program I have.

Line 161 "void Student::setRank (int r)". I am not sure what the "int r" is used for yet. Bust I can say that it is never used in the code you have. When you say "for (int r = 0; ..." only this "r" is used inside the for loop. The parameter "r" is not seen in the scope of the for loop.

Read about scope:
http://en.cppreference.com/w/cpp/language/scope
http://www.cplusplus.com/doc/tutorial/namespaces/#scope
https://www.google.com/#safe=strict&q=c%2B%2B+scope

In line 175 you have "students[r].rank == r + 1;" did you mean students[r].rank = r + 1;". Did you mean to comparing or did you mean to assign?

I am curious about the "void Student::calculateScore(...)" function. Did you use the code from your original post or has this changed?

I will work on implementing the new code you have posted and see what happens.

Hope tht helps,

Andy
Hi Andy,

The files are provided in the assignment file for me...So it is relatively easy for us to do the programming.

For the compiler problem, I think I still need to use ".c.str()" since my teacher told us to do so...I bet the auto grading system for my assignment does not support regular string.

In line 44, the type is given in the problem.txt. In each line, the first character (M/F/S) indicates the type of each problem.

In line 175, I have changed the "==" to "=", I've noticed the mistake I have made...;p

For the void function, I use code from my original post since the function is not allowed to be changed in my assignment.

For the setRank function, I have figured out how to use it...and below is the code I have
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
#include <iostream>
#include <fstream>
#include <string>
#include <cctype>
#include "grading.h"

// #include <algorithm> is forbidden!

using namespace std;

void Problem::readTypeAndAnswer(std::istream &input)
{
    input >> type;

    if (type == "S")
    {
        input >> num_keywords;

        for (int a = 0; a < num_keywords; a++)  // Loop to read all key words.
        {
            input >> keywords[ a ];
        }
    }

    if (type == "M" || type == "F")
    {
        input >> standard_answer;
    }
}

int Problem::grade(std::string student_answer)
{
    int marks;

    if (type == "M")
    {
        if (student_answer == standard_answer)
            return marks = 1;
        else
            return marks = 0;
    }

    if (type == "F")
    {
        for(int c = 0; c < student_answer.length(); c++)
        {
            student_answer [c] = tolower(student_answer [c]);
        }

        for(int d = 0; d < standard_answer.length(); d++)
        {
            standard_answer [d] = tolower(standard_answer [d]);
        }

        if (student_answer ==  standard_answer)
            return marks = 2;
        else
            return marks = 0;
    }

    if (type == "S")
    {
        for(int e = 0; e < student_answer.length(); e++)
        {
            student_answer [e] = tolower(student_answer [e]);
        }

        for(int f = 0; f < standard_answer.length(); f++)
        {
            standard_answer [f] = tolower(standard_answer [f]);
        }

        marks = 0;

        for(int g = 0; g < num_keywords; g++)
        {
            int h = student_answer.find(keywords [g]);

            if (h != string::npos)
                marks = marks + 1;
        }
        return marks;
    }
}

void Student::readNameAndNumber(std::istream &input)
{
    input >> name;
    input >> number;
}

void Student::readAnswers(int num_problems)
{
    ifstream stu_answers;
    string temp;

    temp = number + ".txt";

    stu_answers.open (temp.c_str());

    for (int b = 0; b < num_problems; b++)
    {
        getline(stu_answers, answers [b]);
    }

    stu_answers.close();

}

int Student::getScore ()
{
    return total_score;
}

// An array to store all students
Student students[50];
// The number of students
int num_students;
// An array to store all problems
Problem problems[50];
// The number of problems
int num_problems;

void swap(Student &a, Student &b)
{
    Student temp = a;
    a = b;
    b = temp;
}

// Sort the students, and calculate the ranks
// Do not use the "sort" function in the standard library
void sortByScore()
{
    for (int j = 0; j < num_students - 1; j++)
    {
        for (int k = 0; k < num_students - j - 1; k++)
        {
            if (students[k].getScore() < students[k + 1].getScore())
            {
                swap (students[k], students[k + 1]);
            }
        }
    }
}

void Student::setRank (int r)
{
    for (int l = 0; l < num_students - 1; l++)
    {

        for (int r = 0; r < num_students - l ; r++)
        {
            if (students[r].getScore() == students[r + 1].getScore())
            {
                students[r].rank = r + 1;
                students[r + 1].rank = r + 1;
            }

            else
            {
                students[r].rank = r + 1;
            }
        }
    }
}

void Student::write (std::ostream &output)
{
    for (int m = 0; m < num_students; m++)
        Student::setRank(m);

    output << rank << " " << name << " " << number << " " << total_score << endl;
}

/*
Any changes of the main function will be ignored,
and the main function will be replaced by the standard one.
*/
int main(void)
{
    int i;
    // Read students' names and numbers from "studnets.txt"
    ifstream if_students;
    if_students.open("students.txt");
    if_students >> num_students;
    for (i = 0; i < num_students; i++)
        students[i].readNameAndNumber(if_students);
    if_students.close();

    // Read problem specifications from "problems.txt"
    ifstream if_problems;
    if_problems.open("problems.txt");
    if_problems >> num_problems;
    for (i = 0; i < num_problems; i++)
        problems[i].readTypeAndAnswer(if_problems);
    if_problems.close();

    // Read students' answers from "<student number>.txt"
    for (i = 0; i < num_students; i++)
        students[i].readAnswers(num_problems);

    // Calculate scores of students
    for (i = 0; i < num_students; i++)
        students[i].calculateScore(problems, num_problems);

    // Sort the students, and calculate the ranks
    sortByScore();

    // Output sorted list of students to "ranking.txt"
    ofstream of_students;
    of_students.open("ranking.txt");
    for (i = 0; i < num_students; i++)
        students[i].write(of_students);
    of_students.close();

    return 0;
}


And the header's code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#ifndef GRADING_H
#define GRADING_H
// Above two lines are for avoiding error of multiple inclusion of the same header file

#include <iostream>
#include <string>

// Implement member functions of this class
class Problem
{
public:
	// Read one line of "problems.txt" and get the type and standard answer of one problem
	// The parameter "input" can be used the same way as cin or an object of ifstream
	void readTypeAndAnswer(std::istream &input);
	// Return a score according to the student answer
	// using type and standard answer
	int grade(std::string student_answer);

private:
	std::string type;
	std::string standard_answer;
	std::string keywords[10];
	int num_keywords;
};

// Implement member functions of this class
class Student
{
public:
	// Read one line of "students.txt" and get the name and number of one student
	// The parameter "input" can be used the same way as cin or an object of ifstream
	void readNameAndNumber(std::istream &input);
	// Read the file "<student number>.txt" and get all the answers of this student
	void readAnswers(int num_problems);
	// Write "<rank> <name> <number> <points>" on one line of "ranking.txt"
	// The parameter "output" can be used the same way as cout or an object of ofstream
	void write(std::ostream &output);
	// Return the total score of this student
	int getScore();
	// Set the rank of the student to r
	void setRank(int r);

	// Calculate the score of this student from a given list of problems
	// It is defined here, you do not redefine this function in your grading.cpp
	void calculateScore(Problem p[], int n)
	{
		total_score = 0;
		for (int i = 0; i < n; i++)
		{
			score[i] = p[i].grade(answers[i]);
			total_score += score[i];
		}
	}

	// Optional: you may define a function to swap two Student objects, which may help sorting
	friend void swap(Student &a, Student &b);

private:
	// Name and student number, assigned by readNameAndNumber
	std::string name;
	std::string number;
	// Answers to each question, assigned by readAnswers
	std::string answers[50];
	// Assigned by calculateScore
	int score[50];
	int total_score;
	// Assigned by sortByScore
	int rank;
};

// Below forbids you from using std::sort
namespace std
{
	template <class A>
	void sort(A, A)
	{}
	template <class A, class T>
	void sort(A, A, T)
	{}
}

// Following line is for avoiding error of multiple inclusion of the same header file
#endif 
Last edited on
The current problem I have is the Problem::grade function...Where some of the marking went wrong...I guess it is some problem related to the type S but I don't know what I have done wrong....:(
Hello paulpaul,

For the compiler problem, I think I still need to use ".c.str()".
he ".c_str()" is only needed pre "C++11", but it will work.

In line 44, the type is given in the problem.txt.
, so I will ask this way. Where did you get the value for "type" from? Yes it is in the "problems.txt" file, but you do not read this file in the "grade" function. And it is available in the "problems" array, but you are not using this array. So, where does "type" get its value from.

If int Problem::grade(std::string student_answer) was provided to you then you have to realize this is only a starting point and needs more to make the function work. This is my solution to the "grade" function:

1
2
3
4
5
6
7
int Student::grade(Problem (&problems)[50], std::string student_answer, int index)
{
	int marks;
	std::string standard_answer = problems[index].GetStandardAnswer();
	std::string type = problems[index].GetType();

	if (type == "M")


I should back up and start with the
void Student::calculateScore(Problem (&p)[50], Student (&students)[50], int numProblems) function. I had to pass the two arrays "problems" and "students" to "calculateScore" because both arrays are needed to calculate the score for each answer from the "students" array. From the "calculateScore" function you have to pass the "problems" array to the grade function. Notice the code Student (&students)[50]. This is passing by reference. This is not needed, Student students[] will work fine. I did the by reference for my use during testing. It allows me to see the entire array not just one element.

In the section for type "S" the third for loop along with the find is wrong. In the condition g < num_keywords You are trying to access "num_keyWords" directly from the class and you can not do it this way I had to add the function int GetNumKeyWords() { return num_keywords; } to the class and write the for loop as
for (int g = 0; g < problems[index].GetNumKeyWords(); g++) to get the correct number on the rhs of <.

In the find part you will need to access the "problems" array then the "keywords" array to each possible answer.

You deleted the message I was working from, but as I remember it I had to change if (h != string::npos) to
if (h == string::npos) to get the if statement to work the way you have it written. Otherwise you would have to switch the lines after "if" and "else".

Still have not reached the "setRank" function yet because I got stuck on the "sort" function last night.

If your teacher gave you this then I would say that it is OK to use, but I would otherwise caution against putting code into the std namespace unless you know what you are doing. IMHO.

1
2
3
4
5
6
7
8
9
namespace std
{
	template <class A>
	void sort(A, A)
	{}
	template <class A, class T>
	void sort(A, A, T)
	{}
}


Hope that helps,

Andy
Hi Andy,

Thanks for your work :D Really much appreciated.

I read the type from line 13 "input >> line".

However, the header file is not allowed to change....but seems that the function cannot be declared in the main function to access the private member of the Problem class...Can I still declare new function then?
Hello paulpaul,

Sorry for the delay. My internet access has been a problem the last few days.

I would say that if you can not change the header file than declaring a new member function in main would not be allowed because you will have to change the header file.

After understanding what you were given and what you needed to do I redid my version to match what you needed. I do not know if it would make any difference, but I put the global variables at the beginning before the function definitions in stead of the middle. Thinking about it, since some of the code was provided to you, it should not make any difference where the global variables are defined. At the beginning just seams a better place.

I still need to adjust the student answer files to check the sort.

Hope that helps,

Andy
Topic archived. No new replies allowed.