R. Clayton (rclayton@monmouth.edu)
Fri, 11 Aug 2000 15:14:30 -0400 (EDT)
The set-multiset programming assignments have been graded and are available.
Here are a few comments on the assignment and on programming in general:
1 The assignment was a set-multiset programming assignment, with the
expectation that you'd actually use a set or multiset or two in your
assignment. Those of you that managed to avoid using both sets and
multisets aren't going to get as good a grade as those that managed to
shoehorn a set or multiset or two into their assignments.
You should know that I have a similar expectation for the map-multimap
assignment.
2 Those of you that did use a set or multiset or two didn't quite put them to
appropriate use. The general approached used was
order_msgs(word) {
multiset<msg, msg_greater> msgs
for each message m in msg_list do
m.count = the number of times word appears in m.header
msgs.insert(m)
msg_list.erase()
for each m in msgs do
msg_list.push_back(m)
}
msg_greater(m1, m2) {
return m1.order > m2.order
}
The problem with this approach is that it doesn't use multisets in any
essential way. The above code is equivalent to (not quite, see below)
order_msgs(word) {
for each message m in msg_list do
m.count = the number of times word appears in m.header
msg_list.sort(msg_greater)
}
A more appropriate use of a multiset would have been to associate one with
each message and store in it the words in the header of the associated
message. This way the code "the number of times word appears in m.header",
which most of you implemented from scratch, can be replace by a a call to
count(word) on the proper multiset; see my example (when I get around to
posting it) for details.
There is a more serious problem with the first version of order_msgs(): it
doesn't implement a stable sort. There is no guarantee (that I can find)
that identical keys inserted into a set or multiset are ordered in the
multiset using the order with which they were inserted. In other words, the
only guarantee is that identical values will be grouped together; there is
no guarantee that identical values will be grouped together by insertion
order. It is by the luck of the implementation that the result of reading
out the sequence of values from a multiset is a stable-sort sequence.
3 It is a truth generally acknowledged that global variables are bad, and
their use should be minimized. Global variables are bad because
understanding how a global variable is used in a program requires
understanding the entire program; a global variable can affect and be
affected by any code anywhere in the program. Even if a program references
a global variable in only one or two places, the entire program must be
understood to ensure that those one or two places are the only references to
the global variable.
Using object-oriented programming techniques (or at least using objects in
your programs) does not absolve you of the responsibility of avoiding global
variables. In fact, you must become more diligent in avoiding global
variables because object-oriented approaches tend to both increase the
number of global variables used (via instance variables, which are
essentially global variables relative to the class in which they're
declared) and making it harder to understand the scope of any particular
global variable (via inheritance, which can take an instance variable global
in one class and make it global to an arbitrarily long chain of descendent
classes).
Even though global variables are a central feature of the object-oriented
approach, you avoid using them in the same way you'd avoid using global
variables in procedural programming: pass them around as subroutine
parameters. Passing a global variable into a subroutine as a parameter
turns the global variable into a local variable within the subroutine.
For example, consider the following class
class Message {
private:
int mcnt;
void f(void) {
// blah blah blah
mcnt++;
// blah blah blah
}
void g(void) {
// blah blah blah
f();
// blah blah blah
}
}
The private member function g() calls the private member function f(), which
adds one to the instance variable (a.k.a. global variable) mcnt. The global
reference go mcnt in f() can be eliminated by passing mcnt in as a parameter
to f():
class Message {
private:
int mcnt;
void f(int & mcnt) {
// blah blah blah
mcnt++;
// blah blah blah
}
void g(void) {
// blah blah blah
f(mcnt);
// blah blah blah
}
}
Similarly, the global reference to mcnt in g() can be eliminated:
class Message {
private:
int mcnt;
void f(int & mcnt) {
// blah blah blah
mcnt++;
// blah blah blah
}
void g(ing & mcnt) {
// blah blah blah
f(mcnt);
// blah blah blah
}
}
Note that it now clear that the value of mcnt may change when g() is called;
this was not at all apparent in the original version of Message.
4 There were two test sets applied to the fourth assignment. You've already
seen the first; here's the second:
-- File begins on the next line; delete leading '-'
-From
-Subject: The cheese stands alone.
-From: The man on the moon.
-To: Mice everywhere.
-
-Frommage: it's what's for dinner.
-
-Frommage stands alone.
-
-Frommage and crackers
-- File ends on the previous line; delete leading '-'
Here's an example of a successful test:
spawn readmbox ../../test-mbox-4.2
There is 1 message.
* 1
Frommage: it's what's for dinner.
Frommage stands alone.
Frommage and crackers
* q
This archive was generated by hypermail 2.0b3 on Fri Aug 11 2000 - 15:25:05 EDT