स्ट्रिंग रूप में दी गई गणित की अभिव्यक्ति का मूल्यांकन कैसे करें?


318

मैं Stringमूल्यों से सरल गणित के भावों का मूल्यांकन करने के लिए एक जावा रूटीन लिखने की कोशिश कर रहा हूं :

  1. "5+3"
  2. "10-40"
  3. "10*3"

मैं बहुत सारे इफ-तत्कालीन बयानों से बचना चाहता हूं। मैं यह कैसे कर सकता हूँ?


7
मैंने हाल ही में एक गणित अभिव्यक्ति पार्सर लिखी है जिसे एक्सप 4 जे कहा जाता है जिसे आपाचे लाइसेंस के तहत जारी किया गया था जिसे आप यहाँ देख सकते हैं: objecthunter.net/exp4j
fasseg

2
आप किस तरह के एक्सप्रेशन की अनुमति देते हैं? केवल एकल ऑपरेटर अभिव्यक्ति? क्या कोष्ठक की अनुमति है?
राधावल्द



3
इसे कैसे संभव माना जा सकता है? दिज्क्स्ट्रा का मूल्यांकन यहाँ स्पष्ट समाधान है। en.wikipedia.org/wiki/Shunting-yard_algorithm
मार्टिन स्पैमर

जवाबों:


376

JDK1.6 के साथ, आप अंतर्निहित जावास्क्रिप्ट इंजन का उपयोग कर सकते हैं।

import javax.script.ScriptEngineManager;
import javax.script.ScriptEngine;
import javax.script.ScriptException;

public class Test {
  public static void main(String[] args) throws ScriptException {
    ScriptEngineManager mgr = new ScriptEngineManager();
    ScriptEngine engine = mgr.getEngineByName("JavaScript");
    String foo = "40+2";
    System.out.println(engine.eval(foo));
    } 
}

52
ऐसा लगता है कि वहाँ एक बड़ी समस्या है; यह एक स्क्रिप्ट निष्पादित करता है, एक अभिव्यक्ति का मूल्यांकन नहीं करता है। स्पष्ट होने के लिए, engine.eval ("8; 40 + 2"), आउटपुट 42! यदि आप एक अभिव्यक्ति पार्सर चाहते हैं जो सिंटैक्स की जांच भी करता है, तो मैंने सिर्फ एक को समाप्त किया है (क्योंकि मुझे कुछ भी नहीं मिला जो मेरी आवश्यकताओं के अनुरूप हो): जेवलुलेटर
जीन-मार्क एस्टेसाना

4
एक साइड नोट के रूप में, यदि आपको अपने कोड में कहीं और इस अभिव्यक्ति के परिणाम का उपयोग करने की आवश्यकता है, तो आप परिणाम को डबल की तरह टाइपकास्ट कर सकते हैं: return (Double) engine.eval(foo);
बेन विजनेस

38
सुरक्षा नोट: आपको उपयोगकर्ता इनपुट के साथ सर्वर संदर्भ में इसका उपयोग कभी नहीं करना चाहिए। निष्पादित जावास्क्रिप्ट सभी जावा वर्गों का उपयोग कर सकता है और इस प्रकार आपके आवेदन को बिना सीमा के अपहरण कर सकता है।
बोआन

3
@ बॉन, मैं आपसे अनुरोध करता हूं कि आपने जो कहा है उसके बारे में मुझे एक संदर्भ दें। (निश्चित रूप से 100%)
पार्थो

17
@partho new javax.script.ScriptEngineManager().getEngineByName("JavaScript") .eval("var f = new java.io.FileWriter('hello.txt'); f.write('UNLIMITED POWER!'); f.close();");- जावास्क्रिप्ट के माध्यम से एक फ़ाइल लिखेंगे (डिफ़ॉल्ट रूप से) कार्यक्रम की वर्तमान निर्देशिका
Boann

236

मैंने evalइस प्रश्न का उत्तर देने के लिए अंकगणितीय अभिव्यक्तियों के लिए यह विधि लिखी है । यह जोड़, घटाव, गुणा, भाग, घातांक ( ^प्रतीक का उपयोग ), और कुछ बुनियादी कार्य करता है sqrt। यह (... का उपयोग कर समूहीकरण का समर्थन करता है ), और यह ऑपरेटर की पूर्ववर्तीता और सहानुभूति नियमों को सही करता है।

public static double eval(final String str) {
    return new Object() {
        int pos = -1, ch;

        void nextChar() {
            ch = (++pos < str.length()) ? str.charAt(pos) : -1;
        }

        boolean eat(int charToEat) {
            while (ch == ' ') nextChar();
            if (ch == charToEat) {
                nextChar();
                return true;
            }
            return false;
        }

        double parse() {
            nextChar();
            double x = parseExpression();
            if (pos < str.length()) throw new RuntimeException("Unexpected: " + (char)ch);
            return x;
        }

        // Grammar:
        // expression = term | expression `+` term | expression `-` term
        // term = factor | term `*` factor | term `/` factor
        // factor = `+` factor | `-` factor | `(` expression `)`
        //        | number | functionName factor | factor `^` factor

        double parseExpression() {
            double x = parseTerm();
            for (;;) {
                if      (eat('+')) x += parseTerm(); // addition
                else if (eat('-')) x -= parseTerm(); // subtraction
                else return x;
            }
        }

        double parseTerm() {
            double x = parseFactor();
            for (;;) {
                if      (eat('*')) x *= parseFactor(); // multiplication
                else if (eat('/')) x /= parseFactor(); // division
                else return x;
            }
        }

        double parseFactor() {
            if (eat('+')) return parseFactor(); // unary plus
            if (eat('-')) return -parseFactor(); // unary minus

            double x;
            int startPos = this.pos;
            if (eat('(')) { // parentheses
                x = parseExpression();
                eat(')');
            } else if ((ch >= '0' && ch <= '9') || ch == '.') { // numbers
                while ((ch >= '0' && ch <= '9') || ch == '.') nextChar();
                x = Double.parseDouble(str.substring(startPos, this.pos));
            } else if (ch >= 'a' && ch <= 'z') { // functions
                while (ch >= 'a' && ch <= 'z') nextChar();
                String func = str.substring(startPos, this.pos);
                x = parseFactor();
                if (func.equals("sqrt")) x = Math.sqrt(x);
                else if (func.equals("sin")) x = Math.sin(Math.toRadians(x));
                else if (func.equals("cos")) x = Math.cos(Math.toRadians(x));
                else if (func.equals("tan")) x = Math.tan(Math.toRadians(x));
                else throw new RuntimeException("Unknown function: " + func);
            } else {
                throw new RuntimeException("Unexpected: " + (char)ch);
            }

            if (eat('^')) x = Math.pow(x, parseFactor()); // exponentiation

            return x;
        }
    }.parse();
}

