Makale Özeti

Geliştirdiğimiz her uygulamada mutlaka bir matematiksel işlem yapılmaktadır. Ancak bazı özel durumlarda bu matematiksel işlemi temsil eden formüllerin veritabanında tutulması veya yapılacak olan matematiksel işlemin string olarak oluşturulması gibi durumlar söz konusudur. İşte bu durumlarda yardımımıza yetişecek olan bir metodu nasıl yazacağımızdan bu makalede bahsedeceğim. Örneklerle açıklamak gerekirse metodumuz 3+5 gibi bir değeri string olarak verdiğinzde size 8 sonucunu döndürecektir. Metod işlemleri /,*,-,+ işlem sırasına göre yapmakta ve parantez önceliğine dikkat etmektedir. Bunun yanı sıra 3A+5B gibi bir değeri string olarak metoda verdiğiniz ve A ile B nin değerini parametrik olarak tanımladığınız durumda ise A ve B parametrelerinin yerine değerlerini koyarak işlem yapabilecektir. Metodun cevap verdiği işlem karmaşıklığını "3B+120/((2+3(3-1))*5A)" olarak örneklendirebiliriz.

Makale

         Merhabalar,

         Geliştirdiğimiz her uygulamada mutlaka bir matematiksel işlem yapılmaktadır. Ancak bazı özel durumlarda bu matematiksel işlemi temsil eden formüllerin veritabanında tutulması veya yapılacak olan matematiksel işlemin string olarak oluşturulması gibi durumlar söz konusudur. İşte bu durumlarda yardımımıza yetişecek olan bir metodu nasıl yazacağımızdan bu makalede bahsedeceğim. Örneklerle açıklamak gerekirse metodumuz 3+5 gibi bir değeri string olarak verdiğinzde size 8 sonucunu döndürecektir. Metod işlemleri /,*,-,+ işlem sırasına göre yapmakta ve parantez önceliğine dikkat etmektedir. Bunun yanı sıra 3A+5B gibi bir değeri string olarak metoda verdiğiniz ve A ile B nin değerini parametrik olarak tanımladığınız durumda ise A ve B parametrelerinin yerine değerlerini koyarak işlem yapabilecektir. Metodun cevap verdiği işlem karmaşıklığını "3B+120/((2+3(3-1))*5A)" olarak örneklendirebiliriz.

         Öncelikle class'ımızı yazmaya başlayalım.

Yazacağımız class dosyası .Net Framework 3.5 kullanılarak yazılmıştır ancak kopyalanması durumunda 2.0 da da çalışabilmektedir.
 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
 
namespace MathFunctions
{
    public enum Parameters
    {
        A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z
    }


         Bu kısımda  matematik fonksiyonunun içerisine yazılabilecek parametreleri enum olarak tanımlıyoruz. Bu sayede kullanıcı yanlış bilgi giremeyecek.
 

        private Dictionary<Parameters, decimal> _Parameters = new Dictionary<Parameters, decimal>();
        private List<String> OperationOrder = new List<string>();
 
        public Dictionary<Parameters, decimal> Parameters
        {
            get { return _Parameters; }
            set { _Parameters = value; }
        }
 
        public MathParser()
        {
            OperationOrder.Add("/");
            OperationOrder.Add("*");
            OperationOrder.Add("-");
            OperationOrder.Add("+");
        }

        
         Bu kısımda gördüğünüz gibi öncelikle Dictionary tipinde generic bir member değişken tanımlıyoruz ve bu değişkeni property ile encapsule ediyoruz. Bu değişken formülde bulunan parametrelerin değerlerinin set edilmesi için kullanılacaktır. Daha sonrasında ise class'ımızın constructorunda işlem sıramızı tanımlıyoruz.
         Artık metodlarımızı yazabiliriz.
 

        public decimal Calculate(string Formula)
        {
            try
            {
                string[] arr = Formula.Split("/+-*()".ToCharArray(),    StringSplitOptions.RemoveEmptyEntries);
                foreach (KeyValuePair<Parameters, decimal> de in _Parameters)
                {
                    foreach(string s in arr)
                    {
                        if (s != de.Key.ToString() && s.EndsWith(de.Key.ToString()))
                        {
                            Formula = Formula.Replace(s, (Convert.ToDecimal(s.Replace(de.Key.ToString(), "")) * de.Value).ToString());
                        }
                    }
                    Formula = Formula.Replace(de.Key.ToString(), de.Value.ToString());
                }


         Bu metodumuz kullanıcının çağıracağı metoddur. Öncelikle gördüğünüz gibi string olarak gelen formül içerisinde bulunan parametrelerimizin yerlerine değerleri yazılıyor ve daha sonrasında çarpım olarak ifade edilmesi sağlanıyor. Örneğin 5A gibi yazılmış bir parametrik verinin A'nın 3 olması durumunda 15 şekline dönüştürülmesi sağlanıyor.
 

                while (Formula.LastIndexOf("(") > -1)
                {
                    int lastOpenPhrantesisIndex = Formula.LastIndexOf("(");
                    int firstClosePhrantesisIndexAfterLastOpened = Formula.IndexOf(")", lastOpenPhrantesisIndex);
                    decimal result = ProcessOperation(Formula.Substring(lastOpenPhrantesisIndex + 1, firstClosePhrantesisIndexAfterLastOpened - lastOpenPhrantesisIndex - 1));
                    bool AppendAsterix = false;
                    if (Formula.Substring(lastOpenPhrantesisIndex - 1, 1) != "(" && !OperationOrder.Contains(Formula.Substring(lastOpenPhrantesisIndex - 1, 1)))
                    {
                        AppendAsterix = true;
                    }
                    Formula = Formula.Substring(0, lastOpenPhrantesisIndex) + (AppendAsterix ? "*" : "") + result.ToString() + Formula.Substring(firstClosePhrantesisIndexAfterLastOpened + 1);
                }
                return ProcessOperation(Formula);
            }
            catch (Exception ex)
            {
                throw new Exception("Error Occured While Calculating. Check Syntax", ex);
            }
        }

        
         Metodun ilerleyen kısımlarında kısımlarında ise formül içerisinde ( olduğu sürece en içteki parantezden başlamak üzere kendisinden sonra gelen ilk )'e kadar olan kısmı alıyor ve ProcessOperation metoduna parametre olarak geçiriyor. Burdan dönen değer ise iki parantez arasındaki formül yerine yazılıyor. Yani bu işlem bittiğinde 3/(2-1) gibi bir formül 3/1 olarak ifade ediliyor. Elimizde kalan en son değer () içermediğinden bu değeri de direkt ProcessOperation metoduna göndererek işlemi biritiyoruz.
 

