I wrote this program for an assignment, thus I had some requirements as far as the format of the cfg.txt file and some basic other classes that we had to use. Other than that, I am curious if there is a way to drastically simplify this code. Although the program runs fine and produces the desired output, is there a better way to go about writing this? (What initially spurred my concern was the iterateData
method. I did not like the code repetition and thought it may be convenient to "reuse" some of it. However, in doing so, I then introduced numerous class variables to prevent from having to pass in 7 instance variables to the method. I can't say that I'm all too thrilled about this...) Regardless, advice and suggestions appreciated.
Thank you.
import java.util.Scanner;
import java.io.*;
import java.util.Random;
public class ContextFreeGrammar {
private static String nt, useExp = "";
private static int totalWeight = 0, randomNum;
private static ArrayUnsortedList<Production> data = new ArrayUnsortedList<Production>();
private static Production pObj;
public static void main(String[] args) throws IOException {
String result = "<S>";
File myFile = new File("cfg.txt");
Scanner fileScan = new Scanner(myFile);
// Read and process each line of the file
while (fileScan.hasNext()) {
Production p = new Production(fileScan.nextLine());
data.add(p);
}
// If the result string still contains non-terminals
while (result.contains("<")) {
// Get the non-terminal
nt = result.substring(result.indexOf('<')+1, result.indexOf('>'));
// Cannot condense because we have an ambiguous
// totalWeight. Hence, we need to iterate twice:
// 1. To generate appropriate random number from totalWeight
// 2. To determine when the totalWeight exceeds that random number.
iterateData(false);
Random rand = new Random(); // Generate a random number object
randomNum = rand.nextInt(totalWeight - 1) + 1; // Generate actual random number
// Resets
totalWeight = 0;
data.reset();
iterateData(true);
// Replace the non-terminal with the expression to use
result = result.replaceFirst("<" + nt + ">", useExp);
totalWeight = 0; // Reset totalWeight;
}
System.out.println(result);
}
public static void iterateData(boolean GETEXP) {
// Iterate through list again
for (int i = 0; i < data.size(); i++) {
// Get the current value
pObj = data.getNext();
// Keep track of weight
if (nt.equalsIgnoreCase(pObj.getNonTerminal())) {
totalWeight += pObj.getWeight();
if (GETEXP) {
if (totalWeight > randomNum) {
useExp = pObj.getExpression(); // This is expression to use
break;
}
}
}
}
}
}
My cfg.txt
file is as such:
80 <S> = My homework is late because <reason>.
20 <S> = I want to <action> instead of writing this program.
40 <reason> = <who> ate it
40 <reason> = I set it on fire because I was cold
20 <reason> = I didn't feel like doing it
30 <who> = Deep Shah
70 <who> = my pet <animal>
10 <animal> = dog
20 <animal> = narwhal
50 <animal> = python
20 <animal> = velociraptor
30 <action> = go on a vacation
30 <action> = indulge in a giant feast
40 <action> = frolic in the snow