Pengurai sederhana untuk operasi aritmatika

Untuk studi, perlu untuk menulis parser operasi aritmatika, yang bisa menghitung tidak hanya operasi yang paling sederhana, tetapi juga bekerja dengan tanda kurung dan fungsi.

Saya tidak menemukan solusi yang siap pakai dan cocok untuk saya di Internet (beberapa terlalu rumit, yang lain tidak sepenuhnya memuaskan kondisi tugas saya). Setelah sedikit kesedihan, saya mulai memecahkan masalah sendiri dan sekarang saya ingin membagikan kode asli saya dengan solusi asli dengan dunia.

Masalah pertama yang saya temui adalah tanda kurung. Tidak hanya harus dieksekusi terlebih dahulu, jadi kurung juga bisa ada di dalamnya. Dan sebagainya.

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

Kisah yang persis sama dengan fungsi - dalam parameter fungsi mungkin ada fungsi lain dan bahkan seluruh ekspresi.

sqrt(22;log(4;2))

Tetapi lebih lanjut tentang itu nanti. Pertama, Anda perlu menguraikan seluruh ekspresi. Perhatikan bahwa kita mungkin menemukan braket, atau angka, atau operan (+, -, *, /, ^), atau fungsi, atau konstanta.

Buat daftar untuk semua ini:

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

Dan kami akan memeriksa setiap karakter secara bergantian. Secara alami, jika kita memenuhi tanda "+" atau "-" bukan setelah angka, maka tanda ini masing-masing menunjukkan angka positif atau negatif.

 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 = ""; } //     ,       

Pada contoh, fungsi GetParametrs dilewati. Ini diperlukan untuk kasus-kasus ketika suatu fungsi memiliki 2 parameter. Faktanya adalah bahwa Anda tidak dapat membuat Split sederhana. Kami dapat memiliki ungkapan ini:

sqrt(22;log(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 } ); } 

Jadi, ekspresi dibagi menjadi yang lebih kecil, dan di samping itu, nilai-nilai fungsi sudah dihitung dan diganti menjadi ekspresi utama sebagai angka.

Kurung dapat diproses sesuai dengan prinsip yang sama - segera hitung dan gantilah dengan angka:

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

Menemukan indeks braket penutup lagi sedikit lebih rumit. Anda tidak bisa hanya mengambil braket penutupan berikutnya. Ini tidak akan berfungsi untuk tanda kurung bersarang.

Bagian utama dari program ini ditulis. Tetap menerapkan perhitungan ekspresi aritmatika biasa. Agar tidak khawatir tentang urutan tindakan, saya memutuskan untuk menuliskan ekspresi dalam notasi Polandia:

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

Dan akhirnya, dengan tumpukan, kami menghitung nilai ekspresi:

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

Jika semuanya berjalan dengan baik, hasilnya akan menjadi hasil [0].

Tautan ke GitHub dengan kode lengkap

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


All Articles