Reputation: 41
Back with another question from the same project! The project is for a first semester introduction to programming course (Java) and for the project we have to calculate the FRI for some example texts using only what we have learned up to this point.
My question is: How does someone compute syllables using a for loop. So far, I have it set up to go through each letter, check for vowels, stop when it sees a vowel and then check the next letter for another vowel or another character.
It works if I'm counting syllables in words with vowels and follow my first two rules.
However, I don't know how to count a single word without any vowels in it as a single syllable.
Here are the rules for counting syllables we have to follow:
- The number of syllables contained in a word is based on the number of occurrences of vowels that is, the characters a, e, i, o, u with the following modifications:
a. Each group of adjacent vowels counts as one syllable. For example, “real, air, sound, tooth, soap” are one-syllable words, the word “regal” has two syllables, “continuous” has three and “queueing” just one. The role of ‘y’ is ambiguous. It can be both a consonant (“year”, “royal”, “yes”) and a vowel (“by”, “ugly”, “many”). We ignore the ‘y’ ambiguity and your program must take it for a consonant.
b. An ‘-e’ or ‘-ed’ at the end of a word does not count as a syllable For example, the word, "validate" has four vowels but only three syllables. There are only few words where rule b eliminates actual syllables (for instance ‘-ed’ after ‘t’ or ‘d’ is not silent), such omissions do not significantly distort the test results. You should also notice that words having double ‘e’ at the end do not lose a syllable for their count. For instance, “committee” has three syllables counted. The rule may also increase the counting, for instance carefree has three syllables by the rule, but it sounds as having two. There is an important case when b is overruled as explained in the next section c.
c. Each word has at least one syllable Even if a word does not contain any vowel, or the previous rules give a count of zero, it is still counted as having one syllable. For example, the words str, grrrr , pp, the, she, he, fed, led” all have one syllable
And here is the code I have so far:
// While loop to count words and increment syllable count per word
while(countWords.hasNext()){
String word = countWords.useDelimiter(WORD_DELIMITERS).next();
if(word.compareTo("") != 0){
numWords++;
for(k=0; k < word.length(); k++){
char letter = word.charAt(k);
if(letter == 'A' || letter == 'a'
|| letter == 'I' || letter == 'i'
|| letter == 'O' || letter == 'o'
|| letter == 'U' || letter == 'u'){
if(k+1 < word.length()){
char nextLetter = word.charAt(k+1);
if(nextLetter == 'A' || nextLetter == 'a'
|| nextLetter == 'E' || nextLetter == 'e'
|| nextLetter == 'I' || nextLetter == 'i'
|| nextLetter == 'O' || nextLetter == 'o'
|| nextLetter == 'U' || nextLetter =='u'
|| nextLetter == ' '){
numSyllables++;
}
else{
numSyllables++;
}
}
}
else if(letter == 'E' || letter == 'e'){
if(k+1 < word.length()){
char nextLetter = word.charAt(k+1);
if(nextLetter != 'D' || nextLetter != 'd'
|| nextLetter != 'A' || nextLetter != 'a'
|| nextLetter != 'I' || nextLetter != 'i'
|| nextLetter != 'O' || nextLetter != 'o'
|| nextLetter != 'U' || nextLetter !='u'
|| nextLetter == ' '){
numSyllables++;
}
}
}
}
if(numSyllables == 0){
numSyllables = 1;
}
System.out.println((numWords) + " " + word + " " + numSyllables);
numSyllables = 0;
}
}
Example printout:
1 Hello 1 2 world 1 3 This 1 4 is 1 5 just 1 6 a 1 7 test 1 8 to 1 9 see 1 10 how 1 11 my 1 12 program 2 13 works 1 14 up 1 15 to 1 16 this 1 17 point 2 18 I'm 1 19 pretty 1 20 sure 1 21 I've 1 22 figured 3 23 out 2 24 the 1 25 syllable 1 26 thing 1 27 but 1 28 I'm 1 29 not 1 30 sure 1
Upvotes: 4
Views: 5013
Reputation: 3412
Here are few comments on the code you posted:
String equality vs String sorting
word.compareTo("") != 0
is a bit strange. compareTo
is usually done for alphabetical purpose. Maybe you wanted to use !word.equals("")
redundant if...else...
if(nextLetter == 'A' || nextLetter == 'a'
|| nextLetter == 'E' || nextLetter == 'e'
|| nextLetter == 'I' || nextLetter == 'i'
|| nextLetter == 'O' || nextLetter == 'o'
|| nextLetter == 'U' || nextLetter =='u'
|| nextLetter == ' '){
numSyllables++;
}
else{
numSyllables++;
}
So if the condition is true
or false
, you'll end up with numSyllables++
so there is no point to have the condition ... Moreover, nextLetter == ' '
may never happen if you only have space-free words
Final "a", "i", "u", "o" are ignored:
if (letter == 'A' ...){
if(k+1 < word.length()){
...
}
}
It means if k == word.length() - 1
, you do nothing. In the word Vienna
, you'll ignore the last "a". You did the same thing for the letter "e" which is correct as last "e" has to be ignored
Tests never happens + final "ed"/"eD" not ignored
if(nextLetter != 'D' || nextLetter != 'd'
|| nextLetter != 'A' || nextLetter != 'a'
|| nextLetter != 'I' || nextLetter != 'i'
|| nextLetter != 'O' || nextLetter != 'o'
|| nextLetter != 'U' || nextLetter !='u'
|| nextLetter == ' '){
numSyllables++;
}
if nextLetter
is D or d
: it is D
, then nextLetter != 'd'
is true, otherwise, nextLetter != 'D'
would have already been true
so the next tests will never happen. Plus, you do not ignored final "ed" here because if it is eD
, nextLetter != 'd'
is true, and if it is ed
then nextLetter != 'D'
is true
You don't seem to skip consecutive vowel
My suggestion is to have two dedicated methods, for readibility purpose:
The method to check is a letter is a vowel or not is obviously case-insensitive:
private static boolean isVowel(char letter) {
return letter == 'A' || letter == 'a'
|| letter == 'E' || letter == 'e'
|| letter == 'O' || letter == 'o'
|| letter == 'I' || letter == 'i'
|| letter == 'U' || letter == 'u';
}
Then the method to count the number of syllable given a word has some modification compared to yours:
I'll first check if a letter is a vowel or not:
2.1 It is not a vowel: syllable count does not increase and the pointer moves forward
2.2 The vowel is not an "E": syllable count increase by 1 and the pointer moves until the next non-vowel letter
2.3 The vowel is an E: check if the "e" is the last letter or if it is the one before last followed by a "d": in this case, follow the rule 2.1, otherwise, follow the rule 2.2
Math.max(count, 1)
, I'm ensuring that at least 1
is returnedThe code is:
private static int countWithLoop(String word) {
// start counting
int syllableCount = 0;
// use a while loop
int index = 0;
while (index < word.length()) {
char letter = word.charAt(index);
// if vowel:
if (isVowel(letter)) {
// specific case of "E"/"e"
if (letter == 'E' || letter == 'e') {
// 1. last "e" is ignored:
if (index == word.length() - 1) {
index++;
}
// 2. if "ed" finished the word, it is ignored
else if (index == word.length() - 2
&& (word.charAt(word.length() - 1) == 'd' || word.charAt(word.length() - 1) == 'D')) {
index++;
}
// 3. this is neither a last "e" or last "ed". Count as a syllable
else {
// count one more syllable...
syllableCount++;
// ...and skip consecutive vowel
while (isVowel(letter) && index < word.length() - 1) {
index++;
letter = word.charAt(index);
}
}
} else {
// count one more syllable...
syllableCount++;
// ...and skip consecutive vowel
while (isVowel(letter) && index < word.length() - 1) {
index++;
letter = word.charAt(index);
}
}
}
// otherwise, keep going
else {
index++;
}
}
// return
return Math.max(1, syllableCount);
}
Another solution, a bit more complex but also a bit funnier, is to use Regular Express, known as Regex. It basically requires a pattern and the pattern will be tested against a String
. If the pattern is found, the matching characters are returned and the pattern will be tested against the remaining characters until nothing is found. This will use Pattern
and Matcher
classes
The code looks like
private static int countWithRegex(String word) {
String i = "(?i)[aiou][aeiou]*|e[aeiou]*(?!d?\\b)";
Matcher m = Pattern.compile(i).matcher(word);
int count = 0;
while (m.find()) {
count++;
}
// return at least 1
return Math.max(count, 1);
}
Some explanation about i
:
(?i)[aiou][aeiou]*|e[aeiou]*(?!d?\\b) = complete regex
(?i) = case insensitivity: A=a, B=b and so on
[aiou] = look for letter "a", "i", "o", "u", and their
capital letter counterpart
* = look for the characters between the bracket
with zero, one or more occurences
[aiou][aeiou]* = look for non-E vowel followed a zero, one
or more vowel. Like "a", or "Oa", or "ueu"
| = Or ...
e = simple look for letter "e" or "E"
[aeiou]* = same as above: look for "e" followed by
consecutive vowels
(?!.....) = but exclude "e"/"E" when followed by...
d?\\b = ...an optional "d"/"D" letter and \\b refer
to the end of the String: it excludes final
"e" and final "ed"
And here is the main:
public static void main(String... aArgs) {
List<String> words = new ArrayList<>();
words.add("real");
words.add("air");
words.add("sound");
words.add("tooth");
words.add("soap");
words.add("regal");
words.add("continuous");
words.add("queueing");
words.add("committee");
words.add("carefree");
words.add("grrrr");
words.add("she");
words.add("fed");
// Challenge the "ed" but not at the end of the word
words.add("Medecine");
// Challenge a final "e"
words.add("Stone");
// Challenge a final "ed"
words.add("Stoned");
// Challenge a double vowel
words.add("Year");
// Challenge case sensitivity
words.add("BoAr");
// Expected answer depends on the number of drink Andy has taken
words.add("Vacuum");
System.out.println("----Counting with Loop----");
for (int i = 0; i < words.size(); i++) {
System.out.println(i + " " + words.get(i) + " " + countWithLoop(words.get(i)));
}
System.out.println("----Counting with Regex----");
for (int i = 0; i < words.size(); i++) {
System.out.println(i + " " + words.get(i) + " " + countWithRegex(words.get(i)));
}
}
With the followed output:
----Counting with Loop----
0 real 1
1 air 1
2 sound 1
3 tooth 1
4 soap 1
5 regal 2
6 continuous 3
7 queueing 1
8 committee 3
9 carefree 3
10 grrrr 1
11 she 1
12 fed 1
13 Medecine 3
14 Stone 1
15 Stoned 1
16 Year 1
17 BoAr 1
18 Vacuum 2
----Counting with Regex----
0 real 1
1 air 1
2 sound 1
3 tooth 1
4 soap 1
5 regal 2
6 continuous 3
7 queueing 1
8 committee 3
9 carefree 3
10 grrrr 1
11 she 1
12 fed 1
13 Medecine 3
14 Stone 1
15 Stoned 1
16 Year 1
17 BoAr 1
18 Vacuum 2
Upvotes: 4