اذهب إلى المحتوى

السؤال

نشر

السلام عليكم.

في الكود التالي:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

// global variables & constants
#define NEXT 2

// represent a tree
typedef struct node
{
    int number;
    struct node* next[NEXT];
    // [0] for left subtree and [1] for the one on right
} node;

// functions prototypes
node* allocate_node(int number);
node* find_parent(node* tree, int number);
bool check(node* tree, int number);

int main(void)
{
    // tree: the root of tree - n: to allocate new memory for nodes
    node* tree = NULL;
    node* n = NULL;

    // array to insert
    const int SIZE = 7;
    const int numbers[SIZE] = {5, 7, 2, 6, 8, 2, 4};

    // insert items automatically
    for (int i = 0; i < SIZE; i++)
    {
        n = allocate_node(numbers[i]);

        // n is the root if tree empty
        if (!tree)
        {
            tree = n;
        }
        else
        {
            // temp holds the address of the right parent of n
            node* temp = find_parent(tree, numbers[i]);

            // insert right or left
            if (numbers[i] > temp->number)
            {
                temp->next[1] = n;
            }
            else
            {
                temp->next[0] = n;
            }
        }
    }

    // try checking x
    int x = 50;
    printf("%sFound\n", check(tree, x) ? "" : "Not ");

    // well done!
    return 1;
}

// func1: allocating new memory
node* allocate_node(int number)
{
    node* new = NULL;
    new = malloc(sizeof(node));
    if (!new)
    {
        exit(0);
    }
    new->number = number;
    for (int i = 0; i < NEXT; i++)
    {
        new->next[i] = NULL;
    }
    return new;
}

// func2: find the parent of a node
node* find_parent(node* tree, int number)
{
    if (!tree)
    {
        return NULL;
    }

    node* temp = tree;
    while (temp->next[0] || temp->next[1])
    {
        if (number > (temp->number))
        {
            if (temp->next[1])
            {
                temp = temp->next[1];
                continue;
            }
            return temp;
        }
        else
        {
            if (temp->next[0])
            {
                temp = temp->next[0];
                continue;
            }
            return temp;
        }
    }
    return temp;
}

// func3: search in BST
bool check(node* tree, int number)
{
    // iterative for goes through the tree
    for (node* temp = tree; temp; )
    {
        // found?
        if (number == temp->number)
        {
            // yeah :)
            return true;
        }

        // go right if target greater than temp->number
        else if (number > (temp->number))
        {
            temp = temp->next[1];
            continue;
        }

        // go left if not
        temp = temp->next[0];
    }

    // not found :(
    return false;
}

كلا دالتي البحث و إيجاد الأب للعقد الجديدة متشابهين لحد كبير. هل يمكن بطريقة أو بأخري تقليل عدد أسطر الكود. أو بمعني اَخر, هل يمكن تخزين الأسطر المتشابهة بين الدالتين في مكان ما ثم أستدعاء تلك الأسطر في كلا الدالتين أو شيئ من هذا القبيل.

Recommended Posts

  • 0
نشر

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

نعم إن دالت find_parent و check متشابهتان إلى حد كبير لأنهما تؤديان نفس العملية الأساسية وهو إجتياز شجرة البحث الثنائية (BST) فكلاهما يبدأ من الجذر (root) ويبدا التحرك يسارا أو يميناً بناء على مقارنة القيمة.

ولكن بالرغم من هذا التشابه فإنهما يستخدمان لهدفين مختلفين تماما:

فدالة check هدفها هو العثور على تطابق تام وهي تسأل هل القيمة X موجودة في الشجرة أم لا وتتوقف إما عند العثور على القيمة وتعيد true أو عند الوصول إلى نهاية فرع فارغ (NULL) وتعيد false.

وأما دالة find_parent هدفها هو العثور على المكان المناسب لإدراج عقدة جديدة وهي تسأل أين يجب أن يتم وضع القيمة X إذا لم تكن موجودة .وإنها لا تبحث عن تطابق بل تبحث عن آخر عقدة في المسار (الأب) التي سيتفرع منها الابن الجديد.

ولذلك فإن محاولة دمجهما مباشرة كما تقول من خلال تخزين الأسطر المتشابهة ستجعل الكود أكثر تعقيدا  وليس أبسط ولن يكون سهل القراءة والفهم  وأيضا ستحتاج إلى إضافة شروط كثيرة أو أعلام (flags) داخل الدالة المشتركة لتقرر ما إذا كانت في وضع البحث أم وضع إيجاد الأب وهذا يتعارض مع مبدأ الفصل بين الاهتمامات (Separation of Concerns) في البرمجة. حيث هذا المبدأ أساسي جدا وهو أن يكون لكل جزء من الكود وظيفة حادة وليس أكثر من وظيفة وذلك لجعل التعديل سهل في المستقبل وعدم إعتماد الأكواد على بعضها البعض .

  • 0
نشر
بتاريخ On 23‏/10‏/2025 at 08:24 قال محمد_عاطف:

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

