2

重複の可能性:
C#-数式の解析
C#、ユーザー定義の数式

方程式は、加算、減算、乗算、除算の演算子のみを使用し、角かっこは使用しません。そんなに難しいとは思いませんでしたが、いろいろなことを試したり、いろいろなアイデアを書いたりしながら、何時間も考えていました。

それらの文字のそれぞれで文字列を分割して出力で何かを行うか、文字列を文字ごとにループして何かを思い付く方法があるかもしれないと思いましたが、私は十分に賢くないと思います。

とにかく困っているので、他の人のアイデアを聞いてみたいです。私が見ている古いスレッドで誰もが提案しているような、ある種のサードパーティライブラリを使用したくありません。

4

3 に答える 3

7

このような単純な方程式の場合、分割と2つのループを使用して実装できます。

このような文字列の場合:"4+5*6/2-8"

演算子を分割して、結果に保持します。

"4", "+", "5", "*", "6", "/", "2", "-", "8"

演算子をループして乗算と除算を計算し、結果をリストに戻します。

"4", "+", "30", "/", "2", "-", "8"
"4", "+", "15", "-", "8"

演算子をもう一度ループして、今度は加算と減算を計算します。

"19", "-", "8"
"11"
于 2012-11-04T23:08:19.907 に答える
2

これを行う最も簡単な方法は、JITコンパイラを利用して計算を評価することです。サントはそれが何のためにあるのかです。Math.Acos(4)のようなコードを式に渡したり、使用しているオブジェクトに関数Acosを「作成」して、ユーザーが数学について心配する必要がないようにすることもできます。プレフィックス。

string code = string.Format  // Note: Use "{{" to denote a single "{" 
( 
   "public static class Func{{ public static Acos(double d) { return Math.ACos(d); }
                               public static int func(){{ return {0};}}}}", expression 
);

また、他の関数が必要な場合は、追加の名前空間を含めることができますが、追加の関数がない場合、コードは次のようになります。

using System; 
using System.Reflection; 
using System.CodeDom.Compiler; 

using Microsoft.CSharp; 

class Program 
{ 
   static void Main() 
   { 
      TestExpression("2+1-(3*2)+8/2"); 
      TestExpression("1*2*3*4*5*6"); 
      TestExpression("Invalid expression"); 
   } 

   static void TestExpression(string expression) 
   { 
      try 
      { 
         int result = EvaluateExpression(expression); 
         Console.WriteLine("'" + expression + "' = " + result); 
      } 
      catch (Exception) 
      { 
         Console.WriteLine("Expression is invalid: '" + expression + "'"); 
      } 
    } 

    public static int EvaluateExpression(string expression) 
    { 
      string code = string.Format  // Note: Use "{{" to denote a single "{" 
      ( 
         "public static class Func{{ public static int func(){{ return {0};}}}}", expression 
      ); 

      CompilerResults compilerResults = CompileScript(code); 

      if (compilerResults.Errors.HasErrors) 
      { 
         throw new InvalidOperationException("Expression has a syntax error."); 
      } 

      Assembly assembly = compilerResults.CompiledAssembly; 
      MethodInfo method = assembly.GetType("Func").GetMethod("func"); 

      return (int)method.Invoke(null, null); 
   } 

   public static CompilerResults CompileScript(string source) 
   { 
      CompilerParameters parms = new CompilerParameters(); 

      parms.GenerateExecutable = false; 
      parms.GenerateInMemory = true; 
      parms.IncludeDebugInformation = false; 

      CodeDomProvider compiler = CSharpCodeProvider.CreateProvider("CSharp"); 

      return compiler.CompileAssemblyFromSource(parms, source); 
   } 
} 

答えはhttp://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/abff98e3-93fe-44fa-bfd4-fcfe297dbc43/からコピーされました。自分でコードを書くのが好きではなかったので、Matthewに感謝します。ワトソン私はする必要はありませんでした。

于 2012-11-04T23:21:16.603 に答える
0

コメントで述べられているように、私は再帰下降構文解析を好みます。これは、リンクされたウィキペディアの記事にあるCの例のC#での非常に迅速な部分的な適応です。

単純な再帰下降は、操車場法よりも読みやすく(再帰下降関数がEBNF非終端記号の定義とどのように密接に一致するかに注意してください)、より拡張性があります。以下は、括弧または「外部」機能を可能にするために簡単に適合させることができます。

より堅牢な実装は、実際にはシンボルクラスをサポートし、無効な文法をより適切に処理します。繰り返しになりますが、このような再帰下降構文解析の設定を追加するのは簡単です。入力のトークン化(読み取り:文字列の分割と数値のdoubleへの変換)は、読者の練習問題として残されています。

class RecDec {
    St x; // ugly shared state, it's a quick example
    public double eval (params object[] tokens) {
        x = new St(tokens);
        return expression();
    }
    double expression() {
        double res = term();
        string accepted;
        while ((accepted = x.acceptOp(new [] {"+", "-"})) != null) {
            res = accepted == "+"
                ? res + term()
                : res - term();
        }
        return res;
    }
    double term() {
        double res = factor();
        string accepted;
        while ((accepted = x.acceptOp(new [] {"*", "/"})) != null) {
            res = accepted == "*"
                ? res * factor();
                : res / factor();
        }
        return res;
    }
    double factor() {
        var val = x.acceptVal();
        if (val == null) {
            throw new Exception(x.ToString());
        }
        return (double)val;
    }
}

「状態」/トークンフィーダークラス:

class St {
    IEnumerable<object> src;
    public St (IEnumerable<object> src) {
        this.src = src;
    }
    public object acceptVal () {
        var first = src.FirstOrDefault();
        if (first is double) {
            src = src.Skip(1);
            return first;
        } else {
            return null;
        }
    }
    public string acceptOp (params string[] syms) {
        var first = src.FirstOrDefault();
        if (syms.Contains(first)) {
            src = src.Skip(1);
            return (string)first;
        } else {
            return null;
        }
    }
    public override string ToString () {
        return "[" + string.Join(",", src.ToArray()) + "]";
    }
}

そして使用法(LINQPad拡張メソッドです。必要に応じて戻り値を使用Dumpしてください):eval

void Main()
{
    var rd = new RecDec();
    // Use results - i.e. Remove Dump - if not on LINQPad
    rd.eval(1d, "+", 2d).Dump();
    rd.eval(2d, "*", 1d, "+", 2d, "*", 9d, "/", 4d).Dump();
}
于 2012-11-05T00:13:39.803 に答える