الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

أله حاسبه بإمكانيات متعدده

بدأه C++er في 30 أغسطس 2009 · 10 رد · 14,433 مشاهدة · في قسم الدروس والمواضيع المتميزة
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

فى الموضوع أسئلة الإحماء لبداية مسابقات القسم قام الأخ ghost بوضع اختبار لعمل اله حاسبه تحتوى على العمليات الحسابيه البسيطه و الأقواس، و حيث ان اضافة الأقواس يجعل تحليل العمليه الحسابيه معقد قليلا لذا انا قلت

اقتباس
و تعقيد بتعقيد نقوم بإضافة بعض الدوال الرياضيه مثل abs و floor و ceil و sin و cos و tan.

طبعا انا لم اكتفى بهذا و لكنى رأيت انه من الجميل انك لو تستطيع كتابة الأرقام بصيغ غير العشريه ايضا، بمعنى انك يمكنك كتابة معدله تقوم فيها بجمع رقمين احداهما ثنائى و الأخر ثمانى و الناتج يظهر سداسى عشرى او اى نوع اخر. لذا فقد شرعت فى التنفيذ و الحمد لله انى انتهيت منها.

هذا الدرس مخصص لشرح كيفية برمجة الأله الحاسبه و كيف قمت بعمل الـ Parser الخاص بها.

post-135620-1251557238.jpg

داخل هذه الأله قمت بإستخدام طريقتين لتحليل المعادله التى يدخلها المستخدم:

  1. استخدام VB.Net Compiler لمعرفة ناتج العمليه
  2. استخدام Custom Parser لمعرفة ناتج العمليه

سأقوم فى البدايه بشرح الـ Custom Parser يليها شرح كيفية استخدام VB.Net Compiler.

يتكون الـ Parser من مجموعه من الدوال شكلها كالتالى:

Imports System.Reflection

Public Class Functional

	Shared Sub New()

	Public Shared Property OutputStyle() As NumberSystem
	Public Shared Property FormatBinary() As Boolean

	Public Shared Function Eval(ByVal value As String) As String

	Private Shared Function CalcExpression(ByVal value As String) As String
	Private Shared Function CalcTerm(ByRef value As String, ByRef index As Integer) As Decimal
	Private Shared Function CalcNumber(ByRef value As String, ByRef index As Integer) As Decimal

	Private Shared Sub NumbersToNativeForm(ByRef value As String)

	Private Shared Function FunctionsInExpression(ByVal value As String, ByRef names() As String) As Integer

	Private Shared Sub FilterFunctions(ByRef value As String)

	Private Shared Function CountOf(ByVal search_for As String, ByVal value As String, ByVal start_index As Integer) As Integer

	Private Shared Function CountOfAll(ByVal search_for() As String, ByVal value As String) As Integer

	Private Shared Function FindBestFitFunction(ByRef fnNames() As String, ByVal value As String, ByVal start_index As Integer, ByVal end_index As Integer) As String

End Class

اولا اسم الفئه هو Functional و الهدف من هذا الإسم هو انى كنت اصنع هذه الأله الحاسبه بثلاث طرق اولهما بإستخدام VB Compiler و الاخرى بإستخدام مجموعه من الدوال اما الأخيره فبإستخدام مجموعه من الفئات لذا فقد قمت بتسمية الفئه الخاصه بالدوال Functional.

لتحليل المعادله المدخله من المستخدم اقوم بالخطوات التاليه:

  1. قم بحذف المسافات من المعادله.
  2. قم بوضع قوس البدايه و النهايه للمعادله (و هما القوسين"()").
  3. قم بالبحث عن اى ارقام غير عشريه و قم بتحويلها للنظام العشرى و ابدال الموجود بالنسخه العشريه.
  4. قم بتحليل المعادلات و ابدالها بناتجها.
  5. قم بتحليل الأرقام الباقيه و التى فى الأغلب لن تكون إلا بعض الأرقام و العلامات الرياضيه البسيطه (+ ، - ، * ، /)
  6. قم بإظهار الناتج

ملحوظه : فكرة تحليل المعادله التى تحتوى على العلامات الحسابيه من كتاب Begining Visual C++ 2005 لـ Ivor Horton.

فى البدايه لابد من فهم كيفية تحليل معادله بسيطه تحتوى فقط على العلامات الرياضيه و كذلك الأرقام فقط.

يقوم المستخدم بإدخال معادله مثل

post-135620-1251625759_thumb.jpg

و كما تلاحظ فقد وضع بها مسافة (المربع الثانى الفارغ) و حيث ان المسافه لا هى رقم و لاهى علامه رياضيه فبالتالى واجب حذفها لتصبح المعادله بالشكل التالى:

post-135620-1251625765_thumb.jpg

تحتوى المعادله السابقه على جميع العلامات الرياضيه و حيث انه من البديهى ان الضرب و القسمه لهما الأولويه الاولى و الجمع و الطرح لهما الترتيب الثانى لذا سنحتاج لتعريف بعض المصطلحات:

expression : و هى المعادله نفسها

term: و تمثل رقم او رقمين بينهما علامة الضرب او القسمه او مجموعه من الأرقام و يأتى بينهم علامات الضرب و القسمه فقط.

operator: و هى علامة الجمع او الطرح.

و بالتالى يمكن القول ان المعادله تحتوى على مجموعه من الـ terms و الـ operator و التى دائما ما تاخذ الشكل التالى:

expression = term operator term operator ........ term

الشكل التالى يوضح المعنى بشكل افضل

post-135620-1251625772_thumb.jpg

و السؤال الذى يتبادر إلى الذهن هو ماذا يحدث عند وجود رقم مكون من اكثر من خانه؟

فى هذه الحاله سيتم تقسيم الرقم الموجود بالـ term إلى مجموعه من الـ digits كما فى الصوره التاليه

post-135620-1251626510_thumb.jpg

كما ترى من الصوره تم الرمز إلى علامة الضرب و القسمه بـ multop اى (multiplication operator) و الجمع و الطرح بـ addop أى (addition operator)، ايضا تم تقسم الرقم الواحد إلى مجموعه من الـ digits.

