تقييم الموضوع :
  • 0 أصوات - بمعدل 0
  • 1
  • 2
  • 3
  • 4
  • 5
تقييم التعبيرات الجبرية (الحسابية) المكتوبة كسلسلة نصية - String
#1
السلام عليكم و رحمة الله و بركاته...

رأيت سؤالاً لأحد الإخوة حول كيفية الحصول على قيمة تعبير حسابي مكتوب في خانة نص. و قد أحببت أن أفتح موضوعاً بهذا الشأن لتقديم بعض التوضيحات و تعم الفائدة.

لنفرض أن لدينا التعبيرين الحسابيين التاليين:
كود :
5 ^ 2 + 1 - 25 * 2

(5 ^ (2 + 1) - 25) * 2

نحن كبشر نستطيع أن نلقي نظرة على التعبير و نحدد أولوية العمليات سواءً من ناحية تطبيق الأقواس أو من ناحية أسبقية العوامل، بحيث نكتب الحل على عدة خطوات لنصل إلى الناتج النهائي.
ففي التعبير الأول سنوجد ناتج الأس أولاً (5 أس 2 = 25) ثم ناتج الضرب (25 ضرب 2 = 50) ثم ناتج الجمع و الطرح (25 زائد 1 ناقص 50) ليكون الناتج -24.
أما في التعبير الثاني - و الذي يتكون من نفس المعاملات أو المقادير أو الأعداد - فإن للأقواس الأولوية العليا، حيث سنجمع القوس الداخلي (2 زائد 1 = 3) ثم الأس (5 أس 3 = 125) ثم الطرح (125 ناقص 25 = 100) و أخيراً الضرب (100 ضرب 2) ليكون الناتج 200.

أما تحليل مثل هذه التعابير - رغم بساطتها - برمجياً فإنه أمر معقد بهذا الشكل لأن البرنامج قد يحتاج إلى الاحتفاظ المؤقت بالعديد من القيم و نواتج الكثير من العمليات ليعود إليها لاحقاً حسب أسبقية العوامل، و إذا كانت هناك أقواس فستكون العملية أكثر تعقيداً.

التعبيرات بالشكل السابق - و التي نستعملها نحن البشر بشكل طبيعي تسمى التعبيرات ذات العوامل البينية (Infix expressions) لأن العوامل (إشارات العمليات) موجودة بين المعاملات أو المقادير.

هناك صيغ أخرى لكتابة التعبيرات الحسابية مثل التعبيرات ذات العوامل البعدية (Postfix expressions) حيث توضع العوامل (الإشارات) بعد القيم، و كذلك التعبيرات ذات العوامل القبلية (Prefix expressions) حيث توضع العوامل (الإشارات) قبل القيم.
و أكثر هاتين الصيغتين استعمالاً هي صيغة العوامل البعدية (Postfix) و التي سنستعملها هنا.

* مثال 1: التعبيران المذكوران أعلاه كمثال يكتبان بطريقة الـ Postfix كالتالي (سنشرح بعد ذلك كيف يتم ذلك يدوياً و برمجياً):
كود :
5 2 ^ 1 + 25 2 * -

5 2 1 + ^ 25 - 2 *

ما نلاحظه في هذين التعبيرين (Postfix) أنه لا وجود للأقواس، و أنه ليس هناك أولوية للعوامل حيث يتم تقييم التعبير من اليسار إلى اليمين بالترتيب - أولاً بأول - بصرف النظر عن أسبقية العامل لأن الأسبقية تمت مراعاتها عند التحويل من التعبير ذي العوامل البينية (Infix).

* إذن لتقييم تعبير حسابي نقوم بخطوتين:
= 1. نقوم بتحويل التعبير من صيغة Infix إلى صيغة Postfix.
= 2. نقوم بتحليل التعبير الناتج بصيغة الـ Postfix.

*** سنبين أولاً كيف يتم تحويل تعبير من صيغة Infix إلى صيغة Postfix يدوياً لكي يكون الأمر واضحاً:
1. إذا كان التعبير يحتوي على أقواس نتركها كما هي.
2. نضع أقواساً من عندنا حول كل مقدارين حسب أسبقية العوامل.
3. نبدأ بتفكيك التعبير ابتداءً من الداخل باتجاه الأقواس الخارجية. عملية التفكيك تعني نقل العامل (الإشارة) من بين المقدارين و وضعها بعد المقدار الثاني.
4. نحذف جميع الأقواس فيبقى لنا التعبير بصيغة Postfix.