उदाहरण:

System.out.println(eval("((4 - 2^3 + 1) * -sqrt(3*3+4*4)) / 2"));

आउटपुट: 7.5 (जो सही है)


पार्सर एक पुनरावर्ती वंशीय पार्सर है , इसलिए आंतरिक रूप से इसके व्याकरण में ऑपरेटर स्तर की पूर्वता के प्रत्येक स्तर के लिए अलग-अलग पार्स तरीकों का उपयोग करता है। मैंने इसे छोटा रखा है इसलिए इसे संशोधित करना आसान है, लेकिन यहां कुछ विचार दिए गए हैं जिन्हें आप इसके साथ विस्तारित करना चाहते हैं:

  • चर:

    Parser की बिट्स जो फ़ंक्शन के लिए नामों को पढ़ती है, आसानी से कस्टम चर को संभालने के लिए आसानी से बदली जा सकती है, evalविधि में पारित चर तालिका में नामों को देखकर , जैसे कि a Map<String,Double> variables

  • अलग संकलन और मूल्यांकन:

    क्या हो सकता है, अगर चर के लिए समर्थन जोड़ा जा रहा है, तो आप हर बार पार्स किए बिना, उसी परिवर्तन का मूल्यांकन लाखों परिवर्तित चर के साथ करना चाहते हैं? यह संभव है। पहले से परिभाषित अभिव्यक्ति का मूल्यांकन करने के लिए उपयोग करने के लिए एक इंटरफ़ेस परिभाषित करें:

    @FunctionalInterface
    interface Expression {
        double eval();
    }

    अब सभी तरीकों को बदलें जो doubleएस वापस करते हैं , इसलिए इसके बजाय वे उस इंटरफ़ेस का एक उदाहरण देते हैं। जावा 8 का लैम्ब्डा सिंटेक्स इसके लिए बहुत अच्छा काम करता है। परिवर्तित तरीकों में से एक का उदाहरण:

    Expression parseExpression() {
        Expression x = parseTerm();
        for (;;) {
            if (eat('+')) { // addition
                Expression a = x, b = parseTerm();
                x = (() -> a.eval() + b.eval());
            } else if (eat('-')) { // subtraction
                Expression a = x, b = parseTerm();
                x = (() -> a.eval() - b.eval());
            } else {
                return x;
            }
        }
    }

    यह Expressionसंकलित अभिव्यक्ति (एक सार वाक्यविन्यास वृक्ष ) का प्रतिनिधित्व करने वाली वस्तुओं का एक पुनरावर्ती पेड़ बनाता है । फिर आप इसे एक बार संकलित कर सकते हैं और विभिन्न मूल्यों के साथ इसका बार-बार मूल्यांकन कर सकते हैं:

    public static void main(String[] args) {
        Map<String,Double> variables = new HashMap<>();
        Expression exp = parse("x^2 - x + 2", variables);
        for (double x = -20; x <= +20; x++) {
            variables.put("x", x);
            System.out.println(x + " => " + exp.eval());
        }
    }
  • विभिन्न डेटाटाइप्स:

    इसके बजाय double, आप मूल्यांकनकर्ता को कुछ अधिक शक्तिशाली उपयोग करने के लिए बदल सकते हैं जैसे BigDecimal, या एक वर्ग जो जटिल संख्याओं, या तर्कसंगत संख्याओं (अंशों) को लागू करता है। तुम भी उपयोग कर सकते हैं Object, कुछ वास्तविक प्रोग्रामिंग भाषा की तरह, भावप्रवणता के कुछ मिश्रण की अनुमति। :)


इस जवाब जारी किया गया के सभी कोड सार्वजनिक क्षेत्र के लिए । मज़े करो!


1
अच्छा एल्गोरिथ्म, इससे शुरू होकर मैं निहितार्थ और तार्किक संचालकों में कामयाब रहा। हमने फ़ंक्शन का मूल्यांकन करने के लिए फ़ंक्शन के लिए अलग-अलग कक्षाएं बनाई हैं, इसलिए चर के आपके विचार की तरह, मैं फ़ंक्शन के साथ एक नक्शा बनाता हूं और फ़ंक्शन के नाम को देखता हूं। हर फंक्शन एक तरीके के इवैल्यूशन (T rightOperator, T leftOperator) के साथ एक इंटरफेस को लागू करता है, इसलिए कभी भी हम एल्गोरिदम कोड को बदले बिना फीचर्स जोड़ सकते हैं। और यह सामान्य प्रकार के साथ काम करने के लिए एक अच्छा विचार है। धन्यवाद!
Vasile Bors

1
क्या आप इस एल्गोरिथम के पीछे के तर्क की व्याख्या कर सकते हैं?
ययोनतन

1
मैं बॉन द्वारा लिखे गए कोड से जो कुछ भी समझता हूं उसका विवरण देने की कोशिश करता हूं, और उदाहरणों में विकी का वर्णन किया गया है। इस अल्गोरिट्म का तर्क ऑपरेशन के आदेशों के नियमों से शुरू होता है। 1. ऑपरेटर साइन | चर मूल्यांकन | फंक्शन कॉल | कोष्ठक (उप-अभिव्यक्तियाँ); 2. घातांक; 3. गुणा, भाग; 4. इसके अलावा, घटाव;
वासिले बोर्स

1
एल्गोरिदम विधियों को प्रत्येक प्रकार के संचालन क्रम के लिए विभाजित किया गया है: parseFactor = 1. ऑपरेटर साइन | चर मूल्यांकन | फंक्शन कॉल | कोष्ठक (उप-अभिव्यक्तियाँ); 2. घातांक; parseTerms = 3. गुणा, भाग; parseExpression = 4. जोड़, घटाव। एल्गोरिथ्म, कॉल विधियों को रिवर्स ऑर्डर में (parseExpression -> parseTerms -> parseFactor -> parseExpression (उप-अभिव्यक्तियों के लिए)), लेकिन पहली पंक्ति के लिए प्रत्येक विधि विधि को अगले स्तर तक बुलाती है, इसलिए संपूर्ण निष्पादन आदेश विधियां होंगी वास्तव में संचालन का सामान्य क्रम।
वासिल बोर्स

1
उदाहरण के लिए parseExpression विधि वास्तविक double x = parseTerm(); ऑपरेटर for (;;) {...}स्तर (इसके अलावा, घटाव) के आत्मघाती संचालन का मूल्यांकन करने के बाद, बाएं ऑपरेटर का मूल्यांकन करें । एक ही तर्क हैं और पार्सटर्म विधि में। ParseFactor का अगला स्तर नहीं होता है, इसलिए केवल विधियों / चर का मूल्यांकन होता है या पार्थिनेसिस के मामले में - उप-अभिव्यक्ति का मूल्यांकन करते हैं। boolean eat(int charToEat)CharToEat चरित्र के साथ वर्तमान कर्सर चरित्र की विधि की जांच समानता, अगर अगले वर्ण के बराबर वापसी सच और कदम कर्सर, मैं नाम का उपयोग इसके लिए 'स्वीकार'।
वासिल बोरस

34

इसे हल करने का सही तरीका एक लेसर और एक पार्सर के साथ है । आप इनमें से सरल संस्करण स्वयं लिख सकते हैं, या उन पृष्ठों में जावा लेकर्स और पार्सर्स के लिंक भी हैं।

एक पुनरावर्ती वंशीय पार्सर बनाना वास्तव में अच्छा सीखने का व्यायाम है।


26

मेरे विश्वविद्यालय के प्रोजेक्ट के लिए, मैं दोनों मूल फ़ार्मुलों और अधिक जटिल समीकरणों (विशेष रूप से पुनरावृत्त ऑपरेटरों) का समर्थन करने वाले पार्सर / मूल्यांकनकर्ता की तलाश कर रहा था। मुझे JAVA और .NET के लिए बहुत अच्छा खुला स्रोत पुस्तकालय मिला, जिसे mXparser कहा जाता है। मैं सिंटैक्स पर कुछ महसूस करने के लिए कुछ उदाहरण दूंगा, आगे के निर्देशों के लिए कृपया प्रोजेक्ट वेबसाइट (विशेषकर ट्यूटोरियल अनुभाग) पर जाएं।

https://mathparser.org/

https://mathparser.org/mxparser-tutorial/

https://mathparser.org/api/

और कुछ उदाहरण

1 - सरल फरमुला

Expression e = new Expression("( 2 + 3/4 + sin(pi) )/2");
double v = e.calculate()

2 - उपयोगकर्ता परिभाषित तर्क और स्थिरांक

Argument x = new Argument("x = 10");
Constant a = new Constant("a = pi^2");
Expression e = new Expression("cos(a*x)", x, a);
double v = e.calculate()

3 - उपयोगकर्ता परिभाषित कार्य

Function f = new Function("f(x, y, z) = sin(x) + cos(y*z)");
Expression e = new Expression("f(3,2,5)", f);
double v = e.calculate()

4 - गर्भाधान

Expression e = new Expression("sum( i, 1, 100, sin(i) )");
double v = e.calculate()

हाल ही में मिला - यदि आप वाक्यविन्यास को आज़माना चाहते हैं (और उन्नत उपयोग के मामले को देखें) तो आप स्केलर कैलकुलेटर ऐप डाउनलोड कर सकते हैं जो mXparser द्वारा संचालित है।

सादर


अब तक यह सबसे अच्छा गणित पुस्तकालय है; किकस्टार्ट करने के लिए सरल, प्रयोग करने में आसान और विस्तार योग्य। निश्चित रूप से शीर्ष उत्तर होना चाहिए।
ट्राईनेक्विज़ मार्जेज़

यहाँ Maven संस्करण का पता लगाएं ।
izogfif

मैंने पाया कि mXparser अवैध सूत्र की पहचान नहीं कर सकता है, उदाहरण के लिए, '0/0' को '0' के रूप में परिणाम मिलेगा। मैं इस समस्या को कैसे हल कर सकता हूं
ul

बस समाधान मिल गया, एक्सप्रेशन
.setSlientMode

20

यहाँ GitHub पर एक और ओपन सोर्स लाइब्रेरी है जिसका नाम EvalEx है।

जावास्क्रिप्ट इंजन के विपरीत यह पुस्तकालय केवल गणितीय अभिव्यक्तियों के मूल्यांकन में केंद्रित है। इसके अलावा, पुस्तकालय विस्तार योग्य है और बूलियन ऑपरेटरों के साथ-साथ कोष्ठक के उपयोग का समर्थन करता है।


यह ठीक है, लेकिन विफल रहता है जब हम 5 या 10 के गुणकों के मानों को गुणा करने की कोशिश करते हैं, उदाहरण के लिए 65 * 6 परिणाम 3.9E + 2 ...
parth batra

.लेकिन इसे int यानी int output = (int) 65 * 6 पर कास्ट करके इसे ठीक करने का एक तरीका है, अब इसका परिणाम 390 होगा
parth batra

1
स्पष्ट करने के लिए, यह लाइब्रेरी की समस्या नहीं है, बल्कि फ्लोटिंग पॉइंट वैल्यू के रूप में संख्याओं के प्रतिनिधित्व के साथ एक समस्या है।
डेविडबिटनर

यह पुस्तकालय वास्तव में अच्छा है। @ तिराहे पर बल्लेबाजी करने से सभी दशमलव के अंक निकल जाएंगे। इसके बजाय इसका प्रयोग करें: एक्सप्रेशन.वैल () .PlainString ();
einUsername

15

आप बीनशेल दुभाषिया भी आज़मा सकते हैं :

Interpreter interpreter = new Interpreter();
interpreter.eval("result = (7+21*6)/(32-27)");
System.out.println(interpreter.get("result"));

1
क्या आप मुझे बता सकते हैं कि एडनॉइड स्टूडियो में बीनशेल का उपयोग कैसे करें।
हन्नी

1
हन्नी - यह पोस्ट आपको अपने
Androidstudio

14

आप आसानी से भावों का मूल्यांकन कर सकते हैं यदि आपका जावा एप्लिकेशन पहले से ही किसी अन्य JAR का उपयोग किए बिना, डेटाबेस तक पहुंचता है।

कुछ डेटाबेस के लिए आपको डमी टेबल (उदाहरण के लिए, ओरेकल की "दोहरी" तालिका) का उपयोग करने की आवश्यकता होती है और अन्य आपको किसी भी तालिका से "चयन" किए बिना अभिव्यक्ति का मूल्यांकन करने की अनुमति देंगे।

उदाहरण के लिए, Sql Server या Sqlite में

select (((12.10 +12.0))/ 233.0) amount

और ओरेकल में

select (((12.10 +12.0))/ 233.0) amount from dual;

DB का उपयोग करने का लाभ यह है कि आप एक ही समय में कई अभिव्यक्तियों का मूल्यांकन कर सकते हैं। इसके अलावा अधिकांश डीबी आपको अत्यधिक जटिल अभिव्यक्तियों का उपयोग करने की अनुमति देगा और इसमें कई अतिरिक्त कार्य भी होंगे जिन्हें आवश्यक कहा जा सकता है।

हालाँकि, कई एकल अभिव्यक्तियों का व्यक्तिगत रूप से मूल्यांकन करने की आवश्यकता होती है, खासकर जब DB एक नेटवर्क सर्वर पर स्थित होता है, तो प्रदर्शन को नुकसान हो सकता है।

निम्नलिखित एक Sqlite-in-memory डेटाबेस का उपयोग करके प्रदर्शन समस्या को कुछ हद तक संबोधित करता है।

यहाँ जावा में एक पूर्ण काम करने का उदाहरण दिया गया है

Class. forName("org.sqlite.JDBC");
Connection conn = DriverManager.getConnection("jdbc:sqlite::memory:");
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery( "select (1+10)/20.0 amount");
rs.next();
System.out.println(rs.getBigDecimal(1));
stat.close();
conn.close();

बेशक आप एक ही समय में कई गणनाओं को संभालने के लिए उपरोक्त कोड का विस्तार कर सकते हैं।

ResultSet rs = stat.executeQuery( "select (1+10)/20.0 amount, (1+100)/20.0 amount2");

5
SQL इंजेक्शन के लिए नमस्ते कहो!
सायबरज

यह इस बात पर निर्भर करता है कि आप डीबी का उपयोग किस लिए करते हैं। यदि आप सुनिश्चित होना चाहते हैं, तो आप आसानी से एक खाली स्क्लेलाइट डीबी बना सकते हैं, विशेष रूप से मैथ्स मूल्यांकन के लिए।
DAB

4
@ बर्बर यदि आप ऊपर मेरे उदाहरण का उपयोग करते हैं, तो स्क्लाइट स्मृति में एक अस्थायी डीबी बनाएगा। देखें stackoverflow.com/questions/849679/...
डी ए बी

11

यह लेख विभिन्न दृष्टिकोणों पर चर्चा करता है। यहाँ लेख में उल्लिखित 2 मुख्य दृष्टिकोण दिए गए हैं:

अपाचे से JEXL

स्क्रिप्ट के लिए अनुमति देता है कि जावा वस्तुओं के संदर्भ शामिल हैं।

// Create or retrieve a JexlEngine
JexlEngine jexl = new JexlEngine();
// Create an expression object
String jexlExp = "foo.innerFoo.bar()";
Expression e = jexl.createExpression( jexlExp );

// Create a context and add data
JexlContext jctx = new MapContext();
jctx.set("foo", new Foo() );

// Now evaluate the expression, getting the result
Object o = e.evaluate(jctx);

JDK में एम्बेडेड जावास्क्रिप्ट इंजन का उपयोग करें:

private static void jsEvalWithVariable()
{
    List<String> namesList = new ArrayList<String>();
    namesList.add("Jill");
    namesList.add("Bob");
    namesList.add("Laureen");
    namesList.add("Ed");

    ScriptEngineManager mgr = new ScriptEngineManager();
    ScriptEngine jsEngine = mgr.getEngineByName("JavaScript");

    jsEngine.put("namesListKey", namesList);
    System.out.println("Executing in script environment...");
    try
    {
      jsEngine.eval("var x;" +
                    "var names = namesListKey.toArray();" +
                    "for(x in names) {" +
                    "  println(names[x]);" +
                    "}" +
                    "namesListKey.add(\"Dana\");");
    }
    catch (ScriptException ex)
    {
        ex.printStackTrace();
    }
}

4
कृपया लेख की जानकारी को संक्षेप में बताएं, यदि इससे लिंक लिंक टूट गया है।
डीजेकेवर्थ

मैंने लेख से प्रासंगिक बिट्स को शामिल करने के लिए उत्तर को उन्नत किया है
ब्रैड पार्क्स

1
व्यवहार में, JEXL धीमा है (सेम के आत्मनिरीक्षण का उपयोग करता है), मल्टीथ्रेडिंग (वैश्विक कैश) के साथ प्रदर्शन के मुद्दे हैं
निशि

जानकर अच्छा लगा @ निशि! - मेरा उपयोग मामला लाइव वातावरण में चीजों को डीबग करने के लिए था, लेकिन सामान्य तैनात ऐप का हिस्सा नहीं था।
ब्रैड पार्क्स

10

दूसरा तरीका स्प्रिंग एक्सप्रेशन लैंग्वेज या स्पेल का उपयोग करना है जो गणितीय अभिव्यक्तियों के मूल्यांकन के साथ-साथ पूरी तरह से बहुत कुछ करता है इसलिए शायद थोड़ा अधिक हो जाए। इस अभिव्यक्ति पुस्तकालय का उपयोग करने के लिए आपको स्प्रिंग फ्रेमवर्क का उपयोग करने की आवश्यकता नहीं है क्योंकि यह स्टैंड-अलोन है। स्पेल के प्रलेखन से उदाहरणों की नकल करना:

ExpressionParser parser = new SpelExpressionParser();
int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2 
double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); //24.0

और पढ़ें संक्षिप्त स्पेल उदाहरण यहाँ और पूर्ण डॉक्स यहाँ


8

अगर हम इसे लागू करने जा रहे हैं तो हम नीचे दिए गए एल्गोरिदम का उपयोग कर सकते हैं: -

  1. जबकि अभी भी पढ़ने के लिए टोकन हैं,

    १.१ अगला टोकन प्राप्त करें। 1.2 यदि टोकन है:

    1.2.1 एक संख्या: इसे स्टैक मान पर रखें।

    १.२.२ एक चर: इसका मान प्राप्त करें, और मूल्य स्टैक पर धकेलें।

    1.2.3 एक बाईं कोष्ठक: इसे ऑपरेटर स्टैक पर धक्का दें।

    1.2.4 एक सही कोष्ठक:

     1 While the thing on top of the operator stack is not a 
       left parenthesis,
         1 Pop the operator from the operator stack.
         2 Pop the value stack twice, getting two operands.
         3 Apply the operator to the operands, in the correct order.
         4 Push the result onto the value stack.
     2 Pop the left parenthesis from the operator stack, and discard it.

    1.2.5 एक ऑपरेटर (इसे इसे कॉल करें):

     1 While the operator stack is not empty, and the top thing on the
       operator stack has the same or greater precedence as thisOp,
       1 Pop the operator from the operator stack.
       2 Pop the value stack twice, getting two operands.
       3 Apply the operator to the operands, in the correct order.
       4 Push the result onto the value stack.
     2 Push thisOp onto the operator stack.
  2. जबकि ऑपरेटर स्टैक खाली नहीं है, 1 ऑपरेटर स्टैक से ऑपरेटर को पॉप करें। 2 दो ऑपरेंड हो रही वैल्यू स्टैक को दो बार पॉप करें। 3 ऑपरेटर को ऑपरेंड में, सही क्रम में लागू करें। 4 परिणाम स्टैक पर परिणाम पुश करें।

  3. इस बिंदु पर ऑपरेटर स्टैक खाली होना चाहिए, और मान स्टैक में केवल एक मान होना चाहिए, जो अंतिम परिणाम है।


3
यह दिज्क्स्ट्रा शूंटिंग-यार्ड एल्गोरिथ्म का एक अनियोजित प्रदर्शन है । जो प्रशंसा का पात्र है, उसकी प्रशंसा करें।
लोर्ने

7

यह एक और दिलचस्प विकल्प है https://github.com/Shy-Ta/expression-evaluator-demo

उपयोग बहुत सरल है और काम पूरा हो जाता है, उदाहरण के लिए:

  ExpressionsEvaluator evalExpr = ExpressionsFactory.create("2+3*4-6/2");  
  assertEquals(BigDecimal.valueOf(11), evalExpr.eval()); 


4

मुझे लगता है कि आप कभी भी ऐसा करते हैं, इसमें बहुत सारे सशर्त बयान शामिल होते हैं। लेकिन अपने उदाहरणों की तरह एकल संचालन के लिए आप इसे 4 तक सीमित कर सकते हैं यदि कुछ के साथ कथन

String math = "1+4";

if (math.split("+").length == 2) {
    //do calculation
} else if (math.split("-").length == 2) {
    //do calculation
} ...

जब आप "4 + 5 * 6" जैसे कई कार्यों से निपटना चाहते हैं तो यह बहुत अधिक जटिल हो जाता है।

यदि आप एक कैलकुलेटर बनाने की कोशिश कर रहे हैं, तो मैं गणना के प्रत्येक खंड को अलग-अलग (प्रत्येक संख्या या ऑपरेटर) के बजाय एक स्ट्रिंग के रूप में पास करूंगा।


2
जैसे ही आपको कई ऑपरेशन, ऑपरेटर पूर्वता, कोष्ठक से निपटना पड़ता है, यह पूरी तरह से अधिक जटिल हो जाता है ... वास्तव में कुछ भी जो एक वास्तविक अंकगणितीय अभिव्यक्ति की विशेषता है। आप इस तकनीक से शुरुआत नहीं कर सकते।
लोर्ने

4

इसका उत्तर देने में बहुत देर हो गई है लेकिन मुझे जावा में अभिव्यक्ति का मूल्यांकन करने के लिए एक ही स्थिति में आया, यह किसी की मदद कर सकता है