مما سبق نجد اننا يمكننا عمل التالى:

  1. ابحث عن مكان اول علامة جمع او طرح. (لذا مما عرفناه سابقا سيكون ما قبل علامة الجمع و الطرح هو term).
  2. لدينا الأن term نقوم بتخزين كل ما يقابلنا داخل متغير رقمى حتى نقابل علامة الضرب او القسمه فإن وجد سيكون لدينا حاله من اثنين

    1. إما ان التالى هو رقم لذا سنقوم بتخزينه فإن قابلنا علامه جديده سنقوم بتطبيق العلامه التى من 2 على هذا الرقم مع الذى قبله ثم نبدأ من من هذا المكان 2.
    2. و إما اننا وصلنا لمكان علامة الجمع و الطرح لذا سنستبدل حتى هذا المكان بالرقم النهائى و نعود لرقم 1.



    3. الأن عرفنا كيف سنقوم بتحليل الـ Expression و لكن كيف سنقوم بتطبيق هذا الكلام برمجيا
      داخل الفئه Functional توجد 3 دوال وهم

      Private Shared Function CalcTerm(ByRef value As String, ByRef index As Integer) As Decimal
      Private Shared Function CalcNumber(ByRef value As String, ByRef index As Integer) As Decimal
      	Private Shared Function CalcExpression(ByVal value As String) As String

      البدايه تكون مع الداله CalcExpression و التى تستقبل المعادله
      كود الداله كالتالى:

      Dim Expr As String = value.Replace(" ", "")

      Dim current_index As Integer = 0
      Dim retval As Decimal = CalcTerm(Expr, current_index)

      While current_index < Expr.Length

      Select Case Expr.Chars(current_index)

      Case "+"c
      current_index += 1
      retval += CalcTerm(Expr, current_index)

      Case "-"c
      current_index += 1
      retval -= CalcTerm(Expr, current_index)

      Case Else
      Return String.Empty

      End Select

      End While

      Return retval.ToString
      End Function
      Private Shared Function CalcExpression(ByVal value As String) As String

      فى البدايه نقوم بحذف المسافات من المعادله و ذلك عن طريق عمل تبديل المسافه بقيمه فارغه
      بعد ذلك نقوم بتعريف المتغير current_index و اعطيناه القيمه صفر، هذا المتغير سنعرف من خلاله الموقع الذى نحن فيه داخل المعادله.
      بعد ذلك قمنا بتعريف المتغير retval و الذى يقوم بحساب قيمة الـ Term الأول داخل المعادله و ذلك لأن اى معادله ايا كانت تحتوى على الأقل Term واحد فقط لذا نقوم بحساب قيمته اولا و بعد ذلك نبدأ فى البحث عن علامة الجمع و الطرح.
      لحساب اول Term قمنا يتمرير المعادله للداله CalcTerm و هى تطلب معاملين اولهما المعادله التى سنبدأ فى حساب الـ Term الموجود بها و الثانى هو الـ Index الذى من خلاله سنبدأ فى حساب قيمة الـ Term، بالنسبه للمعامل الثانى فيتم تمريره ByRef و ذلك لإنه يقوم بتحديث المتغير الذى يتم تمريره للمكان الذى تم التوقف عنده.
      بعد حساب قيمة الـ Term الأول نقوم فى الدخول بحلقة While وشرط استمرارها هو ان يكون الـ current_index اقل من حجم المعادله و طالما ان هذا الشرط true سيتم الدخول على select case للحرف الذى مكانه هو current_index، فإن كان الحرف هو علامة الجمع فيتم زيادة current_index بواحد ثم استدعاء CalcTerm و تمرير المعادله له و المتغير current_index و القيمه المعاده يتم جمعها على القيمه الموجوده بـ retval و تخزين القيمه النهائيه بـ retval.
      اما اذا كان الحرف هو علامة الطرح سيتم تنفيذ ما قد حدث مع علامة الجمع بإستثناء انه فى هذه الحاله سنطرح يدلا من ان نجمع.
      اما اذا كان هذا الحرف ليس بعلامة جمع او طرح سنقوم بالرجوع من الداله بقيمه فارغه (و التى اعلم منها انه توجد مشكله).
      فى حال الخروج من حلقة While يتم الرجوع بالقيمه الموجوده بـ retval على شكل نص.
      نأتى الان للداله CalcTerm و التى تقوم بحساب قيمة Term معين و شكلها كالتالى:

      Dim retval As Decimal = CalcNumber(value, index)

      If index >= value.Length Then Return retval

      While value.Chars(index) = "*"c OrElse value.Chars(index) = "/"c

      Select Case value.Chars(index)
      Case "*"c
      index += 1
      retval *= CalcNumber(value, index)
      If index >= value.Length Then Exit While

      Case "/"c
      Try
      index += 1
      retval /= CalcNumber(value, index)
      If index >= value.Length Then Exit While

      Catch ex As DivideByZeroException
      Throw ex
      End Try

      End Select
      End While

      Return retval
      End Function
      Private Shared Function CalcTerm(ByRef value As String, ByRef index As Integer) As Decimal

      تقوم هذه الداله بحساب قيمة Term معين و ذلك بدءأ من Index معين يتم تمريره لها.
      فى البدايه يتم تعريف متغير retval و الذى سيتم تخزين القيمه المعاده من هذا الـ term به.
      تلاحظ انه بعد تعريف المتغير retval تم استدعاء الداله CalcNumber لتعود بالقيمه داخله و الهدف من هذه العمليه انه اى term يحتوى على رقم واحد فقط على الأقل لذا تم استدعاء هذه الداله كى تحول الحروف بدءا من الـ index لرقم (لا تشغل بالك الأن بكيفية قيامها بهذه العمليه فستعرف بعد قليل).
      تقوم الداله CalcNumber بتحديث قيمة الـ Index الذى يمرر لها بالمكان الحالى داخل المعادله لذا تلاحظ ان اول سطر بعد تعريف المتغير retval هو التحقق من قيمة الـ index فإن كان اكبر من او يساوى عدد الحروف الخاصه بالمعادله فيتم الرجوع بقيمة المتغير retval.
      غير ذلك يتم الدخول على جملة while حيث شرط تنفيذها هو ان يكون الحرف الحالى هو علامة الضرب او علامة القسمه.
      اذا تم الدخول فى الحلقه يتم التحقق من هذا الحرف فغن كان علامة الضرب يتم زيادة الـ index بواحد ثم استدعاء الداله CalcNumber و تمرير لها الـ index الحالى و القيمه المعاده يتم ضربها فى قيمة المتغير retval و تخزين القيمه النهائيه بـ retval، بعد ذلك يتم التحقق من قيمة index فإن تخطت حجم المعادله او تساوت معها يتم الرجوع بـ retval.
      اما إذا كان الحرف هو القسمه يتم عمل ما تم عمله مع عملية الضرب بإستثناء اننا هنا نقسم و لا نضرب.
      الكود الخاص بالقسمه تم وضعه داخل try حتى اذا كانت القيمه التى سنقسم عليها صفر يتم رمى استثاء (و الذى من خلاله اعرف ان هناك خطأ فأتوقف و اظهر syntax error :blush: ).
      بعد الخروج من الحلقه اقوم بالرجوع بقيمة retval.
      نأتى الأن لأخر داله و هى CalcNumber تقوم هذه الداله بأخذ معاملين اولهم المعادله التى تحتوى على رقم بشكل نصى و تريد تحويله لرقم و المعامل الثانى هو رقم الت index و هو المكان الذى سنبدأ بقرأة الرقم منه. الداله تأخذ الشكل التالى:

      Dim retval As Decimal = 0

      While Char.IsDigit(value.Chars(index))
      retval = 10 * retval + Val(value.Chars(index))
      index += 1

      If index = value.Length Then Return retval
      End While

      If value.Chars(index) <> "." Then Return retval
      index += 1

      Dim fract As Decimal = 1


      While Char.IsDigit(value.Chars(index))
      fract *= 0.1D
      retval = retval + Val(value.Chars(index)) * fract
      index += 1

      If index = value.Length Then Return retval
      End While


      Return retval
      End Function
      Private Shared Function CalcNumber(ByRef value As String, ByRef index As Integer) As Decimal

      تيبدا الداله (مثل الباقيين) بتعريف المتغير retval و الذى سيتم تخزين قيمه الرقم به، بعد ذلك يتم الدخول على حلقة while و شرطها هو ان الحرف الذى مكانه هو index هو Digit اى رقم.
      يتم ضرب القيمه الموجوده فى retval بعشره ثم جمع الناتج بالحرف بعد تحويله لرقم و الناتج النهائى يتم تخزينه بـ retval، بعد ذلك يتم زيادة index بواحد، بعدها يتم التحقق من الـ index فإن كان يساوى او يتخطى عدد حروف المعادله يتم الرجوع بـ retval.
      بعد الخروج من حلقة while يتم التحقق من الحرف الحالى فإن كان هذا الحرف غير النقطه فيتم الرجوع بقيمة retval و غير ذلك فهذا يعنى ان الرقم له بقيه و البقيه هى جزء عشرى لذا يتم الدخول على حلقة while جديده و هى مماثله تماما للأولى و لكن هنا بدلا من الضرب فى 10 يتم الضرب فى 0.1.
      بعد الخروج من while يتم الرجوع بـ retval.
      ===============================================================================
      دعنا نرتاح قليل ثم نكمل . . . . . . . . . . . . . . . . . . . . . ارتحنا خلاص نكمل بقى
      لو تسئلونى نفسك فى ايه دلوقتى اقول لكم نفسى اشوف ghost :thumbup:
      ===============================================================================
      انتهينا من الجزء الأول من الـ parser و الخاص بتحليل العلامات الحسابيه البسيطه.
      نأتى الان لموضوع اضافة الأقواس.
      صراحة موضوع الأقواس يجعلك مشوش حيث انه يلغى كل الاولويات فبإستخدامهم يمكنك تقديم الجمع على الضرب و الطرح على القسمه.
      المشكله الثانيه هنا اننا قمنا بالفعل بكتابة الكود الخاص بالعمليات الحسابيه و ليس من العقل او المنطق ان نقوم بكتابة كود اخر يستخدم فى حالة الأقواس لذا لابد من ان نصنع طريقة للتعامل مع الأقواس و ايضا تكون مبنيه فوق ما سبق.
      قبل ان تتطرق للوسيله التى اتبعها اريد ان اريك شيئا، انظر معى لهذه المعادله :
      (3+4) * 5 - ( (2/3) + 7)

      المعادله السابقه يمكن تقسيمها إلى عدة اقسام و بإستخدام اماكن لتخزين بعض القيم يمكن ان تصبح المعادله بالشكل كالتالى:


      B = 2 / 3

      A = A * 5

      B = B + 7

      A - B
      A = 3 + 4

      هل ترى ما أرى ان الأقواس كل ما تفعله هو صنع Expressions و ناتج هذه الـ Expressions يتم دمجه مع Expressions اخرى و هكذا.
      كلام جميل و لكن المشكله هى اى Expression سنبدأ به او بمعنى اصح اى الأقواس سنبدأ فى تحليله اولا حيث انه قد يحتوى على اقواس اخرى ؟؟
      و جوابى عليك هو انه اذا فكرت قليلا ستجد انه هناك دائما قوس لا يحتوى على قوس اخر فمثلا التعبير 2/3 موجود داخل قوس هذا القوس لا يحتوى على اى شئ إلا بعض العمليات البسيطه و التى يمكن تحليلها بإستخدام الداله CalcExpression.
      يمكننا ان نعرف مكان اخر قوس مفتوح ثم نحضر القوس المغلق لهذا القوس المفتوح، لذا ما بينهم هو عباره عن Expression لذا نستدعى الداله CalcExpression لمحتوى هذا القوس و عندما تعود نقوم بإستبدال هذا القوس بمحتوياته بقيمته و هكذا دواليك حتى تنتهى الأقواس.
      ايضا توجد مشكله اخرى انظر معى هذا المثال.
      3 * ( 5 + 4 ) - ( 2 / 2 )

      إذا قمنا بتطبيق ما قمنا بقوله فى الاعلى فسيكون ناتج هذه المعادله كالتالى:


      3 * ( 5 + 4 ) - 1

      3 * 9 - 1
      3 * ( 5 + 4 ) - ( 2 / 2 )

      كما ترى ناتج هذه العمليه هو 3 * 9 - 1 و السبب فى ذلك ان عمليه تحليل الأقواس ستظل تتم حتى تنتهى الأقواس و نسينا ان المعادله نفسها قد لا تحتوى على اقواس تتضمنها جميعا لذا الحل الوحيد لهذه المشكله هو اما ان نطلب من مستخدم الأله انه دائما لابد منه ان يكتب اقواس البدايه و النهايه و اما قبل ان نبدأ فى تحليل الأقواس ان نضيف قوسين البدايه و النهايه، بالطبع هذا هو الحل الأفضل لذا المعادله السابقه سيتم حلها كالتالى:


      ( 3 * ( 5 + 4 ) - ( 2 / 2 ) )


      ( 3 * ( 5 + 4 ) - 1 )

      ( 3 * 9 - 1 )

      ( 26 )

      26
      3 * ( 5 + 4 ) - ( 2 / 2 )

      كما تلاحظ انه تم حل المعادله و الناتج هو الذى نريده بالظبط، السؤال الأن كيف سنقوم بتضمين تلك العمليه برمجيا.
      الحل يكمن لدينا فى الدالتين IndexOf و LastIndexOf الموجودين بالنوع String.
      كل ما سنقوم به هو اننا سندخل حلقة While و شرط تنفيذها هو ان مكان اول قوس مفتوح لا يساوى -1 لإنه عندما يساوى -1 فإنه لا يوجد قوس مفتوح داخل المعادله و بالتالى فإنه لا يوجد قوس مغلق و الحاله الوحيده التى تصبح فيها المعادله بدون اقواس هو عندما يكون لدينا الناتج النهائى.
      داخل البرنامج توجد الداله Eval و التى تقوم بتحليل الأقواس و العوده بالقيمه النهائيه، و شكل الداله هو التالى: (لقد قمت بحذف بعض الأكواد منها لإنهم ينتموا لأجزاء اخرى من البرنامج)

      value = "(" + value.Replace(" ", "").ToLower + ")"

      While value.IndexOf("(") <> -1
      Dim FirstBracket As Integer = value.LastIndexOf("("c)
      Dim LastBracket As Integer = value.IndexOf(")"c, FirstBracket)
      Dim res As String = value.Substring(FirstBracket, LastBracket - FirstBracket + 1)
      value = value.Replace(res, CalcExpression(res.Substring(1, res.Length - 2)))
      End While

      Return value
      End Function
      Public Shared Function Eval(ByVal value As String) As String

      الداله Eval تقوم بتمرير لها معادله معينه و هى تقوم بإرجاع لك ناتج المعادله.
      اول سطر داخل الداله هو تحويلى للداله لحروف صغير و اضافة ")" فى بدايتها و "(" فى نهايتها بعد ذلك اقوم بالدخول على حلقو While و شرطها هو وجود القوس ")" داخل المعادله و تمثل بـ
      value.IndexOf("(") <> -1

      داخل الحلقه اقوم بعمل متغير اسمه FirstBracket و هو يحتوى على اول قوس من الداخل انظر هذه الصوره

      post-135620-1251636044_thumb.jpg

      و كما ترى فى الصوره سبب اختيار هذا القوس انه لا يحتوى على اى واحد اخر بالتالى بإستبداله بقيمته سيساعد على جعل المعادله ابسط فى التحليل.
      المتغير FirstBracket يحتوى على الـ Index الخاص بهذا القوس المفتوح و يتم معرفة مكانه بإستخدام الداله LastIndexOf و حيث ان لكل قوس مفتوح قوس خاص به يغلقه فسنقوم بإحضار القوس المغلق لهذا القوس بإستخدام الداله IndexOf و تمرير لها موقع القوس الحالىو حفظ الناتج داخل المتغير LastBracket.
      قد يقول البعض منكم لماذا تم تمرير موقع القوس المفتوح للداله IndexOf لماذا لم نستدعيها بدون ارسال اى معاملات؟
      انظر الصوره التاليه و بعدها اقرأ الشرح:

      post-135620-1251636651_thumb.jpg

      الفرق بين الدالتين IndexOf و LastIndexOf هو ان الاولى تحضر اول مكان يقابلها للقيمه المدخله و البحث يبدأ من اليسار اما الاخرى فهى مثلها تماما بإستثناء ان البحث يبدأ من اليمين.
      فى حالتنا هذه اذا قمت بإستخدام الداله IndexOf بدون ارسال معاملات فستعود لك بالرقم الخاطى فكما ترى بالصوره السابقه القوس ذات اللون الأخضر (رقم 3) هو الذى تم العوده بقيمته و ليس القوس الاحمر (رقم 2) هو الذى تم العوده برقمه و لكن عندما تمرر رقم للداله IndexOf يتقوم ببدء البحث بدءا من هذا الرقم و عندما قمنا بتمرير الرقم الموجود بـ FirstBracket للداله IndexOf قامت بالرجوع بالرقم الصحيح (رقم القوس 2).
      بعد ذلك قمت بإستخدام الداله Substring للحصول على القوس المفتوح و المغلق و ما بينهما ثم حفظتهما داخل المتغير res (اختصار لـ result).
      بعد ذلك قمت بكتابة الكود التالى:
      value = value.Replace(res, CalcExpression(res.Substring(1, res.Length - 2)))

      هذه الصيغه مجمعه و قد تبدو معقده قليلا لذا سأقوم بتفكيكها لك حتى يسهل شرحها

      B = CalcExpression( A )
      C = Value.Replace( res , B )
      Value = C
      A	   = res.Substring( 1, res.Length - 2 )

      فى البدايه قمنا بإستخلاص القيمه الموجوده مابين القوس المفتوح و المغلق وضعناها داخل A بعدها قمنا بإستدعاء الداله CalcExpression لما هو موجود بـ A و النتيجه تم تسجيلها بـ B بعد ذلك قمنا بعمل تبديل لقوسين و ما بينهما بما هو موجود بـ B (النتيجه لمحتوى ما بين القوسين) بعد ذلك جعلنا قيمة Value هى الموجوده بـ C.
      ستستمر الحلقه بالعمل حتى تنتهى الأقواس بعدها سيتم العوده بمحتوى value.
      و حتى هنا هذا هو اجابة السؤال الخاص بالأخ GHOST
      التالى هو اضافات من عندى:
      الـ Parser يعمل الان بشكل جيد جدا مع الارقام الصحيحه و الكسريه و الأقواس و لكن ماذا عن الأرقام السالبه ماذا لو كتبنا 3 * -5 النتيجه المتوقعه هى -15 و لكن ليس كل ما يتمناه المرء يدركه فالداله CalcNumber لاتفهم مامعنى هذا الكلام (الإشاره السالبه و الموجبه) لذا لابد منا ان نفهمها هذا الكلام.
      ملحوظه لمن حمل نسخ سابقه من الأله : كنت قد كتب الداله CalcNumber اكثر من مره و لكن اخر مره قمت بكتابتها نسيت اضافة كود الأرقام السالبه لذا النسخه الموجوده فى هذا الدرس تحتوى على هذه الخاصيه.
      الداله CalcNumber بعد التعديل تاخذ الشكل التالى:

      Dim retval As Decimal = 0

      Dim sign As Integer = 1

      If value.Chars(index) = "+" Then
      index += 1
      ElseIf value.Chars(index) = "-" Then
      sign = -1
      index += 1
      End If

      While Char.IsDigit(value.Chars(index))
      retval = 10 * retval + Val(value.Chars(index))
      index += 1

      If index = value.Length Then Return retval * sign
      End While

      If value.Chars(index) <> "." Then Return retval * sign
      index += 1

      Dim fract As Decimal = 1


      While Char.IsDigit(value.Chars(index))
      fract *= 0.1D
      retval = retval + Val(value.Chars(index)) * fract
      index += 1

      If index = value.Length Then Return retval * sign
      End While


      Return retval * sign
      End Function
      Private Shared Function CalcNumber(ByRef value As String, ByRef index As Integer) As Decimal

      الداله كما هى لم تتغير و لكنى قمت بإضافة بعض الأكود و هى :


      If value.Chars(index) = "+" Then
      index += 1
      ElseIf value.Chars(index) = "-" Then
      sign = -1
      index += 1
      End If
      	Dim sign As Integer = 1

      قمت بتعريف المتغير sign و الذى سيحتوى على الأشاره بعد ذلك تحققت من الحرف الموجود بالـ index الحالى فإن كان "+" (لا تستغرب يوجد بعض الناس من يحب كتابة الأرقام الموجبه مسبوقه بـ +) قم بزيادة الـ Index بواحد فقط و إن كانت "-" قم بجعل sign يساوى -1 ثم زد Index بواحد.
      ايضا قمت بجوار كلمة return داخل الداله بضرب retval بـ sign.
      النسخه التنفيذيه
      Calculator.rar
      الكود
      Calculator_Code.rar
      باقى الشرح سأقوم بإضافته اليوم بإذن الله و هو عن تحليل الدوال داخل المعادله و التعامل مع انظمة الاعداد المختلفه.
      و الله ولى التوفيق