        private decimal ProcessOperation(string operation)
        {
            ArrayList arr = new ArrayList();
            string s = "";
            for (int i = 0; i < operation.Length; i++)
            {
                string currentCharacter = operation.Substring(i, 1);
                if (OperationOrder.IndexOf(currentCharacter) > -1)
                {
                    arr.Add(
(s==""?"0":s));
                    arr.Add(currentCharacter);
                    s = "";
                }
                else
                {
                    s += currentCharacter;
                }
            }
            arr.Add(
(s==""?"0":s));
            s = "";
            foreach (string op in OperationOrder)
            {
                while (arr.IndexOf(op) > -1)
                {
                    int operatorIndex = arr.IndexOf(op);
                    decimal digitBeforeOperator = Convert.ToDecimal(arr[operatorIndex - 1]);
                    if (arr[operatorIndex + 1].ToString() == "-")
                    {
                        arr.RemoveAt(operatorIndex + 1);
                        digitAfterOperator = Convert.ToDecimal(arr[operatorIndex + 1]) * -1;
                    }
                    else
                    {
                        digitAfterOperator = Convert.ToDecimal(arr[operatorIndex + 1]);
                    }
                    arr[operatorIndex] = CalculateByOperator(digitBeforeOperator, digitAfterOperator, op);
                    arr.RemoveAt(operatorIndex - 1);
                    arr.RemoveAt(operatorIndex);
                }
            }
            return Convert.ToDecimal(arr[0]);
        }


         ProcessOperation metodunda yaptığımız işlem öncelikle bu metoda parametre olarak gelen 5+3-2/1 gibi pazantez veya parametre bulunmayan formülü öncelikle rakamlar ve operatorleri tek bir dizi haline çeviriyoruz. Yani bizim için 5,+,3,-,2,/,1 ayrı ayrı değerler oluyor. Sonrasında ise OpratıonOrder sırasında ilerleyerek yani işlem kurallarına uyarak operatörün bulunduğu index'i alıyoruz. Örneğin ilk / operatörüne baktığımız için indexini 5 olarak buluyoruz. Bu operatörü bir öncesinde ve bir sonrasında bulunan rakamlar için uygulayacağımızdan bu rakamları alıyoruz ve CalculateByOperator metoduna gönderiyoruz. Buradan dönen değeri operatörün index'ine yazıyor ve öncesindeki ve sonrasındaki indeksleri siliyoruz. Bu sayede yeni array'imiz 5,+,3,-,1 şeklinde oluyor. Döngü bittiğinde elimizde tek değer kalıyor.
 

        private decimal CalculateByOperator(decimal number1, decimal number2, string op)
        {
            if (op == "/")
            {
                return number1 / number2;
            }
            else if (op == "*")
            {
                return number1 * number2;
            }
            else if (op == "-")
            {
                return number1 - number2;
            }
            else if (op == "+")
            {
                return number1 + number2;
            }
            else
            {
                return 0;
            }
        }
    }
}

        
         CalculateByOperator metodunda ise gelen işleme ve sayılara göre 4 işlemden biri yapılıyor ve sonuç geriye döndürülüyor.

         Önemli sayılabilecek özelliklerden birini uygulamamıza eklememize yardım edecek metodumuzu yazdık. Şimdi kullanım örneklerini inceleyelim.
 

        MathFunctions.MathParser mp = new MathFunctions.MathParser();
        private void btnCalculateBasicFormula_Click(object sender, EventArgs e)
        {
            lblBasicFormulaResult.Text = mp.Calculate(txtBasicFormula.Text).ToString();
        }
        private void btnCalculateParametricFormula_Click(object sender, EventArgs e)
        {
            mp.Parameters.Add(MathFunctions.Parameters.A, 5);
            mp.Parameters.Add(MathFunctions.Parameters.B, 2);
            mp.Parameters.Add(MathFunctions.Parameters.C, 1);
            mp.Parameters.Add(MathFunctions.Parameters.D, 3);
            lblParametricFormulaResult.Text = mp.Calculate(txtParametricFormula.Text).ToString();
        }




      Umarım faydalı olmuştur.
         oztamer@hotmail.com
         tamer.oz@yazgelistir.com
         oztamer@hotmail.com
Ornek Uygulama