MVELअभिव्यक्ति का क्रम मूल्यांकन करता है, हम Stringइसमें मूल्यांकन करने के लिए एक जावा कोड लिख सकते हैं ।

    String expressionStr = "x+y";
    Map<String, Object> vars = new HashMap<String, Object>();
    vars.put("x", 10);
    vars.put("y", 20);
    ExecutableStatement statement = (ExecutableStatement) MVEL.compileExpression(expressionStr);
    Object result = MVEL.executeExpression(statement, vars);

मैंने परिहास किया और कुछ अतिरिक्त अंकगणितीय कार्यों को भी यहाँ संभाला github.com/mvel/mvel/blob/master/src/test/java/org/mvel2/tests/…
thecodefather

Awsome! इसने मेरा दिन बचाया। धन्यवाद
Sarika.S

4

आपको सिम्जा ढांचे पर एक नज़र पड़ सकती है :

ExprEvaluator util = new ExprEvaluator(); 
IExpr result = util.evaluate("10-40");
System.out.println(result.toString()); // -> "-30" 

ध्यान दें कि निश्चित रूप से अधिक जटिल अभिव्यक्तियों का मूल्यांकन किया जा सकता है:

// D(...) gives the derivative of the function Sin(x)*Cos(x)
IAST function = D(Times(Sin(x), Cos(x)), x);
IExpr result = util.evaluate(function);
// print: Cos(x)^2-Sin(x)^2

4

कोड इंजेक्शन हैंडलिंग के साथ JDK1.6 के जावास्क्रिप्ट इंजन का उपयोग करके निम्नलिखित नमूना कोड का प्रयास करें।

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

public class EvalUtil {
private static ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
public static void main(String[] args) {
    try {
        System.out.println((new EvalUtil()).eval("(((5+5)/2) > 5) || 5 >3 "));
        System.out.println((new EvalUtil()).eval("(((5+5)/2) > 5) || true"));
    } catch (Exception e) {
        e.printStackTrace();
    }
}
public Object eval(String input) throws Exception{
    try {
        if(input.matches(".*[a-zA-Z;~`#$_{}\\[\\]:\\\\;\"',\\.\\?]+.*")) {
            throw new Exception("Invalid expression : " + input );
        }
        return engine.eval(input);
    } catch (Exception e) {
        e.printStackTrace();
        throw e;
    }
 }
}

4

यह वास्तव में @Boann द्वारा दिए गए उत्तर का पूरक है। इसमें एक मामूली बग है, जिसके कारण -4.0 का त्रुटिपूर्ण परिणाम देने के लिए "-2 ^ 2" होता है। उसके लिए समस्या वह बिंदु है जिस पर उसके मूल्यांकन का मूल्यांकन किया जाता है। बस घातांक को parseTerm () के ब्लॉक में ले जाएँ, और आप सभी ठीक हो जाएंगे। नीचे एक नज़र डालें, जो @ Boann का उत्तर थोड़ा संशोधित है। संशोधन टिप्पणियों में है।

public static double eval(final String str) {
    return new Object() {
        int pos = -1, ch;

        void nextChar() {
            ch = (++pos < str.length()) ? str.charAt(pos) : -1;
        }

        boolean eat(int charToEat) {
            while (ch == ' ') nextChar();
            if (ch == charToEat) {
                nextChar();
                return true;
            }
            return false;
        }

        double parse() {
            nextChar();
            double x = parseExpression();
            if (pos < str.length()) throw new RuntimeException("Unexpected: " + (char)ch);
            return x;
        }

        // Grammar:
        // expression = term | expression `+` term | expression `-` term
        // term = factor | term `*` factor | term `/` factor
        // factor = `+` factor | `-` factor | `(` expression `)`
        //        | number | functionName factor | factor `^` factor

        double parseExpression() {
            double x = parseTerm();
            for (;;) {
                if      (eat('+')) x += parseTerm(); // addition
                else if (eat('-')) x -= parseTerm(); // subtraction
                else return x;
            }
        }

        double parseTerm() {
            double x = parseFactor();
            for (;;) {
                if      (eat('*')) x *= parseFactor(); // multiplication
                else if (eat('/')) x /= parseFactor(); // division
                else if (eat('^')) x = Math.pow(x, parseFactor()); //exponentiation -> Moved in to here. So the problem is fixed
                else return x;
            }
        }

        double parseFactor() {
            if (eat('+')) return parseFactor(); // unary plus
            if (eat('-')) return -parseFactor(); // unary minus

            double x;
            int startPos = this.pos;
            if (eat('(')) { // parentheses
                x = parseExpression();
                eat(')');
            } else if ((ch >= '0' && ch <= '9') || ch == '.') { // numbers
                while ((ch >= '0' && ch <= '9') || ch == '.') nextChar();
                x = Double.parseDouble(str.substring(startPos, this.pos));
            } else if (ch >= 'a' && ch <= 'z') { // functions
                while (ch >= 'a' && ch <= 'z') nextChar();
                String func = str.substring(startPos, this.pos);
                x = parseFactor();
                if (func.equals("sqrt")) x = Math.sqrt(x);
                else if (func.equals("sin")) x = Math.sin(Math.toRadians(x));
                else if (func.equals("cos")) x = Math.cos(Math.toRadians(x));
                else if (func.equals("tan")) x = Math.tan(Math.toRadians(x));
                else throw new RuntimeException("Unknown function: " + func);
            } else {
                throw new RuntimeException("Unexpected: " + (char)ch);
            }

            //if (eat('^')) x = Math.pow(x, parseFactor()); // exponentiation -> This is causing a bit of problem

            return x;
        }
    }.parse();
}

-2^2 = -4वास्तव में सामान्य है, और बग नहीं है। यह समूहबद्ध हो जाता है -(2^2)उदाहरण के लिए, डेसमोस पर इसे आज़माएं। आपका कोड वास्तव में कई बग का परिचय देता है। पहला यह है कि ^अब कोई समूह दाएं-बाएं नहीं है। दूसरे शब्दों में, 2^3^2समूह की तरह माना जाता है 2^(3^2)क्योंकि ^सही-सहयोगी है, लेकिन आपके संशोधन इसे समूह की तरह बनाते हैं (2^3)^2। दूसरा यह है कि ^माना जाता है कि इसकी तुलना में उच्च पूर्वता है *और /, लेकिन आपके संशोधन इसे समान मानते हैं। देखें ideone.com/iN2mMa
रेडियोडफ़

तो, आप जो सुझाव दे रहे हैं, वह यह है कि जहां यह नहीं था, वहाँ घातांक को बेहतर रखा गया है?
रोमियो सिएरा

हां, यही मैं सुझाव दे रहा हूं।
रेडियोडफ

4
package ExpressionCalculator.expressioncalculator;

import java.text.DecimalFormat;
import java.util.Scanner;

public class ExpressionCalculator {

private static String addSpaces(String exp){

    //Add space padding to operands.
    //https://regex101.com/r/sJ9gM7/73
    exp = exp.replaceAll("(?<=[0-9()])[\\/]", " / ");
    exp = exp.replaceAll("(?<=[0-9()])[\\^]", " ^ ");
    exp = exp.replaceAll("(?<=[0-9()])[\\*]", " * ");
    exp = exp.replaceAll("(?<=[0-9()])[+]", " + "); 
    exp = exp.replaceAll("(?<=[0-9()])[-]", " - ");

    //Keep replacing double spaces with single spaces until your string is properly formatted
    /*while(exp.indexOf("  ") != -1){
        exp = exp.replace("  ", " ");
     }*/
    exp = exp.replaceAll(" {2,}", " ");

       return exp;
}

public static Double evaluate(String expr){

    DecimalFormat df = new DecimalFormat("#.####");

    //Format the expression properly before performing operations
    String expression = addSpaces(expr);

    try {
        //We will evaluate using rule BDMAS, i.e. brackets, division, power, multiplication, addition and
        //subtraction will be processed in following order
        int indexClose = expression.indexOf(")");
        int indexOpen = -1;
        if (indexClose != -1) {
            String substring = expression.substring(0, indexClose);
            indexOpen = substring.lastIndexOf("(");
            substring = substring.substring(indexOpen + 1).trim();
            if(indexOpen != -1 && indexClose != -1) {
                Double result = evaluate(substring);
                expression = expression.substring(0, indexOpen).trim() + " " + result + " " + expression.substring(indexClose + 1).trim();
                return evaluate(expression.trim());
            }
        }

        String operation = "";
        if(expression.indexOf(" / ") != -1){
            operation = "/";
        }else if(expression.indexOf(" ^ ") != -1){
            operation = "^";
        } else if(expression.indexOf(" * ") != -1){
            operation = "*";
        } else if(expression.indexOf(" + ") != -1){
            operation = "+";
        } else if(expression.indexOf(" - ") != -1){ //Avoid negative numbers
            operation = "-";
        } else{
            return Double.parseDouble(expression);
        }

        int index = expression.indexOf(operation);
        if(index != -1){
            indexOpen = expression.lastIndexOf(" ", index - 2);
            indexOpen = (indexOpen == -1)?0:indexOpen;
            indexClose = expression.indexOf(" ", index + 2);
            indexClose = (indexClose == -1)?expression.length():indexClose;
            if(indexOpen != -1 && indexClose != -1) {
                Double lhs = Double.parseDouble(expression.substring(indexOpen, index));
                Double rhs = Double.parseDouble(expression.substring(index + 2, indexClose));
                Double result = null;
                switch (operation){
                    case "/":
                        //Prevent divide by 0 exception.
                        if(rhs == 0){
                            return null;
                        }
                        result = lhs / rhs;
                        break;
                    case "^":
                        result = Math.pow(lhs, rhs);
                        break;
                    case "*":
                        result = lhs * rhs;
                        break;
                    case "-":
                        result = lhs - rhs;
                        break;
                    case "+":
                        result = lhs + rhs;
                        break;
                    default:
                        break;
                }
                if(indexClose == expression.length()){
                    expression = expression.substring(0, indexOpen) + " " + result + " " + expression.substring(indexClose);
                }else{
                    expression = expression.substring(0, indexOpen) + " " + result + " " + expression.substring(indexClose + 1);
                }
                return Double.valueOf(df.format(evaluate(expression.trim())));
            }
        }
    }catch(Exception exp){
        exp.printStackTrace();
    }
    return 0.0;
}

public static void main(String args[]){

    Scanner scanner = new Scanner(System.in);
    System.out.print("Enter an Mathematical Expression to Evaluate: ");
    String input = scanner.nextLine();
    System.out.println(evaluate(input));
}

}


1
ऑपरेटर पूर्वता, या कई ऑपरेटर, या कोष्ठक संभाल नहीं करता है। प्रयोग नहीं करें।
लोर्न

2

इस जैसे किसी और के बारे में क्या राय है:

String st = "10+3";
int result;
for(int i=0;i<st.length();i++)
{
  if(st.charAt(i)=='+')
  {
    result=Integer.parseInt(st.substring(0, i))+Integer.parseInt(st.substring(i+1, st.length()));
    System.out.print(result);
  }         
}

और तदनुसार हर दूसरे गणितीय ऑपरेटर के लिए इसी तरह की बात करें ।।


9
आपको कुशल गणित अभिव्यक्ति पार्सर लिखने के बारे में पढ़ना चाहिए। इसके लिए एक कंप्यूटर विज्ञान पद्धति है। उदाहरण के लिए, ANTLR पर एक नज़र डालें। यदि आप जो लिखते हैं उसके बारे में अच्छी तरह से सोचते हैं कि आप देखेंगे कि (a + b / -c) * (e / f) जैसी चीजें आपके विचार से काम नहीं करेंगी या कोड सुपर डुपर गंदा और अक्षम होगा।
डैनियल नुरियेव

