RU beehive logo ITEC dept promo banner
ITEC 120
2008spring
ibarland,
jdymacek

homeinfoarchiveexamslectureslabshws
RecipeLawsliessyntaxjava.lang docsjava.util docs

lab12b
practicing loops
99 bottles

bottles of beverages

99 bottles of rootbeer on the wall,
99 bottles of rootbeer;
   take one down,
   pass it around,
98 bottles of rootbeer on the wall.

98 bottles of rootbeer on the wall,
98 bottles of rootbeer;
   take one down,
   pass it around,
97 bottles of rootbeer on the wall.

[etc., ad nauseum]

— anonymous1
  1. Download the provided documentation, stubs, and test cases for today's lab. (how-to-get-jar-files-into-BlueJ—Downloading .jar files into BlueJ)
  2. Write a function oneVerse, which takes in a number-of-bottles, and returns exactly one verse (exactly 5 lines) of that eternal classic, “99 bottles of rootbeer on the wall”.

    If you want to see your result formatted in a screen-specific way on the console, you can go to BlueJ's code pad and call the static method System.out.print2 yourself, passing it the result of calling your function. For example, call System.out.print( BottleSinger.oneVerse(17) ). from the Code Pad.

    BlueJ Terminal Window tip: It's helpful to select the menu options Options > Unlimited buffering as well as Options > Clear screen at method call. These options are only in the menu when BlueJ's terminal window has focus3

    You might wonder why we don't have our function not bother returning a String, but just print directly to the console window. That's fine, if all you ever want to do is turn console-window pixels on and off. However, by returning a String instead, our code is much more flexible: People can call our method and then use the resulting string for whatever it wants (sending it to a speech-synthesizer, writing it to System.out, writing it to a file or a web-page, or even email a single verse of the song to your parents each minute for 100 minutes). If our method hadn't returned a String, we'd be pre-emptively crippling future uses of our method.

  3. After you finish testing and debugging oneVerse, Write a function versesFromTo which takes two numbers, start and stop (with startstop), and returns the verses from start bottles down to but not including stop bottles.
    1. versesFromTo(50,47) should return verse #50, a blank line, verse #49, a blank line, verse #48, and a blank line. How many calls to oneVerse does this entail? (How many numbers are 50 or less, and greater than 47?)
    2. versesFromTo(50,49) should return just verse #50 followed by a blank line. (How many numbers are 50 or less, and greater than 49?)
    3. What should versesFromTo(50,50) return? (What are all the numbers which are 50 or less, and greater than 50?)
    Note that there is a blank line after each verse, even the last.4
  4. We realize that our code needs to do something over-and-over, so we'll want to use a loop. Which type -- for-each, or while? Well, we don't already have a list-of-somethings to work with, so a for-each loop won't do. That leaves trying a while loop…

    1. declare/init Accumulator variable (part of loop initialization)
      When you are halfway through the problem, what result will you have accumulated so far? (Declare a “so-far” variable of the appropriate type, to hold this value.) (What value should this “so-far” variable hold initially, before starting?)
    2. declare/init Index variable (part of loop initialization)
      What loop variable do we need, to keep track of where we are in the loop? That is, when we are halfway through the problem, we need to know where we are currently at. Declare an int verseNum, which will hold the number of the next verse we'll sing. (What value should verseNum it initially, before starting?)
    3. How to update the accumulator (part of loop body)
      • How do we get the text of the current verse we're looking at? Call an existing method!
      • How do we use combine this current verse with the “so-far” variable, to reflect the fact that we've now seen that one additional verse?
    4. How to update the index variable? (part of Loop control logic:
      How do we update verseNum, so that the next time through we'll be considering the next verse? (We do this inside the loop.)
    5. What is our condition whether to continue? (Part of Loop control logic)
      What is a boolean condition which tells use when to continue the loop?
    You might want to refer back to how countDown (from lecture) answered these same questions, to help you answer them for this problem.

    BlueJ tip:If BlueJ's progress-bar is churning away and you think your program is an infinite loop, you can right-click on the progress bar to stop it.
  5. Write a function entireSong which takes no inputs, and returns the entire lyrics to the song. Don't repeat any code; instead just call a method which already computes sub-answer this method wants.

    Use a named-constant to represent where the song starts. For testing, change the named-constant from 99 to (say) 4. Just call your method and give the result to System.out.println.

  6. ITEC120 does not condone underage drinking, nor in peer pressure to drink rootbeer. It would be nice to provide users with a version of our function which allows the caller to specify what sort of drink they'd like 99 bottles of. For example,

    oneVerse( 17, "ginger ale" ) =
    "17 bottles of ginger ale on the wall,
    17 bottles of ginger ale;
      take one down,
      pass it around,
    16 bottles of ginger ale on the wall.
    "

    Your task Make overloaded5 versions of each of your three previous methods so that it they can each take an additional argument: the beverage to drink.
    (You'll temporarily have repeated code, as each method will have a lot of code in common with each rootbeer-specific method. Don't worry; we'll fix that in the following steps.)

  7. We now have lots of repeated code. Let's fix this, so that we have hardly any more code that a moment earlier:
    How can we delete most of oneVerse, and replace it with a single line of code?
    Put more concretely: If you were oneVerse(int), and you were handed the input 17 (say), your job is to compute the verse starting with “17 bottles of rootbeer on …”. How can you be lazy and get the other method, oneVerse(int,String), to compute the answer for you? Remember, you definitely need to hand that other method two inputs, as per its signature.)
  8. Similarly, get rid of repeated code by gutting the old versesFromTo(int,int) and entireSong(), replacing their body with a single call to their more generalized cousins.

  9. Optional, but highly recommended: If your boss suggests that the default drink should be something changed from “rootbeer” to something more hip, like “boba tea”,
    How many places does your code need to be changed? Hopefully, one word from your boss requires only one change in your code!

    (Yes, the caller can already specify a particular drink; this problem is talking about what default is used whenever the caller doesn't specify.)

  10. Optional: Currently, if you modify "bottles" of rootbeer to be "jugs" of rootbeer, how many places do you have to modify in your code?
    Make it so that your code only has to change in one place, to match the fact that I can describe the change in only one word!
  11. Optional: Your program currently prints half of the lyrics of a song which goes on forever: “…go to the store, buy one more, 1 bottle of rootbeer on the wall” (etc.)!
    Write a program which returns a given number of down-and-up “mega-verses” of this longer song. (That is, one mega-verse is nearly 1000 lines of text; you can omit unit tests, and just print the result to the System.out.) You still do not want any repeated code, so you'll want to factor out the common part of the verse structure into its own function, and pass in the varying part as an argument.
  12. Not for today...

    (These next tasks will either be for a future lab, or will be for extra-credit.)

    The approach taken above has not been object-oriented: all the methods are static. How would we design things differently, if we decided that a user might make mutliple BottleSingers? What information (fields) might each singer have?

  13. Each singer might have some notion of where they are, in the overall song. Write a method String nextVerse(). (This allows the user to have a BottleSinger sing one verse, wait a while, and then just ask the next verse, without the user needing to keep track themselves of what verse to sing.)

    There are two obvious ways you might have a field to represent info: either what verse the server has sung most recently, or what verse they will next sing when asked. Either way is okay, but name your field appropriately. Note that while we don't want a public getter for this field in general, for writing test cases it might be useful to have a getter which returns this. Make that getter neither public nor private6

    Of course, make some test cases before writing nextVerse. You might find it more convenient to edit the unit-test itself, rather than use BlueJ's “record” feature.

  14. Write the method boolean hasNextVerse(), which returns false if the BottleSinger has finished all the verses.
  15. Rather than requesting a specific, custom beverage every time you ask them to sing, a BottleSinger might have this info as a field. Write void setFavoriteBeverage(String).
  16. Generalize nextVerse to take in an int: the number of verses to sing. Generalize hasNextVerse analagously.
  17. Write a method public static void main( String[] _ignore ), which creates a list of three BottleSingers, gives them each a different favorite beverage, and then has them interleave verses of their song. (That is, verse #99 from the first BottleSinger, followed by verse #99 from each of the next two, followed by verse #98 from each singer, etc., as long as you like.) Print the results to System.out.
  18. Let BottleSingers either be progressing through their song downwards (taking bottles down and passing around), or upward (going to the store and buying one more). What field might this entail? How do you need to change nextVerse and hasNextVerse?
  19. For the future, once we talk about interfaces: Make your class implement the Iterator<String> interface, changing the names of nextVerse and hasNextVerse appropriately. (Read the documentation, and do something appropriate to implement remove.) Have your test driver use a for-each loop to get verses and print them to the console window.

On Friday, we will check off through #7. We'll check that (a) you pass all the provided test cases, and (b) no repeated code: four methods should be a single line, one should be three-five lines (declare/init accumulator; loop control; loop body; return)


1Anonymous, and extremely annoying      

2We don't need to use println, as our method already icludes newlines characters in the returned String.      

3You can give the terminal window focus by either (a) calling System.out.println, or (b) selecting View > Terminal.      

4If you really wanted no blank line after the last verse, it's a bit annoying. Approaches include: (a) Have your loop handle the first verse (if it exists) specially; then have a loop which handles all the rest (prepending a newline each time). (b) including an 'if' statement inside the loop, to check for the last verse specially; (c) adding a newline after every single verse, and then remove the last character (retroactively un-doing part of the last iteration — Yech!). None of these are aesthetically pleasing, though both (b) and (a) are acceptable. If you do one of these, you'll also want to edit the unit-test file, so that it doesn't expect the newline at the end of the last verse.      

5 Remember that overloading is a shallow concept: If your language didn't have overloading (like Java does), you wouldn't really mind; you'd just call the methods oneVerse_v1(int) and oneVerse_v2(int,String). When other people call your method, they are fully aware of whether they are passing in one argument or two, so they are fully able to decide whether to call _v1 or _v2. However, overloading is convenient: it means people need to remember fewer method names, especially for closely-related functions.      

6 This makes the getter “package protected”, which means that everybody inside the package can access it; the unit-test is inside the same package.      

homeinfoarchiveexamslectureslabshws
RecipeLawsliessyntaxjava.lang docsjava.util docs


©2008, Ian Barland, Radford University
Last modified 2008.Apr.10 (Thu)
Please mail any suggestions
(incl. typos, broken links)
to iba�rlandrad�ford.edu
Powered by PLT Scheme