تم تعديل هذه المشاركة بواسطة GHOST2010 في 3 سبتمبر 2009 في 17:18

3

مدونتي: C++ Tips and Tricks

#2
اقتباس
لو تسئلونى نفسك فى ايه دلوقتى اقول لكم نفسى اشوف ghost

نعم ياCD ,, أنا جيت :D

جارى مراجعة الدرس :wink:

إن قلت قال الله قال رسولـه همزوك همز المنكر المتعالي
أو قلت قد قال الصحابة والألـى تبعاً لهم بالقول والأعمال
أو قلت قـال الشافعي وأحمد و أبو حنيفة والإمام الغالي
صدوا عن وحي الإله ودينـه واحتالوا على حرام الله بالإحلال
يا أمةً لعبت بدين نبيها كتلاعب الصبيان في الأوحال
حاشا رسول الله يحكم بالهوى تلك إذاً حكومة الضلال

feed.1.gif

#3

انت نسيت دى :thumbup:

مدونتي: C++ Tips and Tricks

#4

قبل البدء في شرح الدوال سأعطيك فاتح للشهيه وهو التعامل مع انظمة الاعداد المختلفه.

ليس الهدف من هذا الدرس ان اشرح لك كيفية عمل الأنظمة العديده المختلفه او كيفية التحويل بينهم و لكن يكفينى ان اقول لك انه داخل الكود الخاص بالبرنامج توجد الفئه BaseConvertor و التى تتيح لك التحويل من اى نظام إلى اى نظام اخر فإذا كنت تريد مثال على استخدامها بعيدا عن هذه الأله فيمكنك الدخول على هذا الرابط.

الأن كيف سنقوم بتضمين عملية استخدام هذه الأنظمة داخل الأله و كيف سنعرف ؟؟

الحل الوحيد لمعرفة نوع الرقم المستخدم هو بان نضع به علامه ... نعم انت لم تقرأ خطأ لابد ان نعلم الرقم حتى نعرف انه حالة خاصه و تحتاج للتعامل معها بشكل خاص.

