Tokenize String expression!
Hej!
Jag sitter här med ett uppgift som går ut på att dela upp String expr i tokens. där token är antingen nummer, operator eller parentes "(" ")".
tokens skall returneras som list av string.
Här är min metod:
List<String> tokenize(String expr) {
List<String> listOftokens = new ArrayList<>(); //Empty List.
StringTokenizer st = new StringTokenizer(expr); //String tokenizer.
while (st.hasMoreTokens()){ //if there are more tokens available
listOftokens.add(st.nextToken()); //return next token, add to list.
}
return listOftokens;
}
}
om min String expr är som följer: "1 + 10" så får jag rätt.
Men, om expr är för ex. "1+ 10" så får jag ["1+", "10"]
rätt return är [ "1", "+", "10"]
Behöver jag jobba på själva Stringen först? innan jag delar upp den i tokens ?
StringTokenizer gör det:
"Constructs a string tokenizer for the specified string. The tokenizer uses the default delimiter set, which is " \t\n\r\f": the space character, the tab character, the newline character, the carriage-return character, and the form-feed character. Delimiter characters themselves will not be treated as tokens."
Du behöver något annat, som kan hitta gränsen mellan tokens utan delimiters.
Eller (t.ex. med hjälp av regexp) infoga mellanslag mellan tokens först.
T.ex. du kan skriva en padNumbers metod som lägger till mellanslag före och efter nummer, och gör detsamma med parentes:
static String padNumbersAndParentheses(String input) {
return input.replaceAll("([0-9]+)", " $1 ").replaceAll("([()])", " $1 ");
}
Macilaci skrev:StringTokenizer gör det:
"Constructs a string tokenizer for the specified string. The tokenizer uses the default delimiter set, which is " \t\n\r\f": the space character, the tab character, the newline character, the carriage-return character, and the form-feed character. Delimiter characters themselves will not be treated as tokens."
Du behöver något annat, som kan hitta gränsen mellan tokens utan delimiters.
Eller (t.ex. med hjälp av regexp) infoga mellanslag mellan tokens först.
De gränser kan vara operatorer och parenteser? Är det ett bra strategi om jag gör följande:
_Skapar en lista av typ String.
_For-sats, från första char i String till sista.
_If-sats, hittar jag en operator eller parenteser så skiljer jag mellan talen. (med tanke på att operatorer och parenteser skall ju oxå adderas till listan)
Ja, det kan vara en bra strategi, men frågan är: Vad är en operator? Kan den bestå av flera tecken?
T. ex i java har vi ++, <=, +=, && osv.
Om jag antar (och det är rimligt att anta) att det finns bara en operator mellan nummer eller parenteser, då är kanske säkrast att göra "padding" runt nummer och parenteser.
Macilaci skrev:Ja, det kan vara en bra strategi, men frågan är: Vad är en operator? Kan den bestå av flera tecken?
T. ex i java har vi ++, <=, +=, && osv.
I min uppgift så är operatorer "+,-,*,/,^". Då tanken vidare är att skapa en Calculator.
Macilaci skrev:Ja, det kan vara en bra strategi, men frågan är: Vad är en operator? Kan den bestå av flera tecken?
T. ex i java har vi ++, <=, +=, && osv.
Om jag antar (och det är rimligt att anta) att det finns bara en operator mellan nummer eller parenteser, då är kanske säkrast att göra "padding" runt nummer och parenteser.
Japp, det är ett bra antagande. Har, tyvärr inte koll på hur man gör "padding" runt nummer.
Macilaci skrev:T.ex. du kan skriva en padNumbers metod som lägger till mellanslag före och efter nummer, och gör detsamma med parentes:
static String padNumbersAndParentheses(String input) {
return input.replaceAll("([0-9]+)", " $1 ").replaceAll("([()])", " $1 ");
}
Är ganska nybörjare i Programmering, det är första gången jag ser det som du skrivit efter replaceAll :)
Om du kan anta att alla operatorer är allt som inte är tal och parenteser borde Macilacis idé med att skjuta in space runt tal funka (glöm inte minustecken och decimalkomma/punkt). Förslaget använder regex vilket kan vara lite uppförsbacke om du inte är van vid dem.
Vill man ha total kontroll får man ta tecken för tecken i en loop. Det vanliga sättet är att ha en boolean som håller koll på om man är i ett tal eller inte. Typ:
for (int i = 0; i < s.length(); i++ {
char c = s.charAt(i);
if (Character.isDigit(c) || c == '-' || c == ',') {
if (!inNumber) {
inNumber = true;
numString = new StringBuffer();
}
numString.append(c);
} else {
addToken(numString);
inNumber = false;
}
} else {
addToken(new String(c));
}
Jag skrev detta direkt här i inmatningsfältet så det innehåller säkert fel, mest tänkt som en illustration.
Ja, logiken verkar vara okej.
Här kommer själva textuppgiften ifall jag inte var så tydligt:
Implement the method tokenize. The method takes a string and breaks it up into tokens.
A token is a number, an operator or a parentheses (left or right), all represented as Strings.
The tokens are returned as a list of String.
operators are "+" "-" "/" "*" "^"
Programmeraren skrev:Om du kan anta att alla operatorer är allt som inte är tal och parenteser borde Macilacis idé med att skjuta in space runt tal funka (glöm inte minustecken och decimalkomma/punkt). Förslaget använder regex vilket kan vara lite uppförsbacke om du inte är van vid dem.
Vill man ha total kontroll får man ta tecken för tecken i en loop. Det vanliga sättet är att ha en boolean som håller koll på om man är i ett tal eller inte. Typ:
for (int i = 0; i < s.length(); i++ {
char c = s.charAt(i);
if (Character.isDigit(c) || c == '-' || c == ',') {
if (!inNumber) {
inNumber = true;
numString = new StringBuffer();
}
numString.append(c);
} else {
addToken(numString);
inNumber = false;
}
} else {
addToken(new String(c));
}Jag skrev detta direkt här i inmatningsfältet så det innehåller säkert fel, mest tänkt som en illustration.
Skulle du kunna ta och förklara villkoret för den andra if-satsen.
Ett problem: När du avgör om ett tecken är en del av ett nummer, glöm "-". Det är för svårt att avgöra om det är en del av ett nummer eller en operatör.
Iden med boolean inNumber är att hålla reda om vi är i ett tal eller inte. Så länge vi är i ett tal vill vi ackumulera alla siffrorna tills talet är slut. Så om vi inte är i ett tal så startar vi ett nytt tal.
Efter loopen behövs också:
if (numString.length() > 0)
addToken(numString);
eftersom ett tal kan stå sist i strängen.
Macilaci skrev:Ett problem: När du avgör om ett tecken är en del av ett nummer, glöm "-". Det är för svårt att avgöra om det är en del av ett nummer eller en operatör.
Ja, som logiken ser ut nu får man ta bort c=='-' från if:en, annars kommer typ 34-2 bli en token
För att klara negativa tal får man istället endast acceptera '-' som del av tal om det inte kommer efter en operator . Så fort man är i ett tal så betyder ett '-' att talet är slut
Min lösning:
static List<String> tokenize(String expr) {
List<String> listOftokens = new ArrayList<>(); //Empty List.
boolean inNumber = false;
StringBuffer numString = new StringBuffer();
for (int i = 0; i < expr.length(); i++) {
char c = expr.charAt(i);
if (Character.isDigit(c) || c == ',') {
if (!inNumber) {
inNumber = true;
numString.append(c);
} else {
numString.append(c);
}
} else {
if(numString.length()>0) {
listOftokens.add(numString.toString());
}
numString = new StringBuffer();
inNumber = false;
listOftokens.add(String.valueOf(c));
}
}
if(numString.length()>0) {
listOftokens.add(numString.toString());
}
return listOftokens;
}
Så här har jag nu gjort:
List<String> tokenize(String expr) {
String goodExpr = padNumbersandParentheses(expr).trim();
List<String> listOftokens = new ArrayList<>();//Empty List.
listOftokens.add(goodExpr);
return listOftokens;
}
String padNumbersandParentheses (String expr) {
return expr.replaceAll("([0-9]+)", " $1 ").replaceAll("([()])", " $1 ");
}
När jag kör testet så får jag nu extra mellan slag mellan talen och operatorer/paranteser.
padNumbersandParentheses() och StringTokenizer funkar bra tillsammans (eftersom StringTokenizer tar bort de extra mellanslagen).
Det gick bra med padNumbersandParentheses() och StringTokenizer. Tack så mycket för hjälpen.