99 Bottles of Beer/C++/Object Oriented
Appearance
Another solution, which in addition correctly handles the grammar. This solution is object-oriented. It is completely overkill for this problem.
#include <iostream>
#include <string>
#include <sstream>
namespace bottle_song
{
// =================================================================
// ***********************************
// * Abstract base class for things. *
// ***********************************
class thing
{
public:
// return the singular of the thing
virtual std::string singular() const = 0;
// return the plural of the thing
virtual std::string plural() const = 0;
// we need a virtual destructor, too
virtual ~thing() {}
};
// =================================================================
// ***************
// * Containers. *
// ***************
// Containers are things which can contain other things. The
// following class makes any thing into a container. The container
// class is actually a decorator which makes any thing into a
// container. Note that the contained thing is actually mutable,
// even if the container is not. Note that the container can only
// contain a single thing; if it shall contain several things, make
// it contain a collection instead.
class container: public thing
{
public:
// The format gives the name. %self% is replaced by the containing
// object's name (in proper pluralization), %contained% is
// replaced by the contained object's name.
container(std::string fmt, thing const& what, thing const& contained);
std::string singular() const;
std::string plural() const;
private:
std::string format;
thing const& self;
thing const& contained_thing;
// helper function to replace strings
static void replace(std::string& str, std::string from, std::string to);
};
container::container(std::string fmt,
thing const& what,
thing const& contained):
format(fmt),
self(what),
contained_thing(contained)
{
}
std::string container::singular() const
{
std::string result = format;
replace(result, "%self%", self.singular());
replace(result, "%contained%", contained_thing.singular());
return result;
}
std::string container::plural() const
{
std::string result = format;
replace(result, "%self%", self.plural());
replace(result, "%contained%", contained_thing.singular());
return result;
}
void container::replace(std::string& str, std::string from, std::string to)
{
std::string::size_type pos = str.find(from);
if (pos != std::string::npos)
str.replace(pos, from.length(), to);
}
// =================================================================
// *********************************
// * A collection of equal things. *
// *********************************
// In the context of this program, a collection of things is again
// considered a single thing.
// This is a concrete class.
class equal_collection: public thing
{
public:
// constructor
equal_collection(int count, thing const& what);
// get singular
std::string singular() const;
// get plural. This has to be implemented, even if it isn't used,
// because the inherited version is pure virtual, and not
// implementing this would make the class abstract.
std::string plural() const;
// this just returns whether thwere are still things left to take away.
bool there_is_some_left();
// this takes one thing away from the collection. Taking a thing
// away from an empty collection is undefined behaviour (i.e. not
// explicitly checked).
void take_one_away();
private:
int count_of_things;
thing const& type_of_thing;
};
// equal_collection constructor
equal_collection::equal_collection(int count, thing const& what):
count_of_things(count),
type_of_thing(what)
{
}
// get singular. The singular of the collection is just the number
// followed by the thing, proper pluralized. The fact that it's
// grammatically still a plural form doesn't matter for the problem
// at hand.
std::string equal_collection::singular() const
{
std::ostringstream oss;
oss << count_of_things << " ";
if (count_of_things == 1)
oss << type_of_thing.singular();
else
oss << type_of_thing.plural();
return oss.str();
}
// get plural. For collections, the plural is just "times " followed
// by the singular. That is 3 collections of 4 bottles each give 3
// times 4 bottles.
std::string equal_collection::plural() const
{
return "times " + singular();
}
// tell if there are still things to take away. There are things to
// take away if there are more than 0 things.
bool equal_collection::there_is_some_left()
{
return count_of_things > 0;
}
// take one thing away from the collection. That is, just decrement
// the count of things.
void equal_collection::take_one_away()
{
--count_of_things;
}
// =================================================================
// ************
// * The beer *
// ************
class beer: public thing
{
public:
std::string singular() const { return "beer"; }
std::string plural() const { return "beers"; }
};
// =================================================================
// **************
// * The bottle *
// **************
class bottle: public thing
{
public:
std::string singular() const { return "bottle"; }
std::string plural() const { return "bottles"; }
};
// =================================================================
// ************
// * The wall *
// ************
class wall: public thing
{
public:
std::string singular() const { return "wall"; }
std::string plural() const { return "walls"; }
};
// =================================================================
// this is the class for the song.
class song
{
public:
song(int bottle_count);
void sing(std::ostream& where); // note: singing the song modifies it!
private:
beer beverage;
bottle drink_source;
container bottle_of_beer;
equal_collection collection_of_bottles;
wall bottle_storage;
container wall_of_bottles;
};
song::song(int bottle_count):
bottle_of_beer("%self% of %contained%", drink_source, beverage),
collection_of_bottles(bottle_count, bottle_of_beer),
wall_of_bottles("%contained% on the %self%",
bottle_storage, collection_of_bottles)
{
}
void song::sing(std::ostream& where)
{
while (collection_of_bottles.there_is_some_left())
{
where << wall_of_bottles.singular() << ".\n"
<< collection_of_bottles.singular() << ".\n"
<< "Take one down, pass it around.\n";
collection_of_bottles.take_one_away();
where << wall_of_bottles.singular() << ".\n\n";
}
}
}
int main()
{
bottle_song::song song(100);
song.sing(std::cout);
return 0;
}