اما بالنسبه لتعليمى للأرقام فذلك يتم عن طريق اضافة حرفين فى بداية كل رقم ماعدا الأرقام العشريه بالطبع فهى الإفتراضيه و هذه البدايات كالتالى:

0x - Hexadecimal
0b - Binary
0o - Octal

تم وضع هذه القيم كثوابت بعد تعريف اسم الفئه و هم:

Private Const HexPrefix As String = "0x"
Private Const OctPrefix As String = "0o"
Private Const BinPrefix As String = "0b"

المفترض ان الـ Parser يقوم بالتعامل مع الأرقام على اساس انها عشريه و بالتالى لابد قبل ان يبدأ الـ Parser فى عمله ان تكون الأرقام بالنظام العشرى.

داخل البرنامج توجد الداله NumbersToNativeForm و هى المسؤلة عن تحويل الأرقام من اى من الأنظمه إلى النظام العشرى و إبدال الأرقام داخل المعادله بصيغتهم العشريه و شكل الداله كالتالى:

	Private Shared Sub NumbersToNativeForm(ByRef value As String)
		Dim keywords() As Char = {"+"c, "-"c, "*"c, "/"c, ")"c, ","c}

		Dim start_index As Integer = 0
		Dim last_index As Integer = 0
		Dim num As String = String.Empty
		Dim dec_num As String = String.Empty

		Dim count_of As Integer = CountOf("0x", value, 0)
		For i As Integer = 0 To count_of - 1
			start_index = value.IndexOf(HexPrefix, start_index)
			If start_index = -1 Then Continue For
			last_index = value.IndexOfAny(keywords, start_index)

			num = value.Substring(start_index, last_index - start_index).Replace(HexPrefix, "")
			If BaseConvertor.IsHex(num.ToUpper) Then
				dec_num = BaseConvertor.HexToDec(num)
				value = value.Replace(String.Format("{0}{1}", HexPrefix, num), dec_num)
			Else
				Throw New Exception
			End If
		Next

		count_of = CountOf(OctPrefix, value, 0)
		For i As Integer = 0 To count_of - 1
			start_index = value.IndexOf(OctPrefix, start_index)
			If start_index = -1 Then Continue For
			last_index = value.IndexOfAny(keywords, start_index)

			num = value.Substring(start_index, last_index - start_index).Replace(OctPrefix, "")
			If BaseConvertor.IsOct(num) Then
				dec_num = BaseConvertor.OctToDec(num)
				value = value.Replace(String.Format("{0}{1}", OctPrefix, num), dec_num)
			Else
				Throw New Exception
			End If
		Next

		count_of = CountOf(BinPrefix, value, 0)
		For i As Integer = 0 To count_of - 1
			start_index = value.IndexOf(BinPrefix, start_index)
			If start_index = -1 Then Continue For
			last_index = value.IndexOfAny(keywords, start_index)

			num = value.Substring(start_index, last_index - start_index).Replace(OctPrefix, "")
			If BaseConvertor.IsBin(num) Then
				dec_num = BaseConvertor.BinToDec(num)
				value = value.Replace(String.Format("{0}{1}", BinPrefix, num), dec_num)
			Else
				Throw New Exception
			End If
		Next

	End Sub

حجمها كبير و لكن كلها اكواد مكرره.

سؤال سألته لنفسى و انا اعمل على هذه الداله؟ كيف سأعرف نهاية الرقم.. و الإجابه كانت ينتهى الرقم عندما تجد اى علامة من العلامات الحسابيه.

صمت قليلا و قلت و لكن ماذا يحدث ان قام بوضعه داخل الأقواس لذا سينتهى بقوس.

بعد قليل ضربت رأسى فى الحائط بسبب انى استخدم الأرقام ايضا داخل الدوال (ستعرفها لاحقا) و الداله لها معاملات و الفصل بينهم يكون بالـ comma و بالتالى لابد من اضافة الكومه.

و بالتالى الرقم من الممكن ان ينتهى بأى مما يأتى:

+ - * / ) ,

و حيث ان الداله IndexOf تأخذ نص او حرف واحد فقط فكان لابد منى ان افكر فى حل و هو يكمن فى الداله IndexOfAny حيث المعامل الأول هو مصفوفه من الحروف التى تبحث عنها و المعامل الثانى هو المكان الذى ستبدء فى البحث منه.

لذا قلت لنفسى عرفت مكان البدايه و النهايه كم مره سأقوم بعملية استبدال الأرقام بقيمهم العشريه لذا قمت بصنع الداله CountOf و التى تعود لك بعدد المرات التى توجد بها كلمة معينه داخل نص.

فى بداية الداله قمت بتعريف مصفوفة اسميتها keywords و وضعت بها الحروف التى سأقوم بالبحث عن محتوياتها داخل المتغير النصى.

بعدها قمت بتعريف عدد من المتغيرات التى سيتم استخدامها فيما بعد بكثره و هما start_index و سيحفظ مكان البدايه للرقم ثم last_index و سيحفظ مكان النهايه للرقم.

و المتغير num و سيحفظ قيمة الرقم بعد استخلاصه من المعادله و المتغير dec_num سيحفظ قيمة الرقم بعد تحويله للعشرى.

بعدها قمت بتعريف المتغير count_of ليحتفظ بعدد الأرقام من نوع معين و بعدها قمت بإستدعاء الداله لمعرفة عدد الأرقام السداسى عشريه داخل المعادله.

و قمت ببدء حلقة for بعدد وجود هذا الرقم، و قمت بحفظ موقع البداه للرقم فى start_index و تحققت إذا كان الموقع يساوى -1 قم بتخطى هذه الدوره للتى تليها. بعد ذلك قمت بحفظ موقع النهايه فى last_index ثم استخلصت الرقم داخل المتغير num بعدها قمت بالتحقق من صحة الرقم السداسى العشرى المدخل فإن كان سداسى عشرى فسيتم تحويله و تخزين القيمه فى dec_num ثم ابدال قيمة الرقم السداسى العشرى بالقيمه العشريه.

بعد ذلك يتم اكمال الحلقه حتى الخروج منها ثم تتكرر العمليه لـ ارقام النظام الثمانى و الثنائى.

داخل الداله Eval يتم استدعاء هذه الداله مباشرة بعد وضع الأقواس.

تم تحديث نسخة البرنامج الموجوده فى المشاركه الأولى للتتلائم مع التعديلات الجديده.

شرح تضمين الدوال فى المشاركه القادمه بإذن الله.

و الله ولى التوفيق

تم تعديل هذه المشاركة بواسطة Muhammad alaa في 31 أغسطس 2009 في 01:32

مدونتي: C++ Tips and Tricks

#5

ايوى يا عام , برنامج دسم , بالافكار و طريقة ايجاد الحلول البرمجية , يحتاج الى تريسنج اي تتبع , لن يفهم بمجرد قراءة الكود

لذالك اولا الشكر لك و بعد ذلك سوف اهلكك بالاسالة ان وجدت. :evil:

<EMBED src=http://www.abdelghafar.com/images/banners/bm2.swf width=486 height=60 type=application/x-shockwave-flash EMBED < menu="false" loop="true" quality="high">

#6

يوجد جزء خاص بأنظمة الأعداد نسيت ان اذكره و هو متعلق بإظهار الناتج حيث توجد الخاصيه OutputStyle و التى يوضع بها النظام المراد اظهار النتيجه به و الخاصيه FormatBinary و التى تتيح للمستخدم اظهار النتيجه فى حال كونها ثنائيه فى شكل مجموعات مكونه من 4 ارقام.

ايضا فى اخر الداله Eval يوجد الكود التالى

		If value <> String.Empty Then

			If value.IndexOfAny(New Char() {"."c, "-"c}) > -1 Then Return value

			If mSystem = NumberSystem.Dec Then Return value

			Dim f(2) As Func(Of String, String)
			f(0) = AddressOf BaseConvertor.DecToHex
			f(1) = AddressOf BaseConvertor.DecToOct

			If OutputStyle = NumberSystem.Bin Then
				value = BaseConvertor.DecToBin(value, mFormatBinary, mFormatBinary)
			Else
				value = f(CType(mSystem, Integer) - 1)(value)
			End If
		End If

فى بداية الكود اتحقق من ان الناتج النهائى ليس فارغ فإن كان كذلك فانا اتأكد من ان الرقم ليس سالب لإنه ان كان سالب لن يتم تحويله ايضا اتحقق من ان الرقم ليس كسرى لإنه ايضا لن يتم تحويله لإن المسموح بتحويلهم فقط هم الأعداد الصحيحه الموجبه (حتى الأن)، ايضا ان كان النوع المطلوب التحويل له هو النظام العشرى يتم الرجوع دون فعل اى شئ لإن النظام العشرى هو الإفتراضى.

بعدها اقوم بتعريف مصفوفه من Generic Delegate اسمه Func و امرر له النوع String كنوع المعامل و القيمه العائده بعد ذلك جعلت قيمة عنصرى المصفوفه تشير إلى الدالتين DecToHex و DecToOct على التوالى حتى إن كان النوع المطلوب التحويل اليه هو احداهما اقوم بتنفيذ الكود التالى:

value = f(CType(mSystem, Integer) - 1)(value)

المتغير mSystem نوعه NumberSystem و هو Enum يحتوى على انواع الأعداد و قيمه موضوعه بترتيب معين و هو Bin ، Oct ، Hex ، Dec.

المتغير mSystem يتم حفظ به قيمة الخاصيه OutputStyle.

الكود السابق يمكن تقسيمه ليصبح كالتالى

A	 = CType(mSystem, Integer) - 1

value = f( A ) (value)

