Write a method regularPluralForm(word) that returns the plural of word formed by following these standard English rules:
a) If the word ends in s, x, z, ch, or sh, add es to the word.
b) If the word ends in y and the y is preceded by a consonant, change the y to ies.
c) In all other cases, add just an s.
Write a test program and design a set of test cases to verify that your program works.
(Roberts ch 10, problem 7).
What it looks like:
/*File: MakePlural.java * --------------------- * This program lets the user enter in any word and it will print out the plural form according to the basic, * not totally comprehensive rules outlined in the problem from ch. 10. It makes use of the private methods * makePlural (which take in a string and returns another string, the plural form of the word). */ import acm.program.ConsoleProgram; public class MakePlural extends ConsoleProgram { public void run() { while (true){ String word = readLine("Enter word and we'll make it plural. Enter '0' to stop: "); if (word.equals("0")){ break; } print(makePlural(word) + " "); } } //Takes the submitted string, inspects the last letter (or last 2 letters in case of "ch" and "sh"), //and returns the appropriate plural form. private String makePlural (String singularWord){ String pluralWord = ""; String strippedWord = singularWord.substring(0, singularWord.length()-1); char lastLetter = singularWord.charAt(singularWord.length()-1); switch (lastLetter){ case 's': case 'x': case 'z': pluralWord = singularWord + "es"; break; case 'h': // checking for if the word ends with "ch" or "sh" if ((singularWord.charAt(singularWord.length()-2)== 'c') || (singularWord.charAt(singularWord.length()-2)== 's')) { pluralWord = singularWord + "es"; break; } case 'y': if (isEnglishConsonant(singularWord.charAt(singularWord.length()-2))) { pluralWord = strippedWord + "ies"; break; } default: pluralWord = singularWord + "s"; break; } return pluralWord; } private boolean isEnglishConsonant(char ch) { switch (Character.toLowerCase(ch)) { case 'a': case 'e': case 'i': case 'o': case 'u': return false; default: return true; } } }
What made it tricky:
The program is structured to work well as a "switch" statement, and I had wanted to create a neat table that took a substring "lastLetter" (last 2 letters in the case of "ch" and "sh") and neatly return the proper plural form. However you can't switch on a string as I learned-- damn! So had to use the character only and switch on that, which made checking for "ch" and "sh" slightly less convenient.
Come to think of it, why *can't* you switch on a string? You'd think that it's a similar situation to comparing strings via "if (s1 == s2)", namely that it would often give you the wrong answer since they're comparing actual objects, not values. However I got an error when I attempted...anyone know why?
Another bug I ran into came from attempting to inspect the last letter and second-to-last-letter by doing charAt(-1) or charAt(-2) and making a substring of all but the last letter of the original word by doing "singularWord.substring(0, -1)". Those were throwing out-of-bounds errors. Oops-- sure would have been convenient.
The main bug I got stuck on before it worked is that it kept giving me the default case. So "sky" gave me "skys", "box" gave me "boxs" etc. I didn't know why and whether the non-default case was even getting run so I added debugging lines to print out the plural form as soon as it gets run.
It appeared that the special cases were getting run, but the default was getting run every time. Why?
I suppose I needed to add "break" statements to my special cases in the "switch" statement. I looked up confirmation for this in the text and this is what it said:
"Java is defined so that if the break statement is missing, the program starts executing statements from the next clause after it finishes the selected one. While this design can be useful in some cases, it tends to cause more problems than it solves. To reinforce the importance of remembering to include the break statement, every case clause in this text ends with an explicit break statement (or sometimes with a return statement, as discussed in Chapter 5)."
That kind of makes sense... depending on what the syntax means when it checks for "default," it's posible that "default" means every single case *in addition to* the special cases that do apply. So yes, once I did add a "break" statement to each case, the problem worked correctly.
It's weird though because one example in the book does a simple switch statement with a default, but does not have a break statement after the special cases:
private boolean isEnglishVowel(char ch) { switch (Character.toLowerCase(ch)) { case 'a': case 'e': case 'i': case 'o': case 'u': return true; default: return false; } }I can confirm that this private method works correctly since I used it in my own program. So I'm not quite sure when a break statement is required. I do realize that the author may not have meant to include a confusing example in the final version of the book, so I'll check this PDF against my hard copy when I'm back home. Any ideas on this switch statement conundrum however?