Yeni başlıyanlar için kompleks hesap makinası
Selamlar;
Bugün ekranı runtime’da oluşan çoklu hesap işlemi yapabilen bir hesap makinası yazıcağız. Herşey bilgisayar mühendisliğinde okuyan stajyer arkadaşın isteği ile başladı. Madem yapıyoruz biraz farklı yapalım dedik:)
Design ekranı aşağıdaki gibidir. Görüldüğü üzere rakamlar ve işlem buttonları yoktur. Sadece silme, geri tuşu ve bir de sonuç ekranı bulunmaktadır.
Aşağıda görüldüğü gibi CreateScreen methodu işlem yapılacak 10 rakamı runtime’da btn_Click event’ine bağlayıp isim,boyut,kordinat ve text bilgilerini döngü ile atayıp, pnlKeyboard paneline sıra ile eklemektedir.
Form1.cs :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public void CreateScreen() { for (int i = 0; i < 10; i++) { int y = i < 5 ? 0 : 100; int x = i < 5 ? 10 + 150 * (i) : 10 + 150 * (i - 5); Button btn = new Button(); btn.Name = "btn" + i.ToString(); btn.Size = new System.Drawing.Size(133, 53); btn.Text = i.ToString(); btn.Location = new Point(x, y); btn.Click += btn_Click; pnlKeyboard.Controls.Add(btn); } CustomButton csm = new CustomButton(new string[]{"+","-", "/","x", "="}, new int[]{50,200,350,510,670}, this); |
CustomButton.cs : Aşağıdaki custom button’da, operatör butonlarının text’leri ve soldan boşlukları parametre dizisi olarak verilmiştir. Runtime’da bu butonların textleri,boyutları,konumu,arka rengi ve Click eventinde çağrılacak method’u belirlenmiştir.”=” operatörü bnt_Calc method’una diğerleri btn_Click method’una bağlanmıştır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace yeni { class CustomButton { public CustomButton(string txt,int left,Form1 frm) { Button btn = new Button(); btn.Text = txt; btn.Size = new Size(133, 25); btn.Location = new Point(left, 200); btn.BackColor = Color.SkyBlue; frm.pnlKeyboard.Controls.Add(btn); } public CustomButton(string[] txt, int[] left, Form1 frm) { if (txt.Length == left.Length) { for (int i = 0; i < txt.Length; i++) { Button btn = new Button(); btn.Text = txt[i]; btn.Size = new Size(133, 25); btn.Location = new Point(left[i], 200); btn.BackColor = Color.SkyBlue; if (txt[i] != "=") { btn.Click += frm.btn_Click; } else { btn.Click += frm.btn_Calc; } frm.pnlKeyboard.Controls.Add(btn); } } else MessageBox.Show("Kahve İç Gel :)"); } } } |
btn_Click() : Burada ekrana bir operatör basılmış ise yani (‘+’, ‘-‘, ‘x’, ‘/’) den biri ise yanına bir başka operatörün basılmaması kontrol edilip, duruma göre var olan operatör ile değiştirilmesi sağlanmıştır. Eğer son basılan tuş operatör değilse işlem sırasına yani ekrana eklenir.
1 2 3 4 5 6 7 8 9 10 |
string[] opr = new string[] { "+", "-", "x", "/" }; public void btn_Click(object sender, EventArgs e) { if (opr.Contains(((Button)sender).Text) && opr.Contains(txtScreen.Text.GetLast()) == true) { txtScreen.Text = txtScreen.Text.Substring(0, txtScreen.Text.Length - 1); txtScreen.Text += ((Button)sender).Text; } else { txtScreen.Text += ((Button)sender).Text; } } |
Extension.cs: Aşağıdaki GetLast extension’ında string ifadenin son karakteri alınmaktadır. IsNumeric’de adından anlaşılacağı gibi string ifadenin sayısal olup olmadığına bakılır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace yeni { public static class Extension { public static string GetLast(this string str) { return str.Length>1? str.Substring(str.Length - 1, 1):str; } public static bool IsNumeric(this string s) { float output; return float.TryParse(s, out output); } } } |
Uygulama çalıştırıldıktan sonra buttonlar aşağıda görüldüğü gibi ekrana runtime’da basılır:
Şimdi gelelim ekrana girilen komut kümesini işleyerek sonuca gitmeye. İlk başta interpreter design pattern bu durum için uygun gözükse de işlem sırasının düzenli olmaması, expression tanımlamada sorunlar çıkarmaktadır.
Interpreter Design Pattern: Düzgün sıralı ifadelerin sayısal veya mantıksal olarak işlenip bundan ilgili sonuçlara ulaşılması için kullanılan design patterndır.
btn_Clac: Aşağıda görüldüğü gibi txtScreen’deki tüm karakterler tek tek alınmaktadır. Eğer numeric ise num değişkenine operator ise match değişkenine atılır.Kısaca sayılar ve operatorler bir listeye atılır.
Daha sonra atılan bu listede önce çarpma ve bölme operatörleri işleme alınıp finalMatch dizisine atılır. Daha sonra toplama ve çıkarma operatörleri finalMatch dizisi üzerinden işleme alınır ve sonuca ulaşılır.
Ayrıca aşağıda tüm sonuç ekranını temizleme ve son karakteri silme butonlarının kodlarını da görmekteyiz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
public void btn_Calc(object sender, EventArgs e) { List<string> match = new List<string>(); List<string> finalMatch = new List<string>(); string num=string.Empty; string first=""; string last = ""; bool doOperate = false; string operation = ""; string finalResult = ""; foreach (char item in txtScreen.Text) { if(item.ToString().IsNumeric()) { num += item.ToString(); } else{ match.Add(num); num = ""; match.Add(item.ToString()); } } //------------------------------------------------------------------------ if(num!="")match.Add(num); num = ""; foreach (string item2 in match) { if (item2.IsNumeric()) { if (first == "") { first = item2; } else { last = item2; if (doOperate) { if(operation=="x") { double result = double.Parse(first) * double.Parse(last); first = result.ToString(); last = ""; doOperate = false; operation = ""; } else if (operation == "/") { double result = double.Parse(first) / double.Parse(last); first = result.ToString(); last = ""; doOperate = false; operation = ""; } } else { } } } else { doOperate = false; if (item2 == "x" || item2 == "/") { doOperate = true; operation = item2; } else { doOperate = false; operation = item2; finalMatch.Add(first.ToString()); finalMatch.Add(item2); first = ""; } } } finalMatch.Add(first.ToString()); //-------------------------------------------------------------------------------------- first = ""; last = ""; operation = ""; foreach (string item3 in finalMatch) { if (item3.IsNumeric()) { if (first == "") { first = item3; } else { last = item3; if (operation == "+") { double result = double.Parse(first) + double.Parse(last); first = result.ToString(); last = ""; operation = ""; } else if (operation == "-") { double result = double.Parse(first) - double.Parse(last); first = result.ToString(); last = ""; operation = ""; } } } else { operation = item3; } } txtScreen.Text=first.ToString(); } private void button1_Click(object sender, EventArgs e) { txtScreen.Text = ""; } private void button2_Click(object sender, EventArgs e) { txtScreen.Text=txtScreen.Text.Length > 1 ? txtScreen.Text.Substring(0, txtScreen.Text.Length - 1) : ""; } |
Sonuç olarak ilgili sayısal ve işlem buttonları için ayrı ayrı kod yazılmamış hepsi tekil method’lara bağlanmıştır. Ayrıca butonların runtime’da oluşturulması performans anlamında ciddi bir artış sağlamıştır. Mantıksal operatörlerde toplu işlem belli bir quee’ye alınmış daha sonra önceliklendirmeye göre işletilip sonuca gidilmiştir. Bu durum roma rakamlarının okunması, cryptography gibi daha birçok sıralı ifadelerin çözüm yöntemleri ile benzerlik göstermektedir.
Source Code:http://www.borakasmer.com/projects/yeni.rar
Bir sonraki makalede görüşmek üzere hoşçakalın.
Tuşların dinamik oluşturulması ve bir design patterndan esinlenerek yorumlanması açısından çok güzel bir kod.
Teşekkürler…