كما ترى نقوم بتحويل قيمة mSystem إلى Integer و بعدها نطرح منها 1 و نضع القيمه النهائيه داخل A.

بعدها نقوم بإستدعاء الداله الموجوده بالمكان A داخل المصفوفه و نمرر لهذه الداله القيمه الموجوده بـ value و بعدها نستقبل القيمه داخل value مره اخرى. اعتقد انك لم تفهم المغزى من A و كيف تتم تلك العمليه لذا سأشرحها بشئ من التفصيل و ركز جيدا.

معنى ان البرنامج وصل لتلك المنطقه من الكود فهذا يعنى ان المستخدم قد قام بإختيار اما Hex او Oct و بالتالى و كما ترى من الـ Enum ان الـ Dec يأتى اولا و ياخذ الرقم صفر يليه Hex و يأخذ الرقم واحد يليه Oct و ياخذ الرقم 2.

ما يهمنا هو Hex و Oct و التى قيمهم هى 1 و 2 على التوالى و كما ترى فى المصفوفه f() ان دالة التحويل من DecToHex تاتى اولا يليها DecToOct لذا كل ما افعله هو تحويل القيمه الخاصه بـ mSystem إلى Integer و كما اعرف ان القيمه ستكون فى المدى من 1 (Hex) إلى 2 (Oct) و حيث ان المصفوفه ترقيمها يبدأ من صفر لذا فقد قمت بطرح واحد من القيمه الناتجه و قمت بتمريرها للمصفوفه و بذلك تم استدعاء الداله المرجوه بنجاح (و لا داعى من وجود Select Case او If-Then-Else).

الحاله الأخرى ان المستخدم اختار Binary و بالتالى اقوم بإستدعاء الداله DecToBin و تمرير لها قيمة المتغير value و ايضا يتم تمرير قيمة المتغير mFormatBinary و الذى يحتوى اما على True او False و تمثل الموافقه على عمل Format للقيمه الثنائيه الناتجه.

جارى تجهيز شرح الدوال.

و الله ولى التوفيق

مدونتي: C++ Tips and Tricks

#7

السلام عليكــم ورحمـة الله وبركاتــه ،،

فكرة استخدام الكومبايلر رائعة جدا يامحمد وبصراحة لم أكن أتوقعها نهائيا وحتى لم تخطر ببالى حين وضعت السؤال لكن كل شئ متوقع من شخص مثلك

تم إضافة العشر درجات :wink:

إن قلت قال الله قال رسولـه همزوك همز المنكر المتعالي
أو قلت قد قال الصحابة والألـى تبعاً لهم بالقول والأعمال
أو قلت قـال الشافعي وأحمد و أبو حنيفة والإمام الغالي
صدوا عن وحي الإله ودينـه واحتالوا على حرام الله بالإحلال
يا أمةً لعبت بدين نبيها كتلاعب الصبيان في الأوحال
حاشا رسول الله يحكم بالهوى تلك إذاً حكومة الضلال

feed.1.gif

#8

نأتى الان لأخر جزء من البرنامج و هو الدوال.

عند استدعاء داله تأخذ الشكل التالى:

funName ( param1, param2, ..... , paramn )

فى حالة اعتمادنا على الدوال التى صنعناها من قبل سنجد اننا فى مشكله صعبه حيث ان الأقواس لها معنى خاص جدا و هنا تم اعادة تعريف معناها بمعنى اخر ان الأقواس تستخدم لتقديم حالة expression على اخر و لكن فى هذه الحاله تستخدم الأقواس بشكل مختلف تماما و مع معامل جديد ألا وهو comma، فما العمل.

الحل الوحيد الموجود امامى هو عمل parsing للدوال قبل البدء فى التعامل مع المعادله حيث ان المستخدم قد يكتب شئ مثل التالى:

max ( 10, 5) * Rad(45)

توجد مشكله اخرى و هى اولا كيف سأقوم بإستدعاء الداله، ثانيا كيف سأمرر المعاملات لها، ثالثا كيف سأعرف ان هذا العدد من المعاملات يماثل هذه الداله و ليس بأكثر و لا اقل، رابعا يمكن استدعاء داله من داخل داله فكيف سأقوم بالفضل و استدعاء المضومنه قبل الحاويه، خامسا و اهمهم ان الداله الواحده قد يوجد لها اكثر من صيغه overloading كيف سأحدد ان احداهما هو ما يتم استدعائه دون الأخر (الـ overloading الوحيد المتاح هو بإختلاف عدد المعاملات و ليس النوع).

قبل التطرق لحلول تلك المشاكل نتوقف عند سؤال مهم ألا وهو طبيعة الدوال نفسها حيث توجد داله تستقبل integer و اخرى short و اخرى ulong و هكذا.

إذا اصبح لدى عبء فوق المشاكل السابقه ان اقوم بتحديد نوع الرقم المدخل و نوع المعامل ثم تحويل القيمه لهذا النوع، فى الواقع انا افضل عدم برمجتها اصلا إذا كنت سأفعل هذا لذا صنعت حل بسيط وهو انى سأقوم بصنع wrapper لهذه الدوال لتستقبل string ايا كان نوع القيمه الفعليه التى تستقبلها هذه الداله ثم داخل هذه الداله البديله سأقوم بتحويل القيم المدخله لما يتناسب مع الداله الأصليه ثم استدعى الداله الأصليه بهذه القيمه و بعدها اقوم بتحويل القيمه العائده إلى نص و اعود بها.

انظر هذا المثال على wrapper للداله cos.

Public Shared Function Cos(ByVal value As String) As String
	Try
		Return Math.Cos(CDbl(value)).ToString
	Catch ex As Exception
		Throw ex
	End Try
End Function

قمت بعمل wrapper لأغلب دوال System.Math و قمت بإضافة البعض من عندى و وضعتهم داخل فئة MyMath.

الأن و مشكلة انواع بيانات الدوال تم حلها، يا مسهل هانت نشوف المشكله اللى بعدها كيفية استدعاء تلك الدوال؟؟

فى الواقع هذه ليست مشكله فيمكن استخدام delegate ليشير إلى داله معينه بمعنى انه يمكننى استخدام generic dictionary و امرر له النوعين string للـ key و delegate للـ value و بعدها اقوم بإضافة كل الدوال بهذه الفئه بيدى اسم الداله ثم addressof مكانها داخل MyMath.

حيث ان الفكره نظريا صحيحه إلا إنها عمليا لن تعمل و السبب ان delegate هو must inherit class و لتقوم بتمرير قيمه له لابد من ان تكون من نوع delegate و ليس مؤشر لداله و بالتالى فالفكره فشلت، و حتى على افتراض انها نجحت كيف ستقوم بعمل invoke لهذا الـ delegate و انت لا تعرف عدد المعاملات التى سيتم تمريرها إذا كان هناك معاملات اصلا.

هذه الطريقه لم تنجح لنجرب اخرى، و لكن كيف؟؟ توجد مقوله تقول

اقتباس
عندما تتعقد الأمور ارجع إلى البدايه و ابحث عن حل اخر

لذا البدايه هى اننا نريد استدعاء داله لا نعرف اسمها و لا نعرف عدد المعاملات التى تحتاجها لذا و بمعنى مختصر نريد استدعاء الداله بإسلوب الربط المتأخر Late Binding و الحل لدينا الأن يكمن فى الـ Reflection حيث تحتوى على الفئه MethodInfo و التى بدورها تحتوى على الداله Invoke و التى تطلب منك مصفوفه من نوع object و تمثل المعاملات.

و لكن كيف سنحصل على الـ MethodInfo لهذه الداله؟؟

لنحصل على MethodInfo لابد من ان نتبع بعض الخطوات الثابته وهى: (ليس دائما ما تحتاج تلك الخطوات و لكن استخدمت هذا الأسلوب لتستهيل تطويرها فيما بعد لإستخدام plug-ins)

  1. الحصول على نسخه من الـ Assembly الذى يحتوى على نوع معين و الذى بدوره يحتوى على الداله التى نريد احضارها.
  2. الحصول على نسخه من الفئه الموجوده بالـ Assembly و التى تحتوى على الداله المراد الحصول عليها.
  3. الخطوه السابقه تعود لك بالفئه Type (يمكنك استخدام اسم الفئه MyMath.GetType للحصول على نفس التأثير) و التى تحتوى على الداله GetMethod التى لها اكثر من نسخه و احداهما تأخذ منك أسم الداله و الخصائص الخاصه بالداله مثل Public و Static التى تعنى ان الداله لا تنتمى لنسخه معينه من الفئه و لكن تنتمى للفئه نفسها بمعنى أخر الداله Shared. تذكر عندما تمرر اسم الداله انه case sensetive.

القيمه المعاده من هذه الداله هى مصفوفه من النوع MethodInfo و التى تحتوى على الدوال التى تماثل هذه الصفات لكن يظهر عائق جديد و هو ان المستخدم قد يدخل اسم الداله بحروف صغيره و حيث ان البحث case-sensitive فلابد من المستخدم ان يدخل اسم الداله بنفس الصيغه المسجله بها داخل البرنامج او نحاول التحايل على هذه المشكله، و لكن كيف سنقوم بهذا؟ الفكره تكمن فى انه إذا استطعنا اهمال الإسم و بطريقه ما استطعنا الوصول لهذه الداله فستكون فكره ذكيه، فى الأوضاع العاديه اقول لك انك تحلم و لكن فى هذا الوضع لدى فكره قد تنفع و هى اننا بدلا من ان نستدعى الداله GetMethod لداله واحده نستطيع استدعاء الداله GetMethods و بالتالى لن نحتاج ارسال اسم الداله عندما للوصول إليها و لكن سنحتاج لعمل Linear search داخل المصفوفه فى كل مره نحتاج فيها لداله معينه ايضا بعد البحث و الوصول للداله سنحتاج لمعرفة عدد المعاملات التى تستقبلها.