نعم إن دالت find_parent و check متشابهتان إلى حد كبير لأنهما تؤديان نفس العملية الأساسية وهو إجتياز شجرة البحث الثنائية (BST) فكلاهما يبدأ من الجذر (root) ويبدا التحرك يسارا أو يميناً بناء على مقارنة القيمة.

ولكن بالرغم من هذا التشابه فإنهما يستخدمان لهدفين مختلفين تماما:

فدالة check هدفها هو العثور على تطابق تام وهي تسأل هل القيمة X موجودة في الشجرة أم لا وتتوقف إما عند العثور على القيمة وتعيد true أو عند الوصول إلى نهاية فرع فارغ (NULL) وتعيد false.

وأما دالة find_parent هدفها هو العثور على المكان المناسب لإدراج عقدة جديدة وهي تسأل أين يجب أن يتم وضع القيمة X إذا لم تكن موجودة .وإنها لا تبحث عن تطابق بل تبحث عن آخر عقدة في المسار (الأب) التي سيتفرع منها الابن الجديد.

ولذلك فإن محاولة دمجهما مباشرة كما تقول من خلال تخزين الأسطر المتشابهة ستجعل الكود أكثر تعقيدا  وليس أبسط ولن يكون سهل القراءة والفهم  وأيضا ستحتاج إلى إضافة شروط كثيرة أو أعلام (flags) داخل الدالة المشتركة لتقرر ما إذا كانت في وضع البحث أم وضع إيجاد الأب وهذا يتعارض مع مبدأ الفصل بين الاهتمامات (Separation of Concerns) في البرمجة. حيث هذا المبدأ أساسي جدا وهو أن يكون لكل جزء من الكود وظيفة حادة وليس أكثر من وظيفة وذلك لجعل التعديل سهل في المستقبل وعدم إعتماد الأكواد على بعضها البعض .

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

الاحظ في بعض الألعاب (القديمة مثل IGI 2000) أن أكثر من شخصية من شخصيات الأعداء تتشارك نفس الصوت و نفس ال(animation) عندما تقتل. هل يضطر المبرمجين لكتابة نفس الأكواد لكلا الشخصيتين المختلفتين.

  • 0
نشر
بتاريخ منذ ساعة مضت قال Abdelrehman Elsied:

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

الاحظ في بعض الألعاب (القديمة مثل IGI 2000) أن أكثر من شخصية من شخصيات الأعداء تتشارك نفس الصوت و نفس ال(animation) عندما تقتل. هل يضطر المبرمجين لكتابة نفس الأكواد لكلا الشخصيتين المختلفتين.

الكود المشترك في دالة والوراثة تنبعان من نفس المبدأ الأساسي وهو Don't Repeat Yourself - DRY لكنهما ليسا نفس الشيء، بل تطبيقان مختلفان لهذا المبدأ في نموذجين برمجيين مختلفين.

فالكود المشترك في البرمجة الإجرائية مثل لغة C، فكرته هي أنه لو لديك مجموعة من الأسطر التي تتكرر في دالتين أو أكثر، تستطيع وضعها في دالة مساعدة جديدة، ثم تستدعي الدالة المساعدة من كل الأماكن التي تحتاجها.

أي علاقة استخدام أو استدعاء فالدالة find_parent والدالة check تستخدمان نفس منطق التنقل في الشجرة،  لمشاركة منطق أو خوارزمية محددة بين عدة دوال.

بينما الوراثة في البرمجة كائنية التوجه OOP مثل C++, Java, Python، فلا تشارك مجرد جزء من الكود، بل تشارك هوية وسلوك كامل، أي طريقة لإنشاء Class جديدة بناءًا على كلاس موجود، فالكلاس الجديد الابن يرث كل الخصائص أي المتغيرات والسلوكيات وهي الدوال من الكلاس الأصل الأب.

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

بتاريخ منذ ساعة مضت قال Abdelrehman Elsied:

الاحظ في بعض الألعاب (القديمة مثل IGI 2000) أن أكثر من شخصية من شخصيات الأعداء تتشارك نفس الصوت و نفس ال(animation) عندما تقتل. هل يضطر المبرمجين لكتابة نفس الأكواد لكلا الشخصيتين المختلفتين.

ليس كذلك، فكتابة الكود بتلك الطريقة غير عملية، بل يستخدمون مفاهيم برمجية مثل الوراثة والمبادئ الخاصة بها مثل Method Overriding.

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

وبما أن Soldier و Sniper و Guard جميعهم يرثون من Enemy، فيحصلون تلقائياً على دالة die() بنفس الكود المشترك دون الحاجة لكتابتها مرة أخرى، وعندما يموت أي منهم في اللعبة، يتم استدعاء نفس الدالة die() من الكلاس الأب، فيصدر نفس الصوت وتحدث نفس الحركة.

وبالنسبة للعدو المميز كزعيم مثلاً له صوت وحركة موت مختلفة؟ يتم استخدام مبدأ تجاوز التابع Method Overriding حيث لكلاس الزعيم أن يرث من Enemy أيضًا، لكن يُعيد كتابة دالة die() الخاصة به لتنفيذ سلوك مختلف.

انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أجب على هذا السؤال...

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.

  • إعلانات

  • تابعنا على



×
×
  • أضف...