# المثال الأول هو تحويل التعبير الأول المذكور أعلاه، أي الذي من غير أقواس:
كود :
Infix = 5 ^ 2 + 1 - 25 * 2
أ. وضع الأقواس
(5 ^ 2) + 1 - (25 * 2)
((5 ^ 2) + 1) - (25 * 2)
(((5 ^ 2) + 1) - (25 * 2))
ب. التفكيك
(((5 2 ^) + 1) - (25 2 *))
(((5 2 ^) 1 +) - (25 2 *))
(((5 2 ^) 1 +) (25 2 *) -)
ج. إزالة كافة الأقواس
Postfix = 5 2 ^ 1 + 25 2 * -

# المثال الثاني هو تحويل التعبير الثاني المذكور أعلاه، أي الذي يحتزي على أقواس:
كود :
Infix = (5 ^ (2 + 1) - 25) * 2
أ. وضع الأقواس
((5 ^ (2 + 1)) - 25) * 2
(((5 ^ (2 + 1)) - 25) * 2)
ب. التفكيك
(((5 ^ (2 1 +)) - 25) * 2)
(((5 (2 1 +) ^) - 25) * 2)
(((5 (2 1 +) ^) 25 -) * 2)
(((5 (2 1 +) ^) 25 -) 2 *)
ج. إزالة كافة الأقواس
Postfix = 5 2 1 + ^ 25 - 2 *

و كما نلاحظ فإن الأمر بسيط.

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

أخيراً: تحتوي المرفقات على برنامج كامل - كمثال - يستعمل تلك الخوارزميات لتحليل مقدار جبري (حسابي) مكتوب في مربع نص (TextBox). عند تشغيل البرنامج اكتب التعبير الحسابي في مربع النص الأول (دون علامة =) ثم انقر الزر "تقييم" حيث سيعرض صيغة الـ Postfix في مربع النص الثاني و الناتج في مربع النص الثالث، مع الملاحظات التالية:
1. الكود يدعم عمليات الجمع (+) و الطرح (-) و الضرب (*) و القسمة (/) و الأس (^). و يمكن طبعاً توسيعه ليدعم عمليات أخرى مثل القسمة الصحيحة (\) و باقي القسمة (%).
2. مع أن البرنامج يحتوي على اختبارات لاقتناص بعض الأخطاء إلا أنه قد يفشل مع بعض الأخطاء (برنامج للتجربة و التوضيح - غير مكتمل من ناحية اصطياد الأخطاء) لذلك أدخل دائماً تعبيرات حسابية صحيحة.
3. الكود لا يدعم استعمال القيم السالبة كمدخلات، مثلاً سيعطي خطأ في حالة بدأ التعبير الحسابي برقم سالب.

نرجو الاستفادة و السلام.


الملفات المرفقة
.rar   ADT.rar (الحجم : 4.04 ك ب / التحميلات : 51)
بِسْمِ اللهِ الرَّحْمَنِ الرَّحِيمِ ( وَ مَا تُقَدِّمُوا لِأَنفُسِكُم مِّنْ خَيْرٍ تَجِدُوهُ عِندَ اللهِ هُوَ خَيْراً وَ أَعْظَمَ أَجْراً ) صَدَقَ اللهُ الْعَظِيمُ
الرد }}}}
تم الشكر بواسطة:
#2
ماشاء الله عليك استاذي الكريم
الرد }}}}
تم الشكر بواسطة:
#3
السلام عليكم ورحمة الله وبركاته


بارك الله فيك


السلام عليكم ورحمة الله وبركاته
الرد }}}}
تم الشكر بواسطة:
#4
وعليكم السلام ورحمة الله تعالى وبركاته

بارك الله فيك وسدد خطاك وجعلها فى ميزان حسناتك إن شاء الله
سبحان الله وبحمده سبحان الله العظيم
آللهم لگ آلحمد حتى ترضى .. ولگ آلحمد إذا رضيِت .. ولگ آلحمد بعد آلرضآ
الرد }}}}
تم الشكر بواسطة:
#5
مشكور أخي الغالي مثال رائع وجميل جداً