طبعا العمليه مرهقه قليلا لذا لنسأل انفسنا سؤال اسم الداله وحده قد يتكرر و كذلك عدد المعاملات وحده قد يتكرر و لكن اسم الداله بعدد معين من المعاملات لا يتكرر و هذا امتياز فنستطيع استخدام Generic Dictionary و نجعل نوعه String للـ Key و MethodInfo للـ value و بالتالى يمكننا دمج اسم الداله التى يدخلها المستخدم مع عدد المعاملات للوصول إلى الـ MethodInfo الخاصه بها.

تفكير جيد و لكن ماذا يحدث اذا اردنا معرفة كل من اسم الداله و عدد المعاملات كل على حدى فى تلك الحاله قد نضع فاصل داخل الـ key الخاص بالدوال بين اسم الداله و عدد المعاملات حتى نستطيع تقسيم الإسم كما يحلو لنا فيما بعد او يوجد حل اخر و هو عمل Structure و بعدها نضع بداخله اسم الداله و عدد المعاملات و نسخه من الـ MethodInfo.

و حيث ان هذا الـ Structure نوع جديد لذا قد نقوم بوضع الداله Invoke داخله و نستخدمها فى استدعاء الداله و يمكننا تمرير لها معاملات الداله ايضا. فى الواقع هذا ما فعلته تماما و لكن بدلا من Structure قمت بعمل class و سميتها MethodProperties (تقنيا تصميم هذه الفئه سيئ لإنه من الإفتراضى عدم جعل المتغيرات public و لكنى تحت ظروف معينه اضررت جعلها كذلك، بإذن الله فى الإصدار الثانى منها ستوجد تعديلات كثيره )، هذا هو شكل الفئه

Imports System.Reflection


Public Class MethodProperties
	' method name
	Public Name As String
	' method info
	Public Info As MethodInfo
	' parameter count
	Public ParameterCount As Integer

	' invoke current method
	Public Function Invoke(ByVal Params As String) As String
		Dim value() As String = Nothing

		If Params <> vbNullString Then
			Params = Params.Replace(" ", "")
			value = Params.Split(","c)
		End If

		Return Invoke(value)
	End Function

	' invoke the method and return the value
	Public Function Invoke(ByVal params() As String) As String

		If params IsNot Nothing Then
			For i As Integer = 0 To params.Length - 1
				If params(i) = vbNullString Then Throw New Exception
			Next
		End If

		Return Info.Invoke(Nothing, params).ToString
	End Function

	Public Overrides Function ToString() As String
		Return Name
	End Function

End Class

كما ترى يوجد المتغير Name و هو اسم الداله و Info و تمثل الـ MethodInfo و ParameterCount و هو عدد معاملات الداله ايضا توجد الدالتين Invoke احداهما تأخذ المعاملات على شكل نص مفصول بكومه و الأخرى تأخذ المعاملات على شكل مصفوفه نصيه، بشكل عام الداله Invoke التى تأخذ المعاملات على شكل نص تقوم بتحويل النص إلى مصفوفه من النصوص ثم تمررها للنسخه الثانيه منها و التى تقوم بإستدعاء الداله فعليا و تقوم بتمرير القيم لها و تعود بالناتج للبرنامج.

بعد ذلك قمت بعمل الفئه FunctionList (فى نفس الملف تحت الفئه السابقه)

Public Class FunctionList
	Inherits Dictionary(Of String, MethodProperties)

	Public Overloads Sub Add(ByVal name As String, ByVal info As MethodInfo, ByVal param_count As Integer)
		If Find(name, param_count) Is Nothing Then
			Dim c As String = (name & "_" & param_count).ToUpper
			MyBase.Add(c, New MethodProperties() With {.Name = name, .Info = info, .ParameterCount = param_count})
		End If
	End Sub

	Public Overloads Function Find(ByVal name As String) As MethodProperties()
		Dim arr As New List(Of MethodProperties)

		For Each skey As String In MyBase.Keys
			If skey.StartsWith(name & "_") Then
				arr.Add(MyBase.Item(skey))
			End If
		Next

		Dim ret(arr.Count - 1) As MethodProperties

		arr.CopyTo(ret, arr.Count - 1)

		Return ret
	End Function

	Public Overloads Function Find(ByVal name As String, ByVal param_count As Integer) As MethodProperties
		Dim k As String = (name & "_" & param_count).ToUpper

		If MyBase.ContainsKey(k) Then Return Item(k)

		Return Nothing
	End Function

	Default Public Overloads ReadOnly Property Item(ByVal index As Integer) As MethodProperties
		Get
			Dim arr(MyBase.Keys.Count - 1) As String

			MyBase.Keys.CopyTo(arr, 0)
			If index < 0 OrElse index >= MyBase.Keys.Count Then Return Nothing

			Return Item(arr(index))
		End Get
	End Property

End Class

هذه الفئه وارثه لـ Generic Dictionary و الـ Key هو String و الـ value هى MethodProperties.

تحتوى هذه الفئه على الداله Add و تقوم بإضافة عنصر جديد للـ Dictionary و الداله Find و يوجد منها نسختين احداهما تأخذ اسم الداله و عدد المعاملات و تعود بنسخه من MethodProperties و الأخرى تأخذ اسم الداله فقط و تعود بمصفوفه من MethodProperties.

ايضا قمت بعمل الخاصيه Item و هى تعود بـ MethodProperties الموجوده بـ Index معين.

هذه الفئه هى عبارة عن حاويه للدوال الموجوده بالفئه MyMath و الهدف منها هو تجميع الدوال الموجوده بـ MyMath هنا لتسريع استدعائهم. هذه العمليه تسمى Cashing فبدلا من البحث عند الداله فى كل مره قمت بتجميعهم مره واحده و بعدها اقوم بإستدعائهم كما اريد.

نعود الأن للفئه Functional، ستجد انها تحتوى على هذا الكود

Friend Shared FunLst As FunctionList

Shared Sub New()
	Dim asm As Assembly = Assembly.GetExecutingAssembly
	Dim t As Type = asm.GetType(asm.GetName.Name & ".MyMath")
	Dim m() As MethodInfo = t.GetMethods(BindingFlags.Static Or BindingFlags.Public)

	FunLst = New FunctionList

	For i As Integer = 0 To m.Length - 1
		With m(i)
		FunLst.Add(.Name, m(i), .GetParameters.Length)
		End With
	Next
End Sub

كما تلاحظ قمت بإنشاء متغير من نوع FunctionList و اسمه FunLst و تلاحظ ايضا ان معرف بالكلمه Shared و ذلك حتى استطيع الوصول إليه من خلال اى داله Shared.

بعدها قمت بتعريف Shared Constructor للفئه و هو يتم تنفيذه مره واحده فقط عند بدء تشغيل البرنامج و كل مايقوم به هو تجميع الدوال و وضعها داخل FunLst و عملية تجميع الدوال تم شرحها من قبل.

نأتى الأن لأهم جزء و هو تحليل الدوال.

كما قلنا فى السابق سنقوم بتحليل الدوال قبل البدء فى تحليل المعادله و فى هذه العمليه ما سيتم هو احضار قيمة الداله و وضعها بدلا من تعريف الداله و بذلك عندما يبدأ الـ parser عمله كل ما سيجده هو الأرقام و الأقواس فقط.

الخطواط التاليه هى التى سنتبعها فى تحليل الدوال:

1 - احضار اسماء الدوال المستخدمه داخل المعادله.

2 - الدخول فى حلقة While و شرط تنفيذها ان يكون عدد الدوال المتبقى داخل المعادله اكبر من صفر.

3 - احضار مكان اول قوس مفتوح و احضار مكان القوس الذى يغلقه، ايضا احضار مكان اول علامه قبل القوس المفتوح (ستفهم معنى كلمة علامه بعد قليل).

4 - احضار ما بين القوس المفتوح و اول علامه قبله.

5 - إذا كان ما بينهم هو اسم داله قم بإستخلاص القوس المفتوح و المغلق و ما بينهما.

6 - تحقق من عدد الحروف الخاصه بالمعاملات

  1. إن كانت حرفين فهذا يعنى ان كل ما يتحويه هو القوس المفتوح و المغلق و بالتالى عدد المعاملات صفر.
  2. إن كانت اكثر من حرفين و لا وجود للكومه فيها فعدد المعاملات واحد.
  3. غير ذلك قم بتقسم المعاملات الموجوده ما بين الأقواس و اجعل الفاصل هو الكومه و قم بتخزين الناتج داخل مصفوفه، ايضا قم بالإحتفاظ بعدد المعاملات.

7 - إذا كان عدد المعاملات اكبر من صفر قم بإستدعاء الداله CalcExpression لكل معامل للحصول على قيمته.

8 - قم بإستدعاء الداله Find الموجوده داخل FunLst و مرر لها اسم الداله و عدد المعاملات و القيمه العائده منها قم بإستدعاء الداله Invoke و مرر لها المصفوفة التى تحتوى على المعاملات بعد الحصول على قيمهم.

9 - القيمه المعاده من الخطوه السابقه هى ناتج الداله قم بإستبدال الداله و معاملاتها الموجوده داخل المعادله الأصليه بالقيمه النهائيه لها.

10 - قم بالرجوع إلى 2 حتى الإنتهاء من كل المعادلات.

الكود المستخدم لتنفيذ الخطوات السابقه يتمثل فى 4 دوال وهم:

