अंकगणितीय संचालन के लिए सरल पार्सर

अध्ययन के लिए, अंकगणितीय संक्रियाओं का एक पार्सर लिखना आवश्यक था, जो न केवल सबसे सरल परिचालनों की गणना कर सकता है, बल्कि कोष्ठक और कार्यों के साथ भी काम कर सकता है।

मुझे इंटरनेट पर मेरे लिए तैयार और उपयुक्त समाधान नहीं मिला (कुछ बहुत जटिल थे, अन्य मेरे कार्य की शर्तों को पूरी तरह से संतुष्ट नहीं कर रहे थे)। थोड़े दुःख के बाद, मैंने स्वयं समस्या का समाधान करना शुरू कर दिया और अब मैं अपने मूल **** कोड को दुनिया के साथ मूल समाधान के साथ साझा करना चाहता हूं।

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

(2+2)((22)+((22)(22))

फ़ंक्शंस के साथ सटीक एक ही कहानी - एक फ़ंक्शन के मापदंडों में अन्य फ़ंक्शन और यहां तक ​​कि पूरे भाव भी हो सकते हैं।

sqrt(22;(4;2))

लेकिन उस पर और बाद में। पहले आपको संपूर्ण अभिव्यक्ति को पार्स करने की आवश्यकता है। ध्यान दें कि हम या तो एक ब्रैकेट, या एक संख्या, या एक ऑपरेंड (+, -, *, /, ^), या एक फ़ंक्शन, या एक स्थिरांक का सामना कर सकते हैं।

इस पूरी चीज़ के लिए सूची बनाएँ:

public static List<string> digits = new List<string>() { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "," }; public static List<string> operands = new List<string>() {"^", "/", "*", "+", "-"}; public static List<string> functions = new List<string>() { "sqrt", "sin", "cos", "log", "abs"}; public static List<string> brackets = new List<string>() { "(", ")" }; public static List<string> constants = new List<string>() { "pi" }; public static Dictionary<string, string> constantsValues = new Dictionary<string, string>() { ["pi"] = "3,14159265359" }; 

और हम प्रत्येक चरित्र को बारी-बारी से जाँचेंगे। स्वाभाविक रूप से, यदि हम संख्या के बाद "+" या "-" संकेत से मिले, तो यह संकेत क्रमशः सकारात्मक या नकारात्मक संख्या को इंगित करता है।

 for (int i = 0; i < expression.Length; i++) { if (brackets.Contains(expression[i].ToString())){ if (lastSymbol != ""){ symbols.Add(lastSymbol); lastSymbol = ""; } //  ,      symbols.Add(expression[i].ToString()); //    -        } else if (digits.Contains(expression[i].ToString()) || (expression[i] == ',' && lastSymbol.IndexOf(",") == -1)){ lastSymbol += expression[i]; } //    -     ,     else if(operands.Contains(expression[i].ToString())) { if (lastSymbol != ""){ symbols.Add(lastSymbol); lastSymbol = ""; } if (symbols.Count > 0 && operands.Contains(symbols[symbols.Count - 1]) || symbols.Count == 0) { string number = ""; switch (expression[i].ToString()) { case "-": number += "-"; break; case "+": number += "+"; break; } i++; while (i < expression.Length && digits.Contains(expression[i].ToString())){ number += expression[i]; i++; } symbols.Add(number); i--; } //   "-"  "+",   -        else symbols.Add(expression[i].ToString()); }else{ lastFunction += expression[i].ToString().ToLower(); //       =>      if (constants.Contains(lastFunction)) { symbols.Add(constantsValues[lastFunction]); lastFunction = ""; } //     -        else if (functions.Contains(lastFunction)) { int functionStart = i + 1; //    int functionEnd = 0; int bracketsSum = 1; for (int j = functionStart + 1; j < expression.Length; j++) { if (expression[j].ToString() == "(") bracketsSum++; if (expression[j].ToString() == ")") bracketsSum--; if (bracketsSum == 0) { functionEnd = j; i = functionEnd; break; } } //   .    - ,      char[] buffer = new char[functionEnd - functionStart - 1]; expression.CopyTo(functionStart + 1, buffer, 0, functionEnd - functionStart - 1); string functionParametrs = new string(buffer); if (lastFunction == "sqrt"){ var parametrs = GetParametrs(functionParametrs); symbols.Add(Math.Pow(CalculateExpression(parametrs[0]), 1 / CalculateExpression(parametrs[1])).ToString()); } if (lastFunction == "log"){ var parametrs = GetParametrs(functionParametrs); symbols.Add(Math.Log(CalculateExpression(parametrs[0]), CalculateExpression(parametrs[1])).ToString()); } if (lastFunction == "sin") symbols.Add(Math.Sin(CalculateExpression(functionParametrs)).ToString()); if (lastFunction == "cos") symbols.Add(Math.Cos(CalculateExpression(functionParametrs)).ToString()); if (lastFunction == "abs") symbols.Add(Math.Abs(CalculateExpression(functionParametrs)).ToString()); //    lastFunction = ""; } } } if (lastSymbol != ""){ symbols.Add(lastSymbol); lastSymbol = ""; } //     ,       

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

sqrt(22;(4;2))

 public static List<string> GetParametrs(string functionParametrs){ int bracketsSum = 0; int functionEnd = 0; for (int j = 0; j < functionParametrs.Length; j++){ if (functionParametrs[j].ToString() == "(") bracketsSum++; if (functionParametrs[j].ToString() == ")") bracketsSum--; if (functionParametrs[j].ToString() == ";" && bracketsSum == 0){ functionEnd = j; break; } } var buffer = new char[functionEnd]; functionParametrs.CopyTo(0, buffer, 0, functionEnd); string firstParametr = new string(buffer); buffer = new char[functionParametrs.Length - functionEnd - 1]; functionParametrs.CopyTo(functionEnd + 1, buffer, 0, functionParametrs.Length - functionEnd - 1); string secondParametr = new string(buffer); return ( new List<string>() { firstParametr, secondParametr } ); } 

तो, अभिव्यक्ति को छोटे लोगों में विभाजित किया गया है, और इसके अलावा, कार्यों के मूल्यों को पहले से ही गणना और मुख्य अभिव्यक्ति में संख्याओं के रूप में प्रतिस्थापित किया गया है।

ब्रैकेट को उसी सिद्धांत के अनुसार संसाधित किया जा सकता है - तुरंत उनकी गणना करें और उन्हें संख्याओं के रूप में प्रतिस्थापित करें:

 while (symbols.Contains("(")) { int bracketsStart = 0; int bracketsEnd = 0; int bracketsSum = 0; for (int i = 0; i < symbols.Count; i++) { if (symbols[i] == "(") { bracketsStart = i; bracketsSum = 1; break; } } for (int i = bracketsStart + 1; i < symbols.Count; i++) { if (symbols[i] == "(") bracketsSum++; if (symbols[i] == ")") bracketsSum--; if (bracketsSum == 0) { bracketsEnd = i; break; } } string bracketsExpression = ""; for (int i = bracketsStart + 1; i < bracketsEnd; i++) bracketsExpression += symbols[i]; symbols[bracketsStart] = CalculateExpression(bracketsExpression).ToString(); symbols.RemoveRange(bracketsStart + 1, bracketsEnd - bracketsStart); } 

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

कार्यक्रम का मुख्य भाग लिखा है। यह साधारण अंकगणितीय अभिव्यक्तियों की गणना को लागू करने के लिए बनी हुई है। कार्यों के क्रम के बारे में चिंता न करने के लिए, मैंने पोलिश नोटेशन में अभिव्यक्ति लिखने का फैसला किया:

 foreach(var j in operands){ //           operands var flagO = true; while (flagO){ flagO = false; for (int i = 0; i < symbols.Count; i++){ if (symbols[i] == j){ symbols[i - 1] = symbols[i - 1] + " " + symbols[i + 1] + " " + j; symbols.RemoveRange(i, 2); flagO = true; break; } } } } 

और अंत में, स्टैक के साथ, हम अभिव्यक्ति के मूल्य की गणना करते हैं:

 List<string> result = new List<string>(); string[] temp = symbols[0].Split(' '); for (int i = 0; i < temp.Length; i++) { if (operands.Contains(temp[i])) { if (temp[i] == "^") { result[result.Count - 2] = Math.Pow(double.Parse(result[result.Count - 2]), double.Parse(result[result.Count - 1])).ToString(); result.RemoveRange(result.Count - 1, 1); } if (temp[i] == "+") { result[result.Count - 2] = (double.Parse(result[result.Count - 2]) + double.Parse(result[result.Count - 1])).ToString(); result.RemoveRange(result.Count - 1, 1); } if (temp[i] == "-") { result[result.Count - 2] = (double.Parse(result[result.Count - 2]) - double.Parse(result[result.Count - 1])).ToString(); result.RemoveRange(result.Count - 1, 1); } if (temp[i] == "*") { result[result.Count - 2] = (double.Parse(result[result.Count - 2]) * double.Parse(result[result.Count - 1])).ToString(); result.RemoveRange(result.Count - 1, 1); } if (temp[i] == "/") { result[result.Count - 2] = (double.Parse(result[result.Count - 2]) / double.Parse(result[result.Count - 1])).ToString(); result.RemoveRange(result.Count - 1, 1); } } else result.Add(temp[i]); } 

यदि सब कुछ ठीक रहा, तो परिणाम में परिणाम होगा [को ०]।

→ GitHub को पूरे कोड के साथ लिंक करें

Source: https://habr.com/ru/post/hi439966/


All Articles