基于C#語(yǔ)言的可編程表達(dá)式計(jì)算器設(shè)計(jì)

2010-08-28 10:49:35來(lái)源:西部e網(wǎng)作者:

1. 說(shuō)明:

   先看看我們的成果:
p_cal_main.bmp
 
   網(wǎng)上的表達(dá)式計(jì)算器有很多,但這次我們來(lái)點(diǎn),不同的,有兩點(diǎn):
   1)用戶(hù)可以編程以擴(kuò)充計(jì)算器的函數(shù)
     即右下角那個(gè)“添加函數(shù)按鈕”
     比如,我們想增加一個(gè)"Factarial"階乘函數(shù),我們可以“添加函數(shù)”
p_cal_addFun.bmp
 
然后編輯我們的函數(shù)
p_cal_edit.bmp
 
  最后“生成”即可,主窗口上會(huì)自動(dòng)添加一個(gè)"Factarial"按鈕,然后就可以使用了(該函數(shù)將一直存在,除非你手動(dòng)刪除它)
  其實(shí),主窗口上的所有函數(shù)按鈕都是這樣生成的

   2)我們對(duì)表達(dá)式的計(jì)算將擺脫傳統(tǒng)的觀點(diǎn)(即傳統(tǒng)的對(duì)表達(dá)式進(jìn)行詞法分析,語(yǔ)法分析等等),在編寫(xiě)我們的計(jì)算器的代碼中,不會(huì)有任何的詞法分析、語(yǔ)法分析、后綴表達(dá)式轉(zhuǎn)換等
   等。
     啟發(fā)來(lái)自于這里:
     假設(shè)有一個(gè)函數(shù)F
     double F()
     {
         double r = 3*4.5+sin(50);
         return r;
     }

     那么,我們就可以 Console.WriteLine("{0}",F());    
     我們計(jì)算了3*4.5+sin(50),但我們有進(jìn)行麻煩的詞法分析與語(yǔ)法分析嗎?沒(méi)有,誰(shuí)幫我們做了,編譯器,ok,關(guān)鍵就在這:如果計(jì)算器用戶(hù)在計(jì)算器主窗口上輸入表達(dá)式
     3*4.5+sin(50),我們負(fù)責(zé)把它傳給F中的r,然后我們?cè)侔袴的返回值輸出到用戶(hù)界面上就OK了,我們要做的就這些。
    

2 計(jì)算器上的函數(shù)
  2.1 函數(shù)按鈕對(duì)應(yīng)的函數(shù)在哪里?
      在Functions.dll中,不要指望這我微軟給我們的,因?yàn)楹瘮?shù)是用戶(hù)編寫(xiě)的,我們也不知道用戶(hù)會(huì)編寫(xiě)什么樣的函數(shù),他甚至可以編寫(xiě)一個(gè)播放音樂(lè)的函數(shù)(呵呵,我們的計(jì)算器
完全可以做到這點(diǎn),不過(guò)似乎對(duì)于一個(gè)計(jì)算器來(lái)說(shuō)這有些過(guò)分了)
  2.2 誰(shuí)來(lái)負(fù)責(zé)Functions.dll的生成?
      不是.net2003,不是.net2005,是我們的計(jì)算器
      要在用戶(hù)自定義函數(shù)的時(shí)候?qū)⒑瘮?shù)打包到Functions.dll中,就涉及到一個(gè)編譯問(wèn)題,我們將負(fù)責(zé)這一工作
  2.3 誰(shuí)給了我們編譯的權(quán)利?
      System.CodeDom.Compiler和Microsoft.CSharp名字空間中有我們的答案(請(qǐng)參見(jiàn)Msdn)

      我們的Functions.dll打包成功以后,我們就可以在計(jì)算表達(dá)式是調(diào)用其中的函數(shù)了


