動態地產生使用者輸入的函數運算式(C#)

來源:互聯網
上載者:User
我在一篇隨筆“畫函數圖形的C#程式,兼論一個病態函數”中提到:

這個畫函數圖形的C#程式有一個嚴重的缺點,就是函數運算式是直接寫的來源程式中的,不能象SciLab和Matlab那樣互動式地輸入。不知道用 System.Reflection.Emit.ILGenerator 類能不能動態地產生使用者輸入的函數運算式?

“空間/IV”在該隨筆的評論中指出:

關於動態地產生使用者輸入的函數運算式, 看看下面這個文章說不定有協助:
http://community.csdn.net/Expert/topic/4169/4169185.xml

經研究,我寫了一個動態地產生使用者輸入的函數運算式的類(class Expression),運算式使用 C# 文法,可帶一個的自變數(x),其自變數和值均為“double”類型。下面是測試程式的運行結果:

C> ExpressionTest
Usage: ExpressionTest expression [ parameters ... ]

 

C> ExpressionTest Math.PI*Math.E 0
f(x): Math.PI*Math.E
f(0) = 8.53973422267357

C> ExpressionTest Math.Pow(2,x) 0 10 49 50 1024 -1 -1024
f(x): Math.Pow(2,x)
f(0) = 1
f(10) = 1024
f(49) = 562949953421312
f(50) = 1.12589990684262E+15
f(1024) = 正無窮大
f(-1) = 0.5
f(-1024) = 5.562684646268E-309

C> ExpressionTest "double u = Math.PI - x; double pi2 = Math.PI * Math.PI; return 3 * x * x + Math.Log(u * u) / pi2 / pi2 + 1;" 3.13 3.14 3.15 3.16 3.1416
f(x): double u = Math.PI - x; double pi2 = Math.PI * Math.PI; return 3 * x * x + Math.Log(u * u) / pi2 / pi2 + 1;
f(3.13) = 30.2991811562164
f(3.14) = 30.44652582187
f(3.15) = 30.6693849404716
f(3.16) = 30.8747746902426
f(3.1416) = 30.3662371931734

其中最後一個例子就是我在隨筆“畫函數圖形的C#程式,兼論一個病態函數”的下列函數的計算結果:

實際上這個病態函數是《C數值演算法(第二版)》第三章“插入法和外推法”中提到的:

---------------------------------------------------------------------------
可以很容易地構造一些病態函數使插入法失敗。例如,考慮函數 
f(x) = 3 * x2 + π-4 * ln[(π-x)2] + 1
它除了 x = π 之外都有定義,而 x = π 時無定義,其它情況,值有正有負。而這函數在任何基於數值 x = 3.13, 3.14, 3.15, 3.16 的插值法,都肯定在 x = 3.1416 處得到一個錯誤的解,儘管通過這五個點所畫的曲線確實相當平滑!(用計算機試試看。)
---------------------------------------------------------------------------

可以看出,而這函數在任何基於數值 x = 3.13, 3.14, 3.15, 3.16 的插值法,在 x = 3.1416 處得到的解肯定在 30.44652582187 和 30.6693849404716 之間,但實際的解應該是 30.3662371931734,所以說作者斷言在該處肯定會得到一個錯誤的解。
下面就是來源程式: // ExpressionTest.cs - 動態產生數學運算式並計算其值的測試程式
// 編譯方法: csc ExpressionTest.cs Expression.cs

using System;
using Skyiv.Util;

namespace Skyiv.Test
{
  class ExpressionTest
  {
    static void Main(string [] args)
    {
      try
      {
        if (args.Length > 0)
        {
          Console.WriteLine("f(x): {0}", args[0]);
          Expression expression = new Expression(args[0]);
          for (int i = 1; i < args.Length; i++)
          {
            double x = double.Parse(args[i]);
            Console.WriteLine("f({0}) = {1}", x, expression.Compute(x));
          }
        }
        else Console.WriteLine("Usage: ExpressionTest expression [ parameters  ]");
      }
      catch (Exception ex)
      {
        Console.WriteLine("錯誤: " + ex.Message);
      }
    }
  }
}
// Expression.cs - 動態產生數學運算式並計算其值
// 運算式使用 C# 文法,可帶一個的自變數(x)。
// 運算式的自變數和值均為(double)類型。
// 使用舉例:
//   Expression expression = new Expression("Math.Sin(x)");
//   Console.WriteLine(expression.Compute(Math.PI / 2));
//   expression = new Expression("double u = Math.PI - x;" +
//     "double pi2 = Math.PI * Math.PI;" +
//     "return 3 * x * x + Math.Log(u * u) / pi2 / pi2 + 1;");
//   Console.WriteLine(expression.Compute(0));

using System;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using System.Reflection;
using System.Text;

namespace Skyiv.Util
{
  sealed class Expression
  {
    object instance;
    MethodInfo method;
    
    public Expression(string expression)
    {  
      if (expression.IndexOf("return") < 0) expression = "return " + expression + ";";
      string className = "Expression";
      string methodName = "Compute";
      CompilerParameters p = new CompilerParameters();
      p.GenerateInMemory = true;
      CompilerResults cr = new CSharpCodeProvider().CompileAssemblyFromSource(p, string.
        Format("using System;sealed class {0}{{public double {1}(double x){{{2}}}}}",
        className, methodName, expression));
      if(cr.Errors.Count > 0)
      {
        string msg = "Expression(\"" + expression + "\"): \n";
        foreach (CompilerError err in cr.Errors) msg += err.ToString() + "\n";
        throw new Exception(msg);
      }
      instance = cr.CompiledAssembly.CreateInstance(className);
      method = instance.GetType().GetMethod(methodName);
    }
    
    public double Compute(double x)
    {
      return (double)method.Invoke(instance, new object [] { x });
    }
  }
}

在這裡向 CSDN 論壇的“LoveCherry(論成敗,人生豪邁;大不了,重頭再來!^_^) ”表示感謝,我的程式就是在他的程式的基礎上發展而來的。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.