اتمنا الاستفادة للجميع
[صورة مرفقة: images?q=tbn:ANd9GcT72OLJW7D1E5QW-HUeWeJ...TGoNeg2jnQ]
الرد }}}}
تم الشكر بواسطة:
#6
السلام عليكم

ايييه وين ايامك يامعلم والله اشتقنا لابداعك ومواضيعك المميزه . والردود أو الإجابات النموذجية .

تحياتي لك
الرد }}}}
تم الشكر بواسطة:
#7
السلام عليكم...

شكراً لأخي VB_Coder و بقية الإخوة على الإطراء.

سنبين في هذه المشاركة خوارزمية التحويل من التعبير ذي العوامل البينية (Infix) إلى التعبير ذي العوامل البعدية (Postfix). و لكن قبل ذلك سنشرح موضوعاً جانبياً لأنه مستعمل في الخوارزمية، و هو موضوع المكدس (Stack).

عندما نعالج بعض التعبيرات على مراحل فإننا غالباً ما نحتاج إلى تخزين بعض القيم بشكل مؤقت لنرجع إليها وقت الحاجة، و غالباً ما نريد تخزين تلك القيم بشكل منظم حسب الترتيب و ليس بشكل عشوائي.
يوجد لمثل هذا الأمر العديد من الأشكال التي تسمى تراكيب أو هياكل البيانات (Data Structures)، و لعل من درسوا البرمجة بشكل منهجي أو أكاديمي يعرفون ذلك. و لأن تلك التراكيب تكون عادة في شكل عام أو مجرد فإنها تسمى أنواع البيانات المجردة (Abstract Data Types = ADT). و من هذه الأنواع أو التراكيب:
1. المكدس Stack.
2. الطابور Queue.
3. القوائم المرتبطة Linked Lists.
... و العديد العديد ...
و هذه التراكيب تستعمل في مجالات برمجية كثيرة جداً مثل تحليل التعابير - كما في موضوعنا هذا - و في عمليات الفرز أو الترتيب و في معالجات النصوص و في تصميم الألعاب و الذكاء الصناعي و في تصميم لغات البرمجة و أنظمة التشغيل... إلخ.

* المكدس Stack: هو هيكل لتخزين البيانات بطريقة "الداخل أخيراً يخرج أولاً" (Last in First out = LIFO) أي أن آخر قيمة تم إدخالها أو تخزينها هي أول قيمة يمكن الوصول إليها (استخراجها) ثم القيمة التي دخلت قبلها ثم التي قبلها و هكذا. مثلاً إذا أدخلنا القيم التالية على الترتيب: 3 ثم 5 ثم 7 ثم 8 فإنه يتم استخراجها بالترتيب العكسي: 8 ثم 7 ثم 5 ثم 3.

* وظائف التعامل مع الـ Stack: توجد ثلاث وظائف أساسية:
1. الوظيفة Push لتخزين قيمة في قمة الـ Stack.
2. الوظيفة Pop لقراءة القيمة الموجودة في قمة الـ Stack و إزالة القيمة من الـ Stack.
3. الوظيفة Peek لقراءة القيمة الموجودة في قمة الـ Stack دون إزالتها.

بالإضافة إلى وظائف أخرى مثل معرفة ما إذا كانت الـ Stack فارغة أم لا، و معرفة عدد القيم في الـ Stack و كذلك وظيفة لتفريغ الـ Stack من القيم.

* في البرنامج المرفق في المشاركة الأولى هناك Class Module باسم TStack لاستعمالها في إنشاء كائن Stack، و في يلي الكود و بعض الشرح حوله:
كود :
Option Explicit

Private FValues As Collection

Private Sub Class_Initialize()
    Set FValues = New Collection
End Sub

Private Sub Class_Terminate()
    Set FValues = Nothing
End Sub

Public Sub Push(AValue As Variant)
    FValues.Add AValue
End Sub

Public Function Pop() As Variant
    If FValues.Count = 0 Then
        Err.Raise vbObjectError + 100, "TStack.Pop", "Stack is empty"
    Else
        Pop = FValues(FValues.Count)
        FValues.Remove FValues.Count
    End If