3 表達(dá)式的計(jì)算
   3.1 如何計(jì)算?
       在前面計(jì)算 3*4.5+sin(50)的例子中,已經(jīng)說(shuō)過(guò),我們將利用編譯器幫我們計(jì)算,我們僅僅像Console.WriteLine("{0}",F())一樣簡(jiǎn)單地將函數(shù)F的返回值輸出給用戶(hù)(F的返回值就是表達(dá)式的值):
     double F()
     {
         double exp = 3*4.5+sin(50);//假設(shè)3*4.5+sin(50)就是用戶(hù)輸入的表達(dá)式
         return exp;
     }
   3.2 F()從哪里來(lái)?到哪里去?
   F()肯定不是我們事先寫(xiě)好的,因?yàn)槠渲械膃xp的初始表達(dá)式是用戶(hù)來(lái)寫(xiě)的
   我們將這樣來(lái)解決這一問(wèn)題:在用戶(hù)輸入表達(dá)式以后動(dòng)態(tài)地生成函數(shù)F()的代碼 -> 將該代碼編譯 -> 調(diào)用編譯成功后的程序集中的該函數(shù)
        /// <summary>
        /// 將數(shù)學(xué)表達(dá)式轉(zhuǎn)化為C#程序
        /// </summary>
        /// <param name="express">用戶(hù)輸入的數(shù)學(xué)表達(dá)式</param>
        /// <returns>返回C#程序代碼</returns>
        public static string TranslateToCSharp(string express)
        {
            string s = "";
            if (File.Exists(Function.GetPathOfFunctionDll()))
            {
                s += "using "+Function.FunctionNameSpace + ";\n";
            }
            
            s +=
             "using System;\n" +
             "namespace ComputeUnit\n" +
             "{\n" +
                "public class Compute\n" +
                "{\n" +
                "   public static double GetResult()\n" +
                "   {\n" +
                "      return "+  TranslateToCSharpExpress(express) + ";\n"+
                "   }\n" +
                "}" +
            "}\n";

            return s;
        }
     這里的GetResult()函數(shù)也就是我們所說(shuō)的函數(shù)F()
      
      3.3 誰(shuí)幫我們?nèi)〉糜?jì)算結(jié)果?
          反射!
         
          假設(shè)用戶(hù)輸入了表達(dá)式express,我們將如同下面的代碼所敘述的那樣計(jì)算它

          string source = TranslateUnit.TranslateToCSharp(express);

            //這里加載了函數(shù)dll
            string[] dlls = new string[1];
            dlls[0] = Function.GetPathOfFunctionDll(); //這里加載了前面所說(shuō)的Functions.dll
             //編譯
            CompilerResults results = CompilerUnit.Compile(source, false,true, dlls,null);

            //重要:利用反射獲取計(jì)算結(jié)果
            if (results.Errors.Count == 0)
            {
                Assembly ass = results.CompiledAssembly;

                Type tp = ass.GetType("ComputeUnit.Compute");//ass.GetType("MyNamespace.MyClass");

                // 獲取方法
                MethodInfo mi = tp.GetMethod("GetResult");//tp.GetMethod("MyMethodl");

                // 創(chuàng)建實(shí)例
                Object obj = System.Activator.CreateInstance(tp);

                //執(zhí)行方法
                try
                {
                    object res = mi.Invoke(obj, null);
                    this.Output(res.ToString(), false, Color.Blue); //將計(jì)算結(jié)果輸出給用戶(hù)

                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.ToString());
                }


以上僅僅簡(jiǎn)要敘述了此程序的核心問(wèn)題,除此之外還有許許多多的細(xì)節(jié)問(wèn)題,如果需要了解更多,你可以下載此程序的源代碼http://www.cnblogs.com/Files/zhouyinhui/ProgramCalculator.rar,里面有詳細(xì)的注釋?zhuān)騟mail:yinhui_zhou@yahoo.com.cn聯(lián)系我

關(guān)鍵詞:C#

贊助商鏈接: