18 svar
406 visningar
Hmowed är nöjd med hjälpen
Hmowed 63
Postad: 29 nov 2021 21:09

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 ?

Macilaci 2106
Postad: 29 nov 2021 21:26

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.

Macilaci 2106
Postad: 29 nov 2021 21:38 Redigerad: 29 nov 2021 21:43

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 ");
}

Hmowed 63
Postad: 29 nov 2021 21:42
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)

Macilaci 2106
Postad: 29 nov 2021 21:47 Redigerad: 29 nov 2021 21:55

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. 

Hmowed 63
Postad: 29 nov 2021 21:54 Redigerad: 29 nov 2021 21:54
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. 

Hmowed 63
Postad: 29 nov 2021 21:57
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. 

Hmowed 63
Postad: 29 nov 2021 21:59
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 :)

Programmeraren 3387
Postad: 29 nov 2021 22:09 Redigerad: 29 nov 2021 22:11

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.

Macilaci 2106
Postad: 29 nov 2021 22:16

Ja, logiken verkar vara okej.

Hmowed 63
Postad: 29 nov 2021 22:17

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 "+" "-" "/" "*" "^"

Hmowed 63
Postad: 29 nov 2021 22:21
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. 

Macilaci 2106
Postad: 29 nov 2021 22:29

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.

Programmeraren 3387
Postad: 29 nov 2021 22:36

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.

Programmeraren 3387
Postad: 29 nov 2021 22:38
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

Macilaci 2106
Postad: 29 nov 2021 22:39
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;
    }
Hmowed 63
Postad: 29 nov 2021 22:55

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. 

Macilaci 2106
Postad: 29 nov 2021 23:28

padNumbersandParentheses() och StringTokenizer funkar bra tillsammans (eftersom StringTokenizer tar bort de extra mellanslagen).

Hmowed 63
Postad: 30 nov 2021 08:25

Det gick bra med padNumbersandParentheses() och StringTokenizer. Tack så mycket för hjälpen.  

Svara Avbryt
Close