End Function

Public Function Peek() As Variant
    If FValues.Count = 0 Then
        Err.Raise vbObjectError + 100, "TStack.Peek", "Stack is empty"
    Else
        Peek = FValues(FValues.Count)
    End If
End Function

Public Function Is_Empty() As Boolean
    Is_Empty = (FValues.Count = 0)
End Function

Public Sub Make_Empty()
    Do While FValues.Count <> 0
        FValues.Remove FValues.Count
    Loop
End Sub

Public Function Count() As Long
    Count = FValues.Count
End Function

كما نرى، في بداية الكود قمنا بتعريف متغير (FValues) من النوع Collection (و هو من أنواع VB6) لتخزين قيم الـ Stack.

* الوظيفة Push:
كود :
Public Sub Push(AValue As Variant)
    FValues.Add AValue
End Sub
كما قلنا هي لتخزين قيمة في الـ Stack و لذلك تقبل بارامتراً واحداً (AValue) الذي يمثل القيمة التي نريد تخزينها. نلاحظ أننا نستعمل هنا الوظيفة Add لكائن الـ Collection، و الظيفة Add تضيف القيمة تلقائياً في نهاية الـ Collection و بالتالي فإن نهاية الـ Collection (أخر قيمة فيه) تعتبر هي القمة بالنسبة إلى الـ Stack الخاصة بنا.

* الوظيفة Pop:
كود :
Public Function Pop() As Variant
    If FValues.Count = 0 Then
        Err.Raise vbObjectError + 100, "TStack.Pop", "Stack is empty"
    Else
        Pop = FValues(FValues.Count)
        FValues.Remove FValues.Count
    End If
End Function
تقوم الوظيفة Pop بقراءة و إزالة آخر قيمة في الـ Stack (الموجودة عند القمة). نلاحظ أن الوظيفة Pop تتأكد أولاً من أن الـ Stack تحتوي فعلاً على قيم. فإذا كان عدد القيم صفراً فإنها تتسبب في حدوث خطأ. أما إذا كانت هناك قيم فإنها تعيد آخر قيمة ثم تقوم بإزالتها من الـ Stack.

* الوظيفة Peek:
كود :
Public Function Peek() As Variant
    If FValues.Count = 0 Then
        Err.Raise vbObjectError + 100, "TStack.Peek", "Stack is empty"
    Else
        Peek = FValues(FValues.Count)
    End If
End Function
الوظيفة Peek تشبه الوظيفة Pop في أنها تعيد القيمة الموجودة عند قمة الـ Stack لكنها تختلف عنها في أنها لا تقوم بحذف أو إزالة تلك القيمة من الـ Stack.

* الوظيفة Is_Empty:
كود :
Public Function Is_Empty() As Boolean
    Is_Empty = (FValues.Count = 0)
End Function
هذه الوظيفة (Is_Empty) للتأكد من كون الـ Stack فارغة أم لا. تعيد True إذا كانت الـ Stack فارغة (لا تحتوي على قيم)، و تعيد False بخلاف ذلك.

* الوظيفة Make_Empty:
كود :
Public Sub Make_Empty()
    Do While FValues.Count <> 0
        FValues.Remove FValues.Count
    Loop
End Sub
نستعمل الوظيفة Make_Empty لإفلراغ الـ Stack، أي حذف كافة القيم منها.

* الوظيفة Count:
كود :
Public Function Count() As Long
    Count = FValues.Count
End Function
تعيد الوظيفة Count عدد القيم في الـ Stack.

*** نأتي الآن إلى خوارزمية التحويل:
= نفرض أن التعبير الأصلي مخزن في متغير اسمه Infix و أن الناتج سيكون في متغير اسمه Posfix.
= الخوارزمية تستعمل الـ Stack من أجل التخزين المؤقت للعوامل (الإشارات الحسابية) و الأقواس اليسرى ")".
= التعبير الناتج (Postfix) يتم تكوينه من اليسار إلى اليمين باستعمال القيم من التعبير الأول (Infix) و العوامل (الإشارات) المخزنة في الـ Stack.
= العملية تبدأ بوضع قوس أيسر على الـ Stack و إضافة قوس أيمن إلى نهاية التعبير الأصلي (Infix).
= تنتهي الخوارزمية عندما تصبح الـ Stack فارغة.