Private Shared Sub FilterFunctions(ByRef value As String)
	Dim keywords() As Char = {"+"c, "-"c, "*"c, "/"c, "("c, ","c}

	Dim FunNames() As String = FunctionsInExpression(value)

	If FunNames Is Nothing Then Return

	While CountOfAll(FunNames, value) > 0
		Dim FirstBracket As Integer = value.LastIndexOf("("c)
		Dim LastBracket As Integer = value.IndexOf(")"c, FirstBracket)
		Dim BeforeFirstBracket As Integer = value.LastIndexOfAny(keywords, FirstBracket - 1)
		Dim fun_name As String = FindBestFitFunction(FunNames, value, BeforeFirstBracket + 1, FirstBracket - 1)

		If fun_name = String.Empty Then Continue While
		Dim params As String = value.Substring(FirstBracket, LastBracket - FirstBracket + 1)
		Dim param_count As Integer = 0
		Dim param_array() As String = Nothing

		If params.Length = 2 Then ' only () and it mean there is not params
		param_count = 0
		ElseIf params.Length > 2 AndAlso params.IndexOf(",") = -1 Then ' only one expression
		param_count = 1
		ReDim param_array(0)
		param_array(0) = params.Substring(1, params.Length - 2)
		Else ' there is multiple expression
		param_array = params.Substring(1, params.Length - 2).Split(","c)
		param_count = param_array.Length
		End If

		If param_array IsNot Nothing Then

		For i As Integer = 0 To param_array.Length - 1
			param_array(i) = CalcExpression(param_array(i))
			If i <> param_array.Length - 1 Then param_array(i) += ","
		Next

		param_array(0) = FunLst.Find(fun_name, param_count).Invoke(String.Concat(param_array))

		Else
		ReDim param_array(0)
		param_array(0) = FunLst.Find(fun_name, param_count).Invoke(String.Empty)
		End If

		value = value.Replace(fun_name & params, param_array(0))
	End While

End Sub

Private Shared Function FunctionsInExpression(ByVal value As String) As String()
	value = value.ToLower

	Dim accumlate As Integer = 0
	Dim fnames() As String = Nothing
	Dim tmp As String = Nothing

	For i As Integer = 0 To FunLst.Count - 1
		tmp = FunLst(i).Name.ToLower
		If value.IndexOf(tmp & "(", 0) > -1 Then
		ReDim Preserve fnames(accumlate)
		fnames(accumlate) = tmp
		accumlate += 1
		End If
	Next

	Return fnames
End Function

Private Shared Function CountOfAll(ByVal search_for() As String, ByVal value As String) As Integer

	Dim ret As Integer = 0

	For i As Integer = 0 To search_for.Length - 1
		ret += CountOf(search_for(i), value, 0)
	Next

	Return ret
End Function

Private Shared Function FindBestFitFunction(ByRef fnNames() As String, ByVal value As String, ByVal start_index As Integer, ByVal end_index As Integer) As String
	Dim size As Integer = end_index - start_index + 1
	Dim tmp() As String = Nothing
	Dim count As Integer = 0


	For i As Integer = 0 To fnNames.Length - 1
		If fnNames(i).Length = size Then
		ReDim Preserve tmp(count)
		tmp(count) = fnNames(i)
		count += 1
		End If
	Next

	If tmp.Length = 1 Then Return tmp(0)

	Dim fnName As String = value.Substring(start_index, end_index - start_index + 1)

	For i As Integer = 0 To tmp.Length - 1
		If tmp(i) = fnName Then Return fnName
	Next

	Return String.Empty

End Function

يحتوى الكود السابق على 4 دوال و هم FilterFunctions و CountOfAll و FunctionsInExpression و FindBestFitFunction.

اما الداله CountOfAll فيتم تمرير لها مصفوفه نصيه و نص و يتم البحث عن مرات وجود كل كلمة فى المصفوفه داخل النص و يتم الرجوع بالعدد الإجمالى، أستخدم هذه الداله فقط مع الدوال لإنها لن تعمل مع اى شئ اخر (انظر فى الكود و ستعلم لماذا؟ إن لم تعرف سأخبرك فى نهاية الدرس).

اما الداله FindBestFitFunction فيتم تمرير لها مصفوفه من الأسماء و نص و مكان بدايه و مكان نهايه و تقوم هى بالبحث عن افضل كلمه من المصفوفه و التى توجد حاليا داخل النص و تقع بين مكان البدايه و النهايه وايضا عدد حروفها لا يتجاوز مكان البدايه مطروحا من مكان النهايه و تقوم بالرجوع به او إن لم تجده تقوم بالرجوع بنص فارغ.

اما الداله FunctionsInExpression فيتم تمرير لها نص و تعود لك بمصفوفه تحتوى على اسماء الدوال الموجوده داخل هذا النص و بدون تكرار.

الداله الأخيره FilterFunctions و هى تستخدم كل الدوال السابقه و نظام عملها يتبع تماما الـ 10 خطوات السابق ذكرهم، و شرح الكود كالتالى:

تم تعريف مصفوفة اسمها keywords و تحتوى على العلامات التى من الممكن ان تاتى قبل القوس المفتوح للداله.

بعدها تم تعريف المصفوفه FunNames و التى يتم وضع بها اسماء الدوال الموجوده بالمعادله. فإن كانت Nothing فبالتالى لا يوجد دوال و يتم الرجوع.

يتم الدخول فى جملة While و شرط تنفيذها ان CountOfAll للـ FunNames اكبر من الصفر

داخل الحلقه قمت بتعريف المتغير FirstBracket الذى يحتوى على موقع اول قوس مفتوح من اليمين و كذلك المتغير LastBracket و الذى يحتوى على موقع اول قوس مغلق موجود بعد FirstBracket.

ايضا قمت بتعريف المتغير BeforeFirstBracket و الذى يحتوى على موقع اول علامة من المسجلين داخل keywords و موجوده مباشرة قبل FirstBracket.

بعد ذلك قمت بتعريف المتغير fun_name و الذى سيحتوى فيما بعد على اسم الداله الموجوده بين FirstBracket و BeforeFirstBracket h او لاشئ اذا لم توجد داله.

إذا لم توجد داهل يتم الخروج من هذه الداله و غير ذلك يتم تسجيل المعاملات داخل المتغير params و بعدها يتم تعريف المتغير param_count و الذى سيحتوى فيما بعد على عدد المعاملات التى سيتم تمريرها إلى الداله، ايضا قمت بتعريف المصفوفه param_array و جعلت قيمتها nothing هذه المصفوفه سيتم وضع بها المعاملات كل واحد فى عنصر فيها.

بعد ذلك بدءت فى اختبار عدد حروف المتغير params فإن كان 2 فهذا يعنى ان عدد المعاملات صفر و بالتالى يتم وضع صفر داخل المتغير param_count.

إذا كان عدد حروف المتغير params اكبر من 2 و ايضا الكومه غير موجوده فعدد المعاملات يساوى 1 و يتم تحجيم المصفوفه param_array و يجعل عدد عناصرها 1 و يتم وضع به قيمة المعامل بدون اقواس البدايه و النهايه.

غير ذلك يتم تقسيم المعاملات الموجوده داخل params و ذلك بعد تخطى قوس البدايه و النهايه و النتيجه يتم حفظها داخل param_array ايضا يتم حفظ عدد عناصر param_array داخل param_count.

بعد ذلك اتحقق من ان المصفوفه param_array ليست فارغه (لأن الحاله الأولى من الشرط السابق لا يتعامل مع المصفوفه) فإن لم تكن فارغه فأقوم بالدوران على عناصر المصفوفه و اقوم بإستبدال العنصر بالقيمه العائده من الداله CalcExpression لهذا العنصر.

بعد ذلك اقوم بإستدعاء الداله Invoke للداله و اقوم بتمرير المصفوفه param_array لها و الناتج اقوم بوضعه داخل العنصر 0 من المصفوفه param_array.

فى حال ان المصفوفه param_array فارغه (و هى تحدث فقط فى حالة انه لا توجد معاملات) فأقوم بإعادة تحجيم المصفوفه و اضافة عنصر واحد لها ثم استدعاء الداله و الناتج اقوم بوضعه داخل هذا العنصر.

و فى النهايه اقوم بإستبدال الداله و معاملاتها الموجوده داخل المعادله الأصليه بالقيمه النهائيه.

بالنسبه للداله CountOfAll قلت لك فى السابق انها لن تعمل إلا مع الدوال و السبب فى ذلك انى عندما اقوم بالبحث عن اسم الداله بداخلها اقوم بإضافة قوس مفتوح لإسم الداله حيث ان اسم الداله دائما ما يتبعه قوس مفتوح و بالتالى الداله CountOfAll لن تعمل إلا مع البحث عن الدوال.

و الله ولى التوفيق

تم تعديل هذه المشاركة بواسطة Muhammad alaa في 3 سبتمبر 2009 في 14:54

مدونتي: C++ Tips and Tricks

#9

إذا اعتقدت اننا انتهينا من الدوال فإنت محق و لكن إذا اعتقدت بإننا انتهينا من البرنامج فأنت شبه مخطئ.

و السبب فى ذلك ان البرنامج يهدف مستخدمه و حيث ان البرنامج انتهى فنحن كمبرمجين نعرف كيفية استخدامه و ماذا يفعل و لكن ماذا يحل بالمستخدم الذى لا يعرف هذا البرنامج، فماذا يفعل عندما يحب استخدام الداله cos كيف يعرف بوجودها. بالطبع سأحتاج لعمل مرجع بالدوال المستخدمه و المعاملات التى تطلبها فإما ان اقوم بكتابتها بيدى له (و هو مالا افضله بالطبع) و اما ان استخدم الـ System.Reflection لمعرفة هذه الدوال و معاملاتها و انواع المعاملات و كذلك القيمه العائده.

صراحة الفكره اعجبتنى و لكن المشكله ان انواع المعاملات لكل الدوال بلا إستثناء و كذلك القيم المعاده نوعها string و اذا اظهرت للمستخدم هذا من الممكن ان يكتب لى نصوص حتى مع علمه انها أله حاسبه فانا من اقول له هذا المعامل يستقبل نص و ليس double او integer.