2

जिक्सर के शंटिंग-यार्ड एल्गोरिथ्म का उपयोग करके किसी भी अभिव्यक्ति स्ट्रिंग को इन्फिक्स नोटेशन में पोस्टफिक्स नोटेशन में परिवर्तित करना संभव है । एल्गोरिथ्म का परिणाम तब पोस्टफिक्स एल्गोरिथ्म के इनपुट के रूप में काम कर सकता है अभिव्यक्ति के परिणाम के साथ ।

मैंने जावा में एक कार्यान्वयन के साथ, इसके बारे में एक लेख लिखा था


2

फिर भी एक अन्य विकल्प: https://github.com/stefanerateein/expressionparser

मैंने इसे लागू करने के लिए दोनों को अनुमति देने के लिए एक सरल लेकिन लचीला विकल्प दिया है:

ऊपर जुड़ा ट्रीबर्स्टल एक कैस डेमो पैकेज का हिस्सा है जो प्रतीकात्मक व्युत्पत्ति करता है। एक बेसिक दुभाषिया उदाहरण भी है और मैंने इसका उपयोग करके टाइपस्क्रिप्ट इंटरप्रेटर का निर्माण करना शुरू कर दिया है।


2

एक जावा वर्ग जो गणितीय अभिव्यक्तियों का मूल्यांकन कर सकता है:

package test;

public class Calculator {

    public static Double calculate(String expression){
        if (expression == null || expression.length() == 0) {
            return null;
        }
        return calc(expression.replace(" ", ""));
    }
    public static Double calc(String expression) {

        if (expression.startsWith("(") && expression.endsWith(")")) {
            return calc(expression.substring(1, expression.length() - 1));
        }
        String[] containerArr = new String[]{expression};
        double leftVal = getNextOperand(containerArr);
        expression = containerArr[0];
        if (expression.length() == 0) {
            return leftVal;
        }
        char operator = expression.charAt(0);
        expression = expression.substring(1);

        while (operator == '*' || operator == '/') {
            containerArr[0] = expression;
            double rightVal = getNextOperand(containerArr);
            expression = containerArr[0];
            if (operator == '*') {
                leftVal = leftVal * rightVal;
            } else {
                leftVal = leftVal / rightVal;
            }
            if (expression.length() > 0) {
                operator = expression.charAt(0);
                expression = expression.substring(1);
            } else {
                return leftVal;
            }
        }
        if (operator == '+') {
            return leftVal + calc(expression);
        } else {
            return leftVal - calc(expression);
        }

    }

    private static double getNextOperand(String[] exp){
        double res;
        if (exp[0].startsWith("(")) {
            int open = 1;
            int i = 1;
            while (open != 0) {
                if (exp[0].charAt(i) == '(') {
                    open++;
                } else if (exp[0].charAt(i) == ')') {
                    open--;
                }
                i++;
            }
            res = calc(exp[0].substring(1, i - 1));
            exp[0] = exp[0].substring(i);
        } else {
            int i = 1;
            if (exp[0].charAt(0) == '-') {
                i++;
            }
            while (exp[0].length() > i && isNumber((int) exp[0].charAt(i))) {
                i++;
            }
            res = Double.parseDouble(exp[0].substring(0, i));
            exp[0] = exp[0].substring(i);
        }
        return res;
    }


    private static boolean isNumber(int c) {
        int zero = (int) '0';
        int nine = (int) '9';
        return (c >= zero && c <= nine) || c =='.';
    }

    public static void main(String[] args) {
        System.out.println(calculate("(((( -6 )))) * 9 * -1"));
        System.out.println(calc("(-5.2+-5*-5*((5/4+2)))"));

    }

}

2
ऑपरेटर की पूर्वता को सही तरीके से नहीं संभालता है। ऐसा करने के मानक तरीके हैं, और यह उनमें से एक नहीं है।
लोर्ने

EJP, क्या आप कृपया यह बता सकते हैं कि ऑपरेटर की प्राथमिकता में कोई समस्या है? मैं इस तथ्य पर पूरी तरह सहमत हूं कि यह ऐसा करने का मानक तरीका नहीं है। पिछली पोस्टों में पहले से ही मानक तरीके बताए गए थे, विचार यह था कि इसे करने का दूसरा तरीका दिखाया जाए।
Efi G

2

जावास्क्रिप्ट को चलाने के लिए RHINO या NASHORN जैसी बाहरी लाइब्रेरी का उपयोग किया जा सकता है। और जावास्क्रिप्ट स्ट्रिंग को पार किए बिना सरल सूत्र का मूल्यांकन कर सकता है। कोई प्रदर्शन प्रभाव और साथ ही अगर कोड अच्छी तरह से लिखा गया है। नीचे राइनो के साथ एक उदाहरण दिया गया है -

public class RhinoApp {
    private String simpleAdd = "(12+13+2-2)*2+(12+13+2-2)*2";

public void runJavaScript() {
    Context jsCx = Context.enter();
    Context.getCurrentContext().setOptimizationLevel(-1);
    ScriptableObject scope = jsCx.initStandardObjects();
    Object result = jsCx.evaluateString(scope, simpleAdd , "formula", 0, null);
    Context.exit();
    System.out.println(result);
}

2
import java.util.*;

public class check { 
   int ans;
   String str="7 + 5";
   StringTokenizer st=new StringTokenizer(str);

   int v1=Integer.parseInt(st.nextToken());
   String op=st.nextToken();
   int v2=Integer.parseInt(st.nextToken());

   if(op.equals("+")) { ans= v1 + v2; }
   if(op.equals("-")) { ans= v1 - v2; }
   //.........
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.