I'm writing something similar to smarterChild (that automated AOL Instant Messenger chat program from back in the day) and was curious about my method of determining sentence structure and response.
Currently, the bare bones design is to use 5 main methods which collectively determine the appropriate response given the user input:
1) Read(): initially accepts user input, then calls first of many methods: sentenceType()
2) getSentenceType(): User sentence type: Question, statement, directive, suggestion, etc…
3) getWho(): If question is about the computer, or about someone else…
4) getCategory(): What category the question is about (weather, sports…)
5) getResponse(): finally, form a response
These methods will query a database to determine if the user input contains key words...
Example of getSentenceType():
public String getSentenceType(String sentence) {
String type = null;
for (String s : db.getQuestions()) {
if (sentence.contains(s)) {
type = "question";
break;
}
else {
type = "statement";
}
}
return getWho(type, sentence);
}
Example of the final method which returns sentence structure:
//final call...
public String getResponse(String cat, String who, String type) {
String response = new String();
String auxVerb, subject, mainVerb, noun;
auxVerb = "do";
subject = who;
mainVerb = "like";
noun = cat;
//question structure: auxiliary verb + subject + main verb + noun/pronoun
// DO YOU LIKE MARY?
if (type.equals("question")) {
response = subject + " " + auxVerb + " " + mainVerb + " " + noun + ". ";
}
else {
response = auxVerb + " " + subject + " " + mainVerb + " " + noun + "?";
}
return response;
}
Sample database methods:
public String[] getQuestions() {
String[] questions = new String[] {"why", "?"};
return questions;
}
public String[] getWeather() {
String[] weather = new String[] {"cold", "hot", "rainy", "weather"};
return weather;
}
It will then progressively concatenate all the method results into a coherent response… then send that result back to Read(), which will print out the result to the user...
Is this an inefficient way to go about this? I know that if I continue down this path... to make a robust system, it will take tons of if else checks and a massive database to determine every possible type of response for user input.
Are there any suggested methods?
Thank you
Related
I am working on a program, where I want users to define a simple functions like
randomInt(0,10)
or
randomString(10)
instead of static arguments. What is the best way to parse and process such functions ?
I have not found any examples of such problem, the parser does not have to be ultra-efficient, it will not be called often, but mainly I want to focus on good code readability and scalability.
Example of user input:
"This is user randomString(5) and he is randomInt(18,60) years old!"
Expected output(s):
"This is user phiob and he is 45 years old!"
"This is user sdfrt and he is 30 years old!"
One option is to use Spring SPEL. But it forces you to change the expression a little and use Spring library:
The expression can look like this:
'This is user ' + randomString(5) + ' and he is ' + randomInt(18,60) + ' years old!'
or this:
This is user #{randomString(5)} and he is #{randomInt(18,60)} years old!
or you can implement your own by having a custom TemplateParserContext.
And here is the code:
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class SomeTest {
#Test
public void test() {
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(
"This is user #{randomString(5)} and he is #{randomInt(18,60)} years old!",
new TemplateParserContext() );
//alternative
//Expression exp = parser.parseExpression(
// "'This is user ' + randomString(5) + ' and he is ' + randomInt(18,60) + ' years old!'");
// String message = (String) exp.getValue( new StandardEvaluationContext(this) );
String message = (String) exp.getValue( new StandardEvaluationContext(this) );
}
public String randomString(int i) {
return "rs-" + i;
}
public String randomInt(int i, int j) {
return "ri-" + i + ":" + "j";
}
}
Whatever object you pass to StandardEvaluationContext should have those methods. I put them in the same class that also runs the expression.
You could use something such as the following:
Warning, I haven't tested it. Just something to get started with
public String parseInput(String input){
String[] inputArray = input.split(" ");
String output = "";
for(String in : inputArray){ //run through each word of the user input
if(in.contains("randomString(")){ //if the user is calling randomString
String params = in.replace("randomString(", ""); //strip away function to get to params
params = in.replace("(", ""); //strip away function to get to params
String[] paramsArray = params.split(","); //these are string integers, and could be converted
//send off these split apart parameters to your randomString method
String out = randomString(paramsArray); //method parses string integers, outputs string
output += out + " ";
}else if(in.contains("randomInt(")){ //if the user is calling randomInt
String params = in.replace("randomInt(", ""); //strip away function to get to params
params = in.replace("(", ""); //strip away function to get to params
String[] paramsArray = params.split(","); //these are string integers, and could be converted
//send off these split apart parameters to your randomInt method
String out = randomInt(paramsArray); //method parses string integers, outputs string
output += out + " ";
}else{ //if the user is just entering text
output += in + " "; //concat the output with what the user wrote plus a space
}
}
return output;
}
I am trying to read the data from CSV file. everything works fine but when i try to read the data for longitude and latitude. it gives me an below error message.
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.yogi.guestlogix/com.example.yogi.guestlogix.MainActivity}: java.lang.NumberFormatException: Invalid double: "EVE"
Caused by: java.lang.NumberFormatException: Invalid double: "EVE"**
MainActivity.java
//Airports data begin
InputStream isss = getResources().openRawResource(R.raw.airports);
BufferedReader readerss = new BufferedReader(
new InputStreamReader(isss, Charset.forName("UTF-8"))
);
String liness = "";
//for airports.csv
try {
readerss.readLine();
while ((liness = readerss.readLine()) != null) {
//split by ',' first
String[] airport = liness.split(",");
//Read the data
AirportsData airdata = new AirportsData();
airdata.setAirportname(airport[0]);
airdata.setAirportcity(airport[1]);
airdata.setAirportcountry(airport[2]);
airdata.setAirportIATAcode(airport[3]);
//airdata.setAirportlang(Double.parseDouble(airport[4]));
//airdata.setAirportlat(Double.parseDouble(airport[5]));
AirportsDatas.add(airdata);
}
} catch (IOException e) {
Log.wtf("My Activity", "Reading data file error " + liness, e);
e.printStackTrace();
}
Log.d("Airline", "name is " + AirportsDatas);
AirportsData.java
public double getAirportlang(double airportlang) {
return airportlang;
}
public void setAirportlang(double airportlang) {
this.airportlang = airportlang;
}
public double getAirportlat( ) {
return airportlat;
}
public void setAirportlat(double airportlat) {
this.airportlat = airportlat;
}
#Override
public String toString() {
return "AirportsData{" +
"airportname='" + airportname + '\'' +
", airportcity='" + airportcity + '\'' +
", airportcountry='" + airportcountry + '\'' +
", airportIATAcode='" + airportIATAcode + '\'' +
", airportlang=" + airportlang +
", airportlat=" + airportlat +
'}';
}
**Any solution for this problem would be greatly appreciated....
The problem with CSV files is, that users (and other programmers) can pretty much write into the file what they want.
In your case, somebody entered a non-number into a CSV cell where you were expecting a double floating point number. Java correctly complains that this cannot be parsed to double. You have two approaches, to treat such situations:
A) Design by Contract
First, you could resolve that by relying on design by contract. You could have a parseLat method:
public Double parseLat(String[] csvRow) {
final String lat= csvRow[4];
assertIsNumeric(lat);
}
private void assertIsNumeric(String lat) {
if(!isNumeric(lat)) {
throw new IllegalArgumentException("Lattitude '" + lat + "' is not numeric");
}
}
There are countless options to implement an isNumeric method, some are discussed here. This will still stop the execution, but it will give a clearer message to users.
B) Defensive Design
A second option, is to provide a parseLAttitude method, that replaces non numeric value with null:
public Double parseLattitude(String[] csvRow) {
final String lattitude = csvRow[4];
if(!isNumeric(lattitude)) {
System.out.println("Could not parse lattitude '" + lat + "'");
return null;
}
return Double.parseDouble(lat);
}
I think the problem is that airport[4] & airport[5] might not be the lon and lat of the airport. If these field contain alpha characters [a-z] you will get an numberFormatException. Can you add the the output when you print airport like:
System.out.println(Arrays.asList(airport));
to the question? Than we will be better able to help you
Right now I am making a small program which should create an email adress and username out of the users actual name. For example, Peter Anderson types his first and last name in two separate text fields, and the program should then return a username and an email adress in two separate textfields, once you press the save button. For example, Peter Anderson gets the username "a13petand" and the email adress "a13petand#test.com" a = autumn, 13 = 2013. It should only take the first 3 letters from first & last name. It should then append the first name, last name, username and email adress to the text area. This is how my code currently looks like;
package test5;
import javax.swing.JOptionPane;
public class Test5 extends javax.swing.JFrame {
String[][] Users = new String[20][4];
int counter;
public Test5() {
initComponents();
}
private void savebtnActionPerformed(java.awt.event.ActionEvent evt) {
if (counter < Users.length) {
Users[counter][0] = Firstnametf.getText();
Users[counter][1] = Lastnametf.getText();
Users[counter][2] = Usernametf.getText();
Users[counter][3] = Emailtf.getText();
jTextArea1.append(Users[counter][0] + ", " + Users[counter][1] + ", " + Users[counter][2] + ", " + Users[counter][3] + "\n");
counter++;
} else {
JOptionPane.showMessageDialog(null, "The array is full!");
counter = Users.length;
}
}
How should I continue from here? How do I make it generate "a13" and then take the first 3 letters in the first and last name? That is my main problem. All I know is that I should use the String class method substring to pick the first 3 letters out of first & last name. And then use the Calendar class to get the correct year. But I don't know how to make it work with my current code, which is the problem.
Date date = Calendar.getInstance().getTime();
String result = "";
result += new SimpleDateFormat("MMM").format(date).substring(0,1).toLowerCase();
result += new SimpleDateFormat("yy").format(date);
result += Firstnametf.getText().subString(0,3);
result += Lastnametf.getText().subString(0,3);
you should use
firstName = Firstnametf.getText().subString(0,3);
lastName = Lastnametf.getText().subString(0,3);
currentYear = Calendar.getInstance().get(Calendar.YEAR);
voila = firstName.concat(lastName).concat(currentYear);
or
voila = firstName + lastName + currentYear.toString ;
I have a number of xml's that come in haphazardly that contain a Ocount, and Lnumber, as well as other data. I have created a class to get that data.
My problem is that how can I group xml's that have the same Lnumber(string), until it reaches the Ocount(int). (the xmls that have the same lnumber has the same Ocount). And eventually send out a email telling with xmls has been processed.
String readLine = FileHandler.checkListFile(sh.getShipmentHeader().getBillToCustomer());
if (!readLine.isEmpty())
{
int orderCount = 0;
int index = readLine.indexOf(";") + 1;
String customerName = readLine.substring(index, readLine.indexOf(";", index)).trim();
index = readLine.indexOf(";", index) + 1;
String to = readLine.substring(index, readLine.length()).trim();
if (!billMap.containsKey(sh.getShipmentHeader().getBillToCustomer()))
{
billMap.put(sh.getShipmentHeader().getBillToCustomer(), 1);
orderCount = 1;
}
else
{
billMap.put(sh.getShipmentHeader().getBillToCustomer(), ((int) billMap.get(sh.getShipmentHeader().getBillToCustomer())) + 1);
orderCount = (int) billMap.get(sh.getShipmentHeader().getBillToCustomer());
}
outboundMessage += sh.getShipmentHeader().getOrderNumber() + li ;
logger.info("On-Demand Outbound Export Info: " + orderCount + " processed out of " + sh.getShipmentHeader().getOrderCount() +
" for " + customerName);
if (orderCount == sh.getShipmentHeader().getOrderCount())
{
Email email = new Email();
billMap.remove(sh.getShipmentHeader().getBillToCustomer());
outboundMessage += li + "Total of #"+ sh.getShipmentHeader().getOrderCount() + " orders processed for "+ customerName + li ;
logger.info("On-Demand Email sent for " + customerName);
System.out.println(outboundMessage);
email.outboundEmail("TEST: Orders for " + customerName + " complete", outboundMessage, to);
outboundMessage = "";
email = null;
}}
I been working on this for days, where am I going wrong.
It seems like you are having difficulty obtaining information from xmls. I suggest using XStream [1]. It is capable of serialising objects to xml and back. By using XStream, you can get an Object from the xml and compare variables (Lnumber and Ocount) easily.
If you insist using this code, I suggest adding comments to notify us what you are doing, but if want an easier alternative to work with xml files using java, I highly suggest using XStream as a solution.
[1] http://x-stream.github.io/
boldHighlight method takes text string and highlights in it q keywords via <b></b> tags
colorHighlight method takes text string and highlights int q keywords via <b style='background-color: #color'></b> with 12 alternating colors
String text = "The use of hello as a telephone greeting has been credited to Thomas
Edison; according to one source, he expressed his surprise with a
misheard Hullo. Alexander Graham Bell initially used Ahoy (as used on
ships) as a telephone greeting"
String keywords = "HELLO Surprise"
boldHighlight(text, keywords); // will produce:
The use of <b>hello</b> as a telephone greeting has been credited to Thomas Edison; according to one source, he expressed his <b>surprise</b> with a misheard Hullo. Alexander Graham Bell initially used Ahoy (as used on ships) as a telephone greeting`
colorHighlight(text, keywords); // will produce:
The use of <b style='background-color:#ffff66'>hello</b> as a telephone greeting has been credited to Thomas Edison;>according to one source, he expressed his <b style='background-color:#a0ffff'>surprise</b> with a misheard Hullo. Alexander Graham Bell initially used Ahoy (as used on ships) as a telephone greeting
The question:
is there something I could use like third party library that would do similar job as bellow methods? Or if you look at the code, is there something that can be improved, to make the performance better and/or make it more elegant?`
private static final String[] colors = new String[]{"ffff66", "a0ffff", "99ff99", "ff9999", "ff66ff", "880000", "00aa00", "886800", "004699", "990099", "ffff66", "a0ffff"};
public static String safeCharWithSpace(String input) {
input = input.trim();
return Normalizer.normalize(input.toLowerCase(), Normalizer.Form.NFD)
.replaceAll("\\p{InCombiningDiacriticalMarks}+", "")
.replaceAll("[^\\p{Alnum}]+", " ");
}
private static String prepQuery(String q) {
try {
log.debug("qr encoded: " + q);
q = URLDecoder.decode(q, "UTF-8");
} catch (UnsupportedEncodingException ignore) {
}
log.debug("qr decoded: " + q);
return removeIgnoreCase(q, stopWords);
}
public static String boldHighlight(String text, String q) {
return highlight(text, q, false);
}
public static String colorHighlight(String text, String q) {
return highlight(text, q, true);
}
private static String replaceWord(String text, String keyword, int colorNumber, boolean useColor) {
String color = "";
keyword = safeCharWithSpace(keyword);
if (StringUtils.isNotEmpty(keyword) && !StringUtils.isWhitespace(keyword)) {
if (useColor) color = " style='background-color: " + colors[colorNumber] + "'";
return text.replaceAll("(?i)(" + keyword + ")(?!([^<]+)?>>)", "<b" + color + ">$1</b>");
} else
return text;
}
public static String highlight(String text, String q, boolean useColor) {
String qr = prepQuery(q);
String rtn = null;
int i = 0;
if (qr.startsWith("\"")) {
String keywords = StringUtils.remove(qr, "\"");
rtn = replaceWord(text, keywords, 0, useColor);
} else {
String[] keywords = qr.split("\\s");
for (String keyword : keywords) {
rtn = replaceWord(text, keyword, i, useColor);
if (useColor) {
if (i < 11) i++;
else i = 0;
}
}
}
return rtn;
}
for removal of stop words removeIgnoreCase() in prepQuery() method refer to my other post: Removing strings from another string in java
Wow, well you could go about it a few different ways.
You could call a static method.
i.e.
${statics["java.lang.System"].currentTimeMillis()}
The MVC thing to do would be to do this processing before the template is processed, but I know your just maintaining the code.
It looks like it is just doing several replace all's, so a change to a Java method should work. I have to suggesst you look at the escaping tools Freemarker has.
Freemarker really has great documentation, and built ins cover many situations.