ماذا سنفعل لابد من وجود حل و لكن المشكله تكمن فى ان المعاملات string فحتى لو قمت ببإستخدام الـ reflection فستظهر string. و لكن انتظر يوجد حل إليست المواصفات يمكن تطبيقها على المعاملات و القيم العائده .... نعم يمكن، إذا لماذا لا نقوم بعمل مواصفه نقوم فيها بتمرير النوع الأصلى لكل معامل و القيمه المعاده و عندما اقوم بإستخدام الـ reflection لأا داعى بأن اعرف نوع المعامل و لكن اقوم بإحضار نسخة المواصفه التى وضعتها لهذا المعامل و بعدها اقوم بمعرفة القيمه المخزنه.

إذا كنت قد ضعت منى فى هذا الشرح (و هو فى الأغلب حدث) دعنا نرى الجزء المشترك بيننا و هو الكود.

<AttributeUsage(AttributeTargets.Parameter Or AttributeTargets.ReturnValue)> _
Public Class MapTypeAttribute
	Inherits Attribute

	Private Shared Names() As String = {"Empty", "Object", "DBNull", "Boolean", "Char", _
										"SByte", "Byte", "Short", "UShort", "Integer", _
										"UInteger", "ULong", "Long", "Single", "Double", _
										"Decimal", "DateTime"}

	Private mTypeIndex As Integer

	Public Sub New(ByVal typ As Type)
		MyBase.New()
		mTypeIndex = Type.GetTypeCode(typ)
	End Sub

	Public ReadOnly Property TypeName() As String
		Get
			Return Names(mTypeIndex)
		End Get
	End Property

End Class

المواصفه السابقه يتم تطبيقها فقط على المعاملات و القيم المعاده و هو ما احدده بإستخدام المواصفه AttributeUsage بعد ذلك قمت بعمل مشيد يستقبل معامل واحد من نوع Type و معنى ذلك انك عندما تقوم بإستخدام هذه المواصفه لابد و ان تستخدم GetType لتمرير القيمه لهذه المواصفه.

بعد ذلك كتبت هذا الكود

 

mTypeIndex = Type.GetTypeCode(typ)

الهدف من هذه المواصفه توفير اسم للنوع الحقيقى للمعامل او القيمه المعاده و بالتالى فأنا احتاج اسم هذا النوع و حيث ان الدوت نت لا تعرف الإسم Integer او Short او Long فبالتالى احتاج وسيله معينه لإظهار هذه الأسماء بدلا من Int16 و Int32 و Int64.

الحل يكمن فى رقم كل نوع من هذه الأنواع حيث ان لكل نوع رقم فإذا اردت ان تعرف هذا الرقم استخدم الداله GetTypeCode الموجوده بالفئه Type.

و حتى لا اطيل عليك فقد قمت بمعرفة ارقام هذه الأنواع و قمت بعمل المصفوفه Names و وضعت بها الأسماء المستخدمه داخل الـ VB و بالتالى عند تمرير رقم اى من هذه الأنواع للمصفوفه سيتم الرجوع بالأسم المستخدم داخل الـ VB و هذا ما يحدث فى الكود السابق حيث انى قمت بعمل متغير رقمى و وضعت به رقم النوع الذى تم تمرير لهذه المواصفه و عندما تقوم بإستدعاء الخاصيه TypeName فكل ما اقوم به هو العوده بالقيمه الموجوده بالمصفوفه Names عند mTypeIndex.

بعد ذلك كل ما نحتاج لفعله هو العوده للفئه MyMath و تطبيق المواصفه على كل المعاملات و الدوال الموجوده بها كالتالى:

 

 

Public Shared Function Abs(<MapType(GetType(Decimal))> ByVal value As String) As <MapType(GetType(Decimal))> String
		Try
			Return Math.Abs(CDec(value)).ToString
		Catch ex As Exception
			Throw ex
		End Try
	End Function

الأن و قد قمنا بعمل المواصفه و وضعناها على المعاملات و القيم المعاده لكل الدوال الموجوده داخل MyMath، كل ما نحتاج لفعله الأن هو وسيله معينه ليعرف بها المستخدم هذه الدوال و معاملاتها و القيم المعاده منها.

هذا هو ما نفعله داخل frmList.

حيث قمت بوضع Listview داخلها و اسميتها lstFuns ثم وضعت بها ثلاثة اعمده يمثلوا اسم الداله و المعاملات و القيمه المعاده على التوالى.

و داخل الـ Form_load كتبت الكود التالى:

 

 

With Functional.FunLst

	For i As Integer = 0 To .Count - 1
		Dim itm As MethodProperties = .Item(i)
		lstFuns.Items.Add(itm.Name)
		lstFuns.Items(i).SubItems.Add("")

		Dim mapinfo As MapTypeAttribute = DirectCast(itm.Info.ReturnParameter.GetCustomAttributes(GetType(MapTypeAttribute), False)(0), MapTypeAttribute)
		lstFuns.Items(i).SubItems(2).Text = mapinfo.TypeName

		Dim pi() As ParameterInfo = itm.Info.GetParameters()
		Dim title As String = String.Empty

		For w As Integer = 0 To pi.Length - 1
			title &= pi(w).Name & " As " + DirectCast(pi(w).GetCustomAttributes(GetType(MapTypeAttribute), False)(0), MapTypeAttribute).TypeName
			If w <> pi.Length - 1 Then title += ", "
		Next

		If title = String.Empty Then title = " - "

		lstFuns.Items(i).SubItems(1).Text = title

	Next

End With

فى البدايه قمت بعمل loop على الدوال المسجله داخل FunLst الموجوده بـ Functional بعد ذلك قمت بإضافة اسم الداله داخل العمود الأول بـ lstFuns بعد ذلك قمت بإضافة قيم فارغه للعمود الأول حيث انه سيحتوى على المعاملات و لن اضيفها الأن.

بعد ذلك قمت بكتابة الكود التالى:

 

 

Dim mapinfo As MapTypeAttribute = DirectCast(itm.Info.ReturnParameter.GetCustomAttributes(GetType(MapTypeAttribute), False)(0), MapTypeAttribute)
				lstFuns.Items(i).SubItems.Add(mapinfo.TypeName)
داخل بداية حلقة for قمت بصنع متغير اسمه itm و وضعت به نسخه من بيانات الداله و الممثله داخل MathodProperties، فى الكود السابق قمت بالتعامل مع الداله GetCustomAttribute الموجوده بالمتغير info و الذى نوعه MathodInfo و قمت بتمرير لها نوع المواصفه التى صنعناها و بعدها False (المعامل الثانى لهذه الداله يتم تجاهله لذا مرر له اى قيمه تشاء)، القيمه المعاده من هذه الداله هى مصفوفه من النوع Attribute لذا فانا أأخذ اول عنصر بهذه المصفوفه (لإننا لم نضيف سوى MapType واحده فقط)، و الناتج النهائى اقوم بتحويله إلى النوع MapTypeAttribute و وضعه داخل المتغير mapinfo.

بعد ذلك اقوم بإضافة عنصر جديد داخل الـ lstFuns و هو العمود التالى و يحتوى على TypeName الموجود بالمغير mapinfo.

الكود التالى مماثل تماما لما شرحته بالاعلى بإستثناء اننا لم نستخدم ReturnParameter و لكننا استخدمنا GetParameters.

فى حالة ان الداله لا معاملات لها سيتم وضع - ليعرف المستخدم بذلك.

الأله الأن تحتوى على الدوال التاليه:

 

 

Rad, Deg, Abs, Acos, Asin, ATan, ATan, Ceiling, Cos, Cosh, Exp, Floor, ln, Log10, Log2, Mod, Max, Pow,Root, Sqrt, Tan, Tanh, PI, E, GR, EC, Permute, Combin, aLog10, aLog2

هذه الدوال هى الموجوده داخل System.Math ماعدا التالى:

الدوال الغير متوفر شرح لها

Rad تحول الرقم الدخل من درجات إلى radians

Deg تحول الرقم الدخل من radians إلى درجات

ln تقوم بإحضار اللوغارتم الطبيعى لرقم و هى تماثل الداله Log الموجوده بـ Systen.Math

Log2 تقوم بإحضار اللوغارتم بأساس 2 لرقم

Mod تقوم بإحضار باقى القسمه

PI تعود بالثابت PI

E تعود بالثابت E

GR تعود بالثابت Golden Ratio

EC تعود بالثابت Euler Constant

Permute تحسب التباديل لرقمين

Combin تحسب التوافيق لرقمين

aLog10 تحضر مقلوب اللوغارتم بأساس 10 لرقم

aLog2 تحضر مقلوب اللوغارتم بأساس 2 لرقم

الجزء الأخير من البرنامج و هو استخدام الكومبيلر الخاص بالـ VB.Net فى الحصول على ناتج المعادله.

كل ماافعله هو صنع داله شكلها كالتالى:

 

 

Option Strict Off
Imports System

Module Module1

	 Public Function Calculate() As String
		Return CStr(value)
	 End Function

End Module

و اقوم بإستبدال الكلمه value بالمعادله المدخله و بعدها استخدم VBCodeProvider لعمل Compile للكود داخل الذاكره ثم الحصول على القيمه النهائيه و اظهارها للمستخدم.

الأله الحاسبه اصبحت الأن Stable و لا مشاكل بها و الحمد لله

تم تحديث المرفقات

تم تعديل هذه المشاركة بواسطة Xacker في 18 يناير 2013 في 03:38 — السبب: رفع المرفقات فى المشاركة الأولى بطلب من صا

مدونتي: C++ Tips and Tricks

#10

شكرااااااااااااا الله يعطيك العافيه وسلامه

#11

احياك اخي .. تستحق كل الشكر والتقدير .. تحية اجلال واكبار .. لك  اهديها

مواضيع مشابهة