I have created the following method which returns a Triple of strings. However, I don't like the way I've written it because I think I've put in too many Npe checks making it unreadable.
private Triplet<String, String, String> getInfoFromTable(Person person) {
StringBuilder idWithText = new StringBuilder();
String idText;
Date time = null;
Level level;
Exercise exerciseRecord = getExercise(person);
if (exerciseRecord != null && exerciseRecord.getId() != null) {
if(exerciseRecord.getLevel1() != null && exerciseRecord.getLevel2() != null){
level = new Level(exerciseRecord.getLevel1(), exerciseRecord.getLevel2());
} else {
level = new Level("1", "1");
}
idText = getIdText(level, exerciseRecord.getId());
if(!Strings.isNullOrEmpty(idText)) {
idWithText = idWithText.append(exerciseRecord.getId()).append(" " + idText);
}
if (exerciseRecord.getTime() != null) {
time = exerciseRecord.getTime().toDate();
}
return new Triplet<>(idWithText.toString(), "1", formatTime(time));
}
return new Triplet<>("", "", "");
}
Ηow can I make the above code look simpler? I've seen a little use of Optional but I don't know if it's good to use them in my case. Could someone help with the method refactor?
You need to split the huge method into several simple, it will decrease complexity.
private Triplet<String, String, String> getInfoFromTable(Person person) {
Exercise exerciseRecord = getExercise(person);
if (exerciseRecord != null && exerciseRecord.getId() != null) {
return new Triplet<>(getIdWithText(exerciseRecord, getLevel(exerciseRecord)), "1", formatTime(exerciseRecord.getTime()));
}
return new Triplet<>("", "", "");
}
private String formatTime(LocalTime time) {
if (time == null) {
return "";
}
return formatTime(time.toDate());
}
private Level getLevel(Exercise exerciseRecord) {
Level level;
if(exerciseRecord.getLevel1() != null && exerciseRecord.getLevel2() != null){
level = new Level(exerciseRecord.getLevel1(), exerciseRecord.getLevel2());
} else {
level = new Level("1", "1");
}
return level;
}
private String getIdWithText(Exercise exerciseRecord, Level level) {
String idWithText = "";
String idText = getIdText(level, exerciseRecord.getId());
if(!Strings.isNullOrEmpty(idText)) {
idWithText = String.format("%s %s", exerciseRecord.getId(), idText);
}
return idWithText;
}
Given an arbitrary number of TextField inputs (t1, t2, t3, ...) and a custom boolean string input from a JtextArea, I need to check if lines in a file match the custom boolean expression. It needs to support nested parentheses.
Example:
User enters "str1" into t1 , "str2" into t2, "str3" into t3, "str4" into t4, "str5" into t5.
User enters the following into the JTextArea for the custom boolean:
"not ((t1 and not t3) or (t4 and t2)) or t5"
Then based on these inputs, I must filter a file and return lines in the file that match the custom boolean based on a "contains" relationship (e.g. "t1 and not t3" means a line must contain the string t1 and not contain the string t3).
For example a file with the following two lines:
str 5
str4 str2
The filter would only return str5 because it is the only line that matches the custom boolean.
I am having trouble even getting started. I have tried to think of a recursive solution but couldn't come up with anything. Also I tried non-recursive solutions but can't come up with anything either.
There is also the problem of the end result boolean needing to take in a parameter (each line in the file). I thought of maybe producing a sequence of operations to perform rather than a boolean that somehow takes in a parameter. But I can't figure out how to get this sequence in the first place.
Here is what I have now. It is very bad and I am thinking of scrapping this approach.
public class CustomInputParser {
private ArrayList<String> pairs;
private String inp;
private HashMap<Integer,String> atomMap;
public CustomInputParser() {
this.pairs = null;
this.inp = "";
this.atomMap = new HashMap<Integer,String>();
}
public void findAtoms() {
int i = 0;
for(String s : this.pairs) {
String[] indices = s.split(",");
int begin = Integer.valueOf(indices[0]);
int end = Integer.valueOf(indices[1]);
if(!inp.substring(begin+1, end).contains("(")) {
this.pairs.set(i, this.pairs.get(i) + ",#");
}
i++;
}
}
public void computeAtoms() {
int i = 0;
for(String s : this.pairs) {
if(s.contains(",#")) {
String[] indices = s.split(",");
int begin = Integer.valueOf(indices[0]);
int end = Integer.valueOf(indices[1]);
//this.pairs.set(i,this.pairs.get(i).replace(",a", ""));
this.pairs.set(i, this.pairs.get(i) + ","+inp.substring(begin+1, end));
this.atomMap.put(begin,this.pairs.get(i).split(",")[3]+"#"+String.valueOf(end));
}
i++;
}
System.out.println(this.pairs.toString());
System.out.println(this.atomMap.toString());
}
public void replaceAtoms() {
int i = 0;
for(String s : this.pairs) {
if(!(s.contains("o") || s.contains("a") || s.contains("n"))) {
String[] indices = s.split(",");
int begin = Integer.valueOf(indices[0])+1;
int end = Integer.valueOf(indices[1]);
for(int j = begin; j < end; j++) {
if(inp.charAt(j) == '(') {
if(atomMap.containsKey(j)) {
this.pairs.set(i, this.pairs.get(i) + ","+j+"#"+atomMap.get(j).split("#")[1]+">"+atomMap.get(j).split("#")[0]);
}
else {
this.pairs.set(i,"!"+ this.pairs.get(i));
}
}
}
}
i++;
}
System.out.println(this.pairs.toString());
}
public ArrayList<String> getPairs(String str){
this.inp = str;
ArrayList<String> res = new ArrayList<String>();
char[] arr = str.toCharArray();
Stack<Integer> s = new Stack<Integer>();
for(int i = 0; i < arr.length; i++) {
if(arr[i] == '(') {
s.push(i);
}
if(arr[i] == ')') {
if(s.empty()) {
return null;
}
else {
Integer start = s.pop();
Integer end = Integer.valueOf(i);
res.add(start.toString() + "," + end.toString());
}
}
}
if(!s.empty()) {
return null;
}
this.pairs = res;
return res;
}
public static void main(String[] args) {
String x = "((not t1 and ((not t2 or t4) or (t3 or t4))) or (t5 and not t6)) and t7";
x = x.replace("not", "n").replace("and","a").replace("or", "o").replace("t", "").replace(" ", "");
System.out.println(x);
CustomInputParser c = new CustomInputParser();
System.out.println(c.getPairs(x).toString());
c.findAtoms();
c.computeAtoms();
c.replaceAtoms();
}
}
The first step is to tokenize the input. Define
enum Token {VAR, LP, RP, NOT, AND, OR, END}
LP and RP are parentheses. Now define a tokenizer class that looks something like this:
class Tokenizer {
Tokenizer(String input) {...}
void reset() {...}
Token getNext() {...}
String getVarName() {...}
}
Calling getNext() on your example in a loop should return
LP LP NOT VAR AND LP LP NOT VAR OR VAR RP OR LP VAR OR VAR RP RP RP OR LP VAR AND NOT VAR RP RP AND VAR END
Calling getVarName() immediately after a VAR has been returned by getNext() gives you the name of the variable (e.g. "t42").
There are many ways to implement little scanners like this. You should do this first and make sure it's bulletproof by testing. Trying to build a parser on top of a flaky scanner is torture.
As I said in comments, I'd consider recursive descent parsing. If you have a suitable grammar, writing an RD parser is a very short step as the Dragon Book (also mentioned above) shows.
A reasonable grammar (using tokens as above) is
Expr -> Term AND Term
| Term OR Term
| Term END
Term -> NOT Term
| Opnd
Opnd -> VAR
| LP Expr RP
For example, here is how you'd get started. It shows the first rule converted to a function:
class Evaluator {
final Tokenizer tokenizer = ...; // Contains the expression text.
final Map<String, Boolean> env = ... // Environment: variables to values.
Token lookAhead; // Holds the token we're parsing right now.
Evaluator(Tokenizer tokenizer, Map<String, Boolean> env) { ... }
void advance() {
lookAhead = tokenizer.getNext();
}
boolean expr() {
boolean leftHandSide = term(); // Parse the left hand side recursively.
Token op = lookAhead; // Remember the operator.
if (op == Token.END) return leftHandSide; // Oops. That's all.
advance(); // Skip past the operator.
boolean rightHandSide = term(); // Parse the right hand side recursively.
if (op == Token.AND) return leftHandSide && rightHandSide; // Evaluate!
if (op == Token.OR) return leftHandSide || rightHandSide;
dieWithSyntaxError("Expected op, found " + op);
}
boolean term() {...}
boolean opnd() {...}
}
The environment is used when a VAR is parsed. Its boolean value is env.get(tokenizer.getVarName()).
So to process the file, you'll
For each line
For each variable tX in the expression
See if the line contains the string tX is bound to in its text field.
If so, put the mapping tX -> true in the environment
else put tX -> false
Reset the tokenizer
Call Evaluator.evaluate(tokenizer, environment)
If it returns true, print the line, else skip it.
This is the simplest approach I can think of. About 150 lines. Many optimizations are possible.
Added
Well since I can no longer take away the thrill of discovery, here is my version:
import static java.lang.Character.isDigit;
import static java.lang.Character.isWhitespace;
import java.util.HashMap;
import java.util.Map;
import static java.util.stream.Collectors.toMap;
public class TextExpressionSearch {
enum Token { VAR, LP, RP, NOT, AND, OR, END }
static class Tokenizer {
final String input;
int pos = 0;
String var;
Tokenizer(String input) {
this.input = input;
}
void reset() {
pos = 0;
var = null;
}
String getRead() {
return input.substring(0, pos);
}
Token getNext() {
var = null;
while (pos < input.length() && isWhitespace(input.charAt(pos))) {
++pos;
}
if (pos >= input.length()) {
return Token.END;
}
int start = pos++;
switch (input.charAt(start)) {
case 't':
while (pos < input.length() && isDigit(input.charAt(pos))) {
++pos;
}
var = input.substring(start, pos);
return Token.VAR;
case '(':
return Token.LP;
case ')':
return Token.RP;
case 'n':
if (input.startsWith("ot", pos)) {
pos += 2;
return Token.NOT;
}
break;
case 'a':
if (input.startsWith("nd", pos)) {
pos += 2;
return Token.AND;
}
break;
case 'o':
if (input.startsWith("r", pos)) {
pos += 1;
return Token.OR;
}
break;
}
throw new AssertionError("Can't tokenize: " + input.substring(start));
}
}
static class Evaluator {
final Tokenizer tokenizer;
final Map<String, Boolean> env;
Token lookAhead;
Evaluator(Tokenizer tokenizer, Map<String, Boolean> env) {
this.tokenizer = tokenizer;
this.env = env;
advance();
}
boolean die(String msg) {
throw new AssertionError(msg + "\nRead: " + tokenizer.getRead());
}
void advance() {
lookAhead = tokenizer.getNext();
}
void match(Token token) {
if (lookAhead != token) {
die("Expected " + token + ", found " + lookAhead);
}
advance();
}
boolean evaluate() {
boolean exprVal = expr();
match(Token.END);
return exprVal;
}
boolean expr() {
boolean lhs = negated();
switch (lookAhead) {
case AND:
advance();
return negated() && lhs;
case OR:
advance();
return negated() || lhs;
case END:
return lhs;
}
return die("Expected expr, found " + lookAhead);
}
boolean negated() {
switch (lookAhead) {
case NOT:
advance();
return !negated();
default:
return operand();
}
}
boolean operand() {
switch (lookAhead) {
case VAR:
if (!env.containsKey(tokenizer.var)) {
die("Undefined variable: " + tokenizer.var);
}
boolean varVal = env.get(tokenizer.var);
advance();
return varVal;
case LP:
advance();
boolean exprVal = expr();
match(Token.RP);
return exprVal;
}
return die("Expected operand, found " + lookAhead);
}
}
public static void main(String [] args) {
String expr = "((not t1 and ((not t2 or t4) or (t3 or t4))) or (t5 and not t6)) and t7";
Map<String, String> bindings = new HashMap<>();
bindings.put("t1", "str1");
bindings.put("t2", "str2");
bindings.put("t3", "str3");
bindings.put("t4", "str4");
bindings.put("t5", "str5");
bindings.put("t6", "str6");
bindings.put("t7", "str7");
String [] lines = {"str5 str7", "str4 str2"};
Tokenizer tokenizer = new Tokenizer(expr);
for (String line : lines) {
Map<String, Boolean> env =
bindings.entrySet().stream()
.collect(toMap(e -> e.getKey(), e -> line.contains(e.getValue())));
tokenizer.reset();
if (new Evaluator(tokenizer, env).evaluate()) {
System.out.println(line);
}
}
}
}
You can define a parser that returns a Predicate<String> that tests if a given string satisfies a conditional expression.
static Predicate<String> parse(String s, Map<String, String> map) {
return new Object() {
String[] tokens = Pattern.compile("[()]|[a-z][a-z0-9]*")
.matcher(s).results()
.map(MatchResult::group)
.toArray(String[]::new);
int length = tokens.length;
int index = 0;
String token = get();
String get() {
return token = index < length ? tokens[index++] : null;
}
boolean eat(String expect) {
if (expect.equals(token)) {
get();
return true;
}
return false;
}
Predicate<String> identifier() {
String id = token;
return s -> {
String value = map.get(id);
if (value == null)
throw new RuntimeException(
"identifier '" + id + "' undefined");
return s.contains(value);
};
}
Predicate<String> factor() {
boolean not = false;
Predicate<String> p;
if (eat("not"))
not = true;
switch (token) {
case "(":
get();
p = expression();
if (!eat(")"))
throw new RuntimeException("')' expected");
break;
case ")": case "not": case "and": case "or":
throw new RuntimeException("syntax error at '" + token + "'");
default:
p = identifier();
get();
break;
}
if (not)
p = p.negate();
return p;
}
Predicate<String> term() {
Predicate<String> p = factor();
while (eat("and"))
p = p.and(factor());
return p;
}
Predicate<String> expression() {
Predicate<String> p = term();
while (eat("or"))
p = p.or(term());
return p;
}
Predicate<String> parse() {
Predicate<String> p = expression();
if (token != null)
throw new RuntimeException("extra tokens string");
return p;
}
}.parse();
}
test case:
#Test
public void testParse() {
String s = "not ((t1 and not t3) or (t4 and t2)) or t5";
Map<String, String> map = new HashMap<>(Map.of(
"t1", "str1",
"t2", "str2",
"t3", "str3",
"t4", "str4",
"t5", "str5"));
Predicate<String> p = parse(s, map);
assertTrue(p.test("str5"));
assertTrue(p.test("str3"));
assertTrue(p.test("str1 str3"));
assertFalse(p.test("str1"));
assertFalse(p.test("str2 str4"));
// you can change value of variables.
assertFalse(p.test("str1 FOO"));
map.put("t5", "FOO");
assertTrue(p.test("str1 FOO"));
}
syntax:
expression = term { "or" term }
term = factor { "and" factor }
factor = [ "not" ] ( "(" expression ")" | identifier )
identifier = letter { letter | digit }
letter = "a" | "b" | ... | "z"
digit = "0" | "1" | ... | "9"
For posterity, here is my shunting yard solution which includes input validation:
public class CustomInputParser {
private Stack<Character> ops;
private LinkedList<Character> postFix;
private HashMap<Character, Integer> precedence;
private Stack<Boolean> eval;
private HashMap<Integer, String> termsMap;
private String customBool;
public CustomInputParser(HashMap<Integer, String> tMap, String custBool) {
this.ops = new Stack<Character>();
this.eval = new Stack<Boolean>();
this.postFix = new LinkedList<Character>();
this.termsMap = tMap;
this.customBool = custBool;
this.precedence = new HashMap<Character, Integer>();
precedence.put('n', 1);
precedence.put('a', 2);
precedence.put('o',3);
precedence.put('(', 4);
}
private int inToPost() {
char[] expr = convertToArr(this.customBool);
char c;
for(int i = 0; i < expr.length; i++) {
c = expr[i];
if(isOp(c)) {
if(processOp(c) != 0) return -1;
}
else {
if(!Character.isDigit(c)) {
return -1;
}
//I made the mistake of using a queue of Characters for postfix initially
//This only worked for up to 9 operands (multi digit would add mutiple chars to
// postfix for a single reference.
//This loops is a lazy workaround:
// 1. get the string of the reference (e.g. "11")
// 2. convert it to int
// 3. store the char value of the int in postfix
// 4. when evaluating operands in postfix eval, convert char back to int to get the termsMap key
String num = "";
while(i < expr.length) {
if(!Character.isDigit(expr[i])) {
i--;
break;
}
c = expr[i];
num += c;
i++;
}
int j = Integer.valueOf(num);
c = (char) j;
postFix.offer(c); //enqueue
}
}
while(!ops.empty()) {
if(ops.peek() == '(')return -1; //no matching close paren for the open paren
postFix.offer(ops.pop()); //pop and enqueue all remaining ops from stack
}
return 0;
}
private boolean isOp(char c) {
if(c == '(' || c == ')' || c =='n' || c=='a' || c=='o') {
return true;
}
return false;
}
private int processOp(char c) {
if (ops.empty() || c == '(') {
ops.push(c);
}
else if(c == ')') {
while(ops.peek() != '(') {
postFix.offer(ops.pop()); //pop and equeue ops wrapped in parens
if(ops.empty()) return -1; //no matching open paren for the close paren
}
ops.pop(); // don't enqueue open paren, just remove it from stack
}
else if(precedence.get(c) > precedence.get(ops.peek())) {
postFix.offer(ops.pop()); //pop and enqueue the higher precedence op
ops.push(c);
}
else {
ops.push(c);
}
return 0;
}
public boolean evaluate(String s) {
while(!postFix.isEmpty()) {
char c = postFix.poll();
boolean op1, op2;
switch(c) {
case 'n':
op1 = eval.pop();
eval.push(!op1);
break;
case 'a':
op1 = eval.pop();
op2 = eval.pop();
eval.push(op1 && op2);
break;
case 'o':
op1 = eval.pop();
op2 = eval.pop();
eval.push(op1 || op2);
break;
default:
int termKey = (int) c;
String term = this.termsMap.get(termKey);
eval.push(s.contains(String.valueOf(term)));
break;
}
}
return eval.pop();
}
private char[] convertToArr(String x) {
x = x.replace("not", "n").replace("and","a").replace("or", "o").replace("t", "").replace(" ", "");
return x.toCharArray();
}
public static void main(String[] args) {
String customBool = "(t1 and not (t2 and t3)) or (t4 and not t5)";
HashMap<Integer,String> termsMap = new HashMap<Integer, String>();
termsMap.put(1,"str1");
termsMap.put(2,"str2");
termsMap.put(3,"str3");
termsMap.put(4,"str4");
termsMap.put(5,"str5");
CustomInputParser c = new CustomInputParser(termsMap, customBool);
if(c.inToPost() != 0) {
System.out.println("invalid custom boolean");
}
else {
System.out.println(c.evaluate("str1str5"));
}
}
}
I have lots of multiple if-else statements. For code optimization, I need to write one function for all if else logic. As of now my code structure is in below.
input request is in JSONObject(org.json.simple.JSONObject), which have more than 10 values.
String s = (String) inputObj.get("test");
String s1 = (String) inputObj.get("test");
String s2 = (String) inputObj.get("test");
String s3 = (String) inputObj.get("test");
if (s != null && s.trim().isEmpty()) {
if (s1 != null && s1.trim().isEmpty()) {
if (s2 != null && s2.trim().isEmpty()) {
if (s3 != null && s3.trim().isEmpty()) {
if (s4 != null && s4.trim().isEmpty()) {
........
} else {
return;
}
} else {
return;
}
} else {
return;
}
} else {
return;
}
} else {
return;
}
How to avoid this kind of looping and throw an error message in common method.
Advance thanks.
Consider adding all your strings to array or ArrayList of string, and looping thru each entry in it, and check them for null or emptiness.
You can try this.
void main() {
List<String> sList = new ArrayList<>();
sList.add(inputObj.get("test"));
sList.add(inputObj.get("test"));
sList.add(inputObj.get("test"));
sList.add(inputObj.get("test"));
for(String s : sList){
try {
checkString(s);
}catch (Exception e){
//log or print the exception, however you like
}
}
}
void checkString(String s) throws Exception{
if(s!= null && !s.trim().isEmpty()){
//doStuff
}else{
throw new Exception("String is null or empty !!!");
}
}
You should also check this out.
public class YourClass{
private boolean isBlankDataPresent(JSONObject inputObj, String[] keys) throws Exception {
for (String key : keys) {
String input = (String) inputObj.get(key);
if( input == null || input.trim().isEmpty())
throw new Exception(key +" is Empty");
}
return false;
}
public boolean validateData(JSONObject inputObj, String[] keys) throws Exception {
boolean isBlankDataPresent= isBlankDataPresent(inputObj, keys);
if (!isBlankDataPresent) {
// do Your Stuff and return true
}
}
}
public Integer checkIsEmapty(String checkingString){
if(checkingString != null && !checkingString.trim().isEmpty()){
return 1;
}
return 0;
}
public String method(){
String s ="";
String s1 = "hi";
String s2 = "java";
String s3 = null;
String s4 = null;
Integer s1i = checkIsEmapty(s);
Integer s2i = checkIsEmapty(s1);
Integer s3i = checkIsEmapty(s2);
Integer s4i = checkIsEmapty(s3);
Integer s5i = checkIsEmapty(s4);
Integer total = s1i + s2i + s3i + s4i + s5i;
switch (total){
case 1 :
// To DO
case 2 :
// To DO
}
}
in switch used to checking the value, U can pass binary and Integer also
Like #Emre Acre mentioned,
List<String> sList = new ArrayList<>();
sList.add(inputObj.get("test"));
sList.add(inputObj.get("test"));
sList.add(inputObj.get("test"));
sList.add(inputObj.get("test"));
boolean allDataValid = sList
.stream()
.allMatch(s -> s != null && s.trim().isEmpty());
if(allDataValid) {
......
} else {
return;
}
I am trying ti implement the insert method of the Patricia Trie data structure but I have the feeling I wrote to many code lines. Please can someone tell me where can I call the method insert(TrieNode nodeRoot, String s) rekursiv?
Code:
private void insert(TrieNode nodeRoot, String s) {
int len1 = nodeRoot.value.length();
int len2 = s.length();
int len = Math.min(len1, len2);
for (int index = 0; index < len; index++) {
if (s.charAt(index) != nodeRoot.value.charAt(index)) {
// In case the both words have common substrings and after the
// common substrings the words are split.
String samesubString = s.substring(0, index);
String substringSplit1 = nodeRoot.value.substring(index);
String substringSplit2 = s.substring(index);
if (!samesubString.isEmpty()) {
nodeRoot.value = samesubString;
}
TrieNode nodeLeft = new TrieNode(substringSplit1);
nodeLeft.isWord = true;
TrieNode nodeRight = new TrieNode(substringSplit2);
nodeRight.isWord = true;
if (nodeRoot.getNext() != null && !nodeRoot.getNext().isEmpty()) {
checkTheValieAvialable(nodeRoot, s, nodeRight);
} else {
nodeRoot.next.add(nodeLeft);
nodeRoot.next.add(nodeRight);
for (TrieNode subword : nodeRoot.getNext()) {
System.out.println(nodeRoot.getValue() + "---"
+ subword.getValue());
}
}
break;
} else if (index == (s.length() - 1)
|| index == (nodeRoot.value.length() - 1)) {
// In case the node just needs one path since one word is
// substring of the other.
// For example (aba and abac)
if (len1 > len2) {
// root value is longer
System.out.println("root value is longer");
String samesubString = nodeRoot.value.substring(0,
index + 1);
String different = nodeRoot.value.substring(index + 1);
if (nodeRoot.getNext() != null
&& !nodeRoot.getNext().isEmpty()) {
for (TrieNode subword : nodeRoot.getNext()) {
String subword2 = subword.getValue();
boolean contains = different.contains(subword2);
if (contains) {
String[] split = different.split(subword2);
TrieNode leaf1 = new TrieNode(split[1]);
leaf1.isWord = true;
subword.next.add(leaf1);
System.out.println("Test.");
}
}
} else {
String substringSplit1 = nodeRoot.value.substring(index + 1);
nodeRoot.value = samesubString;
TrieNode leaf = new TrieNode(substringSplit1);
leaf.isWord = true;
nodeRoot.next.add(leaf);
for (TrieNode subword : nodeRoot.getNext()) {
System.out.println(nodeRoot.getValue() + "---"
+ subword.getValue());
}
}
String substringSplit1 = nodeRoot.value
.substring(index + 1);
nodeRoot.value = samesubString;
nodeRoot.isWord = true;
TrieNode leaf = new TrieNode(substringSplit1);
leaf.isWord = true;
nodeRoot.next.add(leaf);
for (TrieNode subword : nodeRoot.getNext()) {
System.out.println(nodeRoot.getValue() + "---"
+ subword.getValue());
}
} else {
// new inserted string value is longer. For example (abac and aba).
System.out.println("instered is longer");
String samesubString = s.substring(0, index + 1);
String different = s.substring(index + 1);
if (nodeRoot.getNext() != null
&& !nodeRoot.getNext().isEmpty()) {
for (TrieNode subword : nodeRoot.getNext()) {
String subword2 = subword.getValue();
boolean contains = different.contains(subword2);
if (contains) {
String[] split = different.split(subword2);
TrieNode leaf1 = new TrieNode(split[1]);
leaf1.isWord = true;
subword.next.add(leaf1);
System.out.println("Test.");
}
}
} else {
String substringSplit1 = s.substring(index + 1);
s = samesubString;
TrieNode parentLeaf = new TrieNode(s);
parentLeaf.isWord = true;
TrieNode leaf = new TrieNode(substringSplit1);
leaf.isWord = true;
nodeRoot.next.add(leaf);
for (TrieNode subword : nodeRoot.getNext()) {
System.out.println(nodeRoot.getValue() + "---"
+ subword.getValue());
}
}
}
} else {
System.out.println("They are the same - " + index);
}
}
}
TrieNode class:
package patriciaTrie;
import java.util.ArrayList;
public class TrieNode {
ArrayList<TrieNode> next = new ArrayList<TrieNode>();
String value;
boolean isWord;
TrieNode(String value){
this.value = value;
}
public ArrayList<TrieNode> getNext() {
return next;
}
public void setNext(ArrayList<TrieNode> next) {
this.next = next;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
While using recursion please consider the steps:
Base condition
Logic (if any)
Recursive call.
Ex. for factorial of number:
int fact(int n)
{
if(n==0 || n==1)
return 1; // Base condition
return n * fact(n-1); // Recursive call
}
Applying the same concept in Trie:
base condition is: while traversing through a path, if we have reached leaf, current string is not in trie, then create a new edge or node and add remaining character to it.
Recursively call the insert if we have found a matching node. And if a matching node doen't exist create a new path with common parent.
You can take help from link : http://www.geeksforgeeks.org/trie-insert-and-search/
The best way to approach to problem recursively is to identify base condition in a problem.
The calculator is now almost working. It now gives me the same answer for every equation it reads in?
the output ends up as:
49+62*61-36
15.666666666666668
4/64
15.666666666666668
(53+26)
15.666666666666668
0*72
15.666666666666668
21-85+75-85
15.666666666666668
90*76-50+67
15.666666666666668
46*89-15
15.666666666666668
34/83-38
15.666666666666668
20/76/14+92-15
15.666666666666668
5*10/3-1
15.666666666666668
Instead of having the answer for each equation there?
Have i missed something out in my methods?
Thanks
All code is shown below. Any help will be much appreciated.
Stack class:
import java.util.Iterator;
import java.util.NoSuchElementException;
public class myStack<Item> implements Iterable<Item> {
private int N; // size of the stack
private Node first; // top of stack
private class Node {
private Item item;
private Node next;
}
/**
* Create an empty stack.
*/
public myStack() {
first = null;
N = 0;
assert check();
}
public boolean isEmpty() {
return first == null;
}
public int size() {
return N;
}
public void push(Item item) {
Node oldfirst = first;
first = new Node();
first.item = item;
first.next = oldfirst;
N++;
assert check();
}
public Item pop() {
if (isEmpty())
throw new NoSuchElementException("Stack underflow");
Item item = first.item; // save item to return
first = first.next; // delete first node
N--;
assert check();
return item; // return the saved item
}
public Item peek() {
if (isEmpty())
throw new NoSuchElementException("Stack underflow");
return first.item;
}
public String toString() {
StringBuilder s = new StringBuilder();
for (Item item : this)
s.append(item + " ");
return s.toString();
}
// check internal invariants
private boolean check() {
if (N == 0) {
if (first != null)
return false;
} else if (N == 1) {
if (first == null)
return false;
if (first.next != null)
return false;
} else {
if (first.next == null)
return false;
}
// check internal consistency of instance variable N
int numberOfNodes = 0;
for (Node x = first; x != null; x = x.next) {
numberOfNodes++;
}
if (numberOfNodes != N)
return false;
return true;
}
public Object[] toArray(String[] elementData) {
return (Object[]) elementData.clone();
}
public Iterator<Item> iterator() {
return new ListIterator();
}
// did not implement remove as it was not needed
private class ListIterator implements Iterator<Item> {
private Node current = first;
public boolean hasNext() {
return current != null;
}
public void remove() {
throw new UnsupportedOperationException();
}
public Item next() {
if (!hasNext())
throw new NoSuchElementException();
Item item = current.item;
current = current.next;
return item;
}
}
}
Array list class
import java.util.Arrays;
public class myArrayList<Item>{
private Object[] myStore;
private int actSize = 0;
public myArrayList() {
myStore = new Object[100];
}
public Object get(int index) {
if (index < actSize) {
return myStore[index];
} else {
throw new ArrayIndexOutOfBoundsException();
}
}
public void add(Object obj) {
if (myStore.length - actSize <= 0) {
increaseListSize();
}
myStore[actSize++] = obj;
}
public Object remove(int index) {
if (index < actSize) {
Object obj = myStore[index];
myStore[index] = null;
int tmp = index;
while (tmp < actSize) {
myStore[tmp] = myStore[tmp + 1];
myStore[tmp + 1] = null;
tmp++;
}
actSize--;
return obj;
} else {
throw new ArrayIndexOutOfBoundsException();
}
}
public int size() {
return actSize;
}
private void increaseListSize() {
myStore = Arrays.copyOf(myStore, myStore.length * 2);
}
#SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
if (a.length < size())
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(myStore, size(), a.getClass());
System.arraycopy(myStore, 0, a, 0, size());
if (a.length > size())
a[size()] = null;
return a;
}
}
The TestClass for equation handling
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
public class TestClass {
private static final int LEFT_ASSOC = 0;
private static final int RIGHT_ASSOC = 1;
static String OPERATORS1 = "+-*/()";
// Operators
private static final Map<String, int[]> OPERATORS = new HashMap<String, int[]>();
static {
// Map<"token", []{precedence, associativity}>
OPERATORS.put("+", new int[] { 0, LEFT_ASSOC });
OPERATORS.put("-", new int[] { 0, LEFT_ASSOC });
OPERATORS.put("*", new int[] { 5, LEFT_ASSOC });
OPERATORS.put("/", new int[] { 5, LEFT_ASSOC });
OPERATORS.put("(", new int[] {1, LEFT_ASSOC});
OPERATORS.put(")", new int[] {1, LEFT_ASSOC});
}
private static boolean isOperator(String token) {
return OPERATORS.containsKey(token);
}
// Test associativity of operator token
private static boolean isAssociative(String token, int type) {
if (!isOperator(token)) {
throw new IllegalArgumentException("Invalid token: " + token);
}
if (OPERATORS.get(token)[1] == type) {
return true;
}
return false;
}
// Compare precedence of operators.
private static final int cmpPrecedence(String token1, String token2) {
if (!isOperator(token1) || !isOperator(token2)) {
throw new IllegalArgumentException("Invalid tokens: " + token1
+ " " + token2);
}
return OPERATORS.get(token1)[0] - OPERATORS.get(token2)[0];
}
public static String[] infixToRPN(String[] inputTokens) {
myArrayList<String> out = new myArrayList<String>();
myStack<String> stack = new myStack<String>();
// For each token
for (String token : inputTokens) {
StringTokenizer tokens = new StringTokenizer(token,OPERATORS1,true);
while (tokens.hasMoreTokens()) {
token = tokens.nextToken();
// If token is an operator
if (isOperator(token)) {
// While stack not empty AND stack top element
// is an operator
while (!stack.isEmpty() && isOperator(stack.peek())) {
if ((isAssociative(token, LEFT_ASSOC) && cmpPrecedence(
token, stack.peek()) <= 0)
|| (isAssociative(token, RIGHT_ASSOC) && cmpPrecedence(
token, stack.peek()) < 0)) {
out.add(stack.pop());
continue;
}
break;
}
// Push the new operator on the stack
stack.push(token);
}
// If token is a left bracket '('
else if (token.equals("(")) {
stack.push(token);
}
// If token is a right bracket ')'
else if (token.equals(")")) {
while (!stack.isEmpty() && !stack.peek().equals("(")) {
out.add(stack.pop());
}
stack.pop();
}
// If token is a number
else {
out.add(token);
}
}
while (!stack.isEmpty()) {
out.add(stack.pop());
}
}
String[] output = new String[out.size()];
return out.toArray(output);
}
public static double RPNtoDouble(String[] tokens) {
myStack<String> stack = new myStack<String>();
// For each token
for (String token : tokens) {
//System.out.println( "Working this token: " + token );
// If the token is a value push it onto the stack
if (!isOperator(token)) {
stack.push(token);
} else {
// Token is an operator: pop top two entries
Double d2 = Double.valueOf(stack.pop());
Double d1 = Double.valueOf(stack.pop());
// Get the result
Double result = token.compareTo("+") == 0 ? d1 + d2 : token
.compareTo("-") == 0 ? d1 - d2
: token.compareTo("*") == 0 ? d1 * d2 : d1 / d2;
// Push result onto stack
stack.push(String.valueOf(result));
}
}
return Double.valueOf(stack.pop());
}
static public void main(String[] args) throws IOException {
File file = new File("testEquations.txt");
String[] lines = new String[1];
try {
FileReader reader = new FileReader(file);
#SuppressWarnings("resource")
BufferedReader buffReader = new BufferedReader(reader);
int x = 0;
String s;
while ((s = buffReader.readLine()) != null) {
lines[x] = s;
x++;
}
} catch (IOException e) {
System.exit(0);
}
// test printing string array
for (String s : lines) {
System.out.println("" + s);
String[] output =infixToRPN(lines);
System.out.println(RPNtoDouble(output));
}
}
}
Your problem is here:
String[] lines = new String[1];
try {
FileReader reader = new FileReader(file);
#SuppressWarnings("resource")
BufferedReader buffReader = new BufferedReader(reader);
int x = 0;
String s;
while ((s = buffReader.readLine()) != null) {
lines[x] = s;
x++;
}
...
you define array of string with a size = 1 but you don't check inside the loop if x is getting out of the borders of this array.
Do somethink like this:
int Size = // define the size..;
String[] lines = new String[Size];
...
while (x < Size && (s = buffReader.readLine()) != null)) {
lines[x] = s;
x++;
}
when your x becames bigger then Size, x < Size will evaluate false, thus getting out of the loop.
About one of the error you are getting ArrayIndexOutOfBoundsException:
Thrown to indicate that an array has been accessed with an illegal
index. The index is either negative or greater than or equal to the
size of the array. (source)
The other error NoSuchElementException :
Thrown by the nextElement method of an Enumeration to indicate that
there are no more elements in the enumeration (source).
Another problem is here:
// test printing string array
for (String s : lines)
{
System.out.println("" + s);
String[] output =infixToRPN(lines);
System.out.println(RPNtoDouble(output));
}
You have to pass s, and not lines into method infixToRPN, thats why you are getting the same output, because you are giving the same input.
Remember that infixToRPN receives a String [] not a string like 's', but this I leave to you to find a workaround.