الخطوة 1: ضع (خزن) قوساً أيسر ")" في الـ Stack و أضف قوساً أيمن "(" إلى نهاية الـ Infix.
الخطوة 2: تتبع الـ Infix من اليسار (البداية) إلى اليمين (النهاية) و كرر الخطوات من 3 إلى 6 لكل عنصر في التعبير Infix إلى أن تصبح الـ Stack فارغة.
الخطوة 3: إذا التقيت بمعامل (قيمة أو عدد) أضفه إلى Postfix.
الخطوة 4: إذا التقيت بقوس أيسر ")" ضعه على الـ Stack.
الخطوة 5: إذا التقيت بعامل (إشارة حسابية) إذن:
- أ. قم بالسحب (Pop) المتكرر من الـ Stack و الإضافة إلى Postfix لكل عامل (إشارة) موجود عند قمة الـ Stack و له أسبقية مساوية أو أعلى من العامل الذي التقيت به في Infix.
- ب. أضف العامل الذي التقيت به في Infix إلى الـ Stack.
الخطوة 6: إذا التقيت بقوس أيمن "(" إذن:
- أ. قم بالسحب (Pop) المتكرر من الـ Stack و الإضافة إلى Postfix لكل عامل (إشارة) موجود عند قمة الـ Stack إلى أن تلتقي بقوس أيسر عند قمة الـ Stack.
- ب. أزل القوس الأيسر من الـ Stack (و لكن لا تضفه إلى Postfix).
الخطوة 7: النهاية (اخرج).

* في المشاركة القادمة سنشرح كود VB الذي يستند على الشرح المذكور أعلاه.

نرجو الاستفادة و السلام.
بِسْمِ اللهِ الرَّحْمَنِ الرَّحِيمِ ( وَ مَا تُقَدِّمُوا لِأَنفُسِكُم مِّنْ خَيْرٍ تَجِدُوهُ عِندَ اللهِ هُوَ خَيْراً وَ أَعْظَمَ أَجْراً ) صَدَقَ اللهُ الْعَظِيمُ
الرد }}}}
تم الشكر بواسطة:
#8
السلام عليكم

- شكرا لك على ماتقدمه من بحر علمك وإبداعك وتفيدنا به ، ماشاء الله عليك ، الله يزيدك من علمه .

تحياتي لك إستاذنا الكبير بعلمك وبأخلاقك النبيله .
الرد }}}}
تم الشكر بواسطة:
#9
ألف شكر أستاذنا الجليل
زادك الله من علمه ، وزادك من قدرتك على الشرح والتعليم وحل المشاكل المعقدة [SIZE=3]للآخرين

بصراحة أنا لا أستطيع أن أتخيل كيف سيكون هذا المنتدى لو لم يكن به الأستاذ /ناجي إبراهيم
[/SIZE]
الرد }}}}
تم الشكر بواسطة:
#10
السلام عليكم...

العفو يا أخي إسلام، فالمنتدى - و منذ أيامه القديمة - مليء بالمميزين و المتفوقين و المتقدمين (أسامة صبح، sniper.ps، الليث، أحمد جمال... و القائمة طويلة). و كثرة الإطراء قد تكون سبباً للإعجاب بالنفس، و الإعجاب بالنفس قد يكون مدخلاً من مداخل الشيطان. نعوذ يالله من العُجْب و الكِبر و الرياء.

(وَمَا تُقَدِّمُوا لِأَنفُسِكُم مِّنْ خَيْرٍ تَجِدُوهُ عِندَ اللَّهِ هُوَ خَيْراً وَأَعْظَمَ أَجْراً)

و السلام عليكم.
بِسْمِ اللهِ الرَّحْمَنِ الرَّحِيمِ ( وَ مَا تُقَدِّمُوا لِأَنفُسِكُم مِّنْ خَيْرٍ تَجِدُوهُ عِندَ اللهِ هُوَ خَيْراً وَ أَعْظَمَ أَجْراً ) صَدَقَ اللهُ الْعَظِيمُ
الرد }}}}
تم الشكر بواسطة:


التنقل السريع :


يقوم بقرائة الموضوع: بالاضافة الى ( 1 ) ضيف كريم