99 Bottles of Beer/C++/Object Oriented
Another solution, which in addition correctly handles the grammar. This solution is object-oriented. It is completely overkill for this problem.
<lang cpp>
- 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;
} </lang>