فهم كيفية إنشاء رقم عشوائي بالجافا

مستوى الدرس: مبتدئ – لغة البرمجة: جافا (Java)

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

int x = 20 + (int)(Math.random() * 30);

ماذا سيكون جوابك؟ الجواب الصحيح هو أن الكود السابق سينتج أرقام بين 20 و 49.

هل يمكن معرفة الناتج عن طريق اجراء بعض الحسابات؟ بالتأكيد نعم. لنعرف في البداية ماهي حدود دالة Math.random(). الدالة تقوم بإنشاء رقم نوعه (double) أكبر من أو يساوي صفر وأصغر من 1. يعني (الرقم >= 0 و الرقم < 1). مثال على الأرقام التي تقوم بإنشائها هذه الدالة:

0.2045766903616716
0.37790106813735225
0.8582977478804686
0.9393936160319944

بمعرفة السابق، نستطيع أن نحسب الكود السابق بأقل قيمة تستطيع الدالة Math.random() إنشائها، ومرة أخرى نحسب الكود بأعلى قيمة تستطيع الدالة Math.random() إنشائها. أي سنحصل على المعادلتين التاليتين:

أقل قيمة (بتعويض مكان الدالة Math.random بصفر) = 20 ، تم حسابها كالتالي:

x = 20 + (int)(0 * 30);
X = 20 + (int)(0);
x = 20 + 0
x = 20

أعلى قيمة (بتعويض مكان الدالة Math.random برقم يكون أقل من 1 بنسبة بسيطة جداً، مثل 0.9999) = 49 ، تم حسابها كالتالي:

x = 20 + (int)(0.9999 * 30);
X = 20 + (int)(29.99);
x = 20 + 29
x = 49

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

الرقم العشوائي = أدنى قيمة + (دالة إنشاء الرقم العشوائي * ( أعلى قيمة – أدنى قيمة + 1)

int x = min + (int)(Math.random() * (max - min + 1));

إثبات المعادلة السابقة بحساب اقل وأعلى قيمة تستطيع المعادلة إنشائها. نفترض أن قيمة المتغير (min) هو مثلاً 10، وقيمة متغير (max) هو 50. أي أننا نريد إنشاء رقم عشوائي بين 10 و 50.

أقل قيمة (بتعويض Math.random بصفر) = 10 ، تم حسابها كالتالي:

x = 10 + (int)(0 * (50 - 10 + 1));
x = 10 + (int)(0 * (40 + 1));
x = 10 + (int)(0 * 41);
x = 10 + (int)(0);
x = 10 + 0
x = 10

أعلى قيمة (بتعويض Math.random بـ 0.9999) = 50، تم حسابها كالتالي:

x = 10 + (int)(0.9999 * (50 - 10 + 1));
x = 10 + (int)(0.9999 * (40 + 1));
x = 10 + (int)(0.9999 * 41);
x = 10 + (int)(40.99);
x = 10 + 40
x = 50

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

public static int random(int min, int max) {
    return min + (int)(Math.random() * (max - min + 1));
}

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

تم أخذ صورة العرض من wallpaperbetter.com

تمرير دالة كمعطى لدالة في الجافا (Callback & Lambda)

إذا كنت من محبي لغة الجافا سكربت (JavaScript)، قد يبدوا لك مثل هذا الكود وهو عبارة عن تعريف دالة (function) وتستقبل دوال كمعطيات (Arguments) أمر طبيعي ومعتاد:

function check_grade(score, pass_callback, fail_callback) {
    let grade = null;
    if (score >= 90) {
        grade = "A";
    } else if (score >= 80 && score <= 89) {
        grade = "B";
    } else if (score >= 70 && score <= 79) {
        grade = "C";
    }  else if (score >= 60 && score <= 69) {
        grade = "D";
    }

    if (grade == null) {
        fail_callback();
    } else {
        pass_callback(grade);
    }
}

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

لنجرب الدالة السابقة مرتين، مره عند درجة النجاح، ومره عند درجة الرسوب، ونعطي أوامر مختلفة (دالة) في كل مره:

//Test 1
check_grade(81, (grade) => {
    console.log("Congratulation. You got: " + grade);
}, () => {
    console.log("Unfortunately, You failed.");
});

//Test 2
check_grade(55, (grade) => {
    console.log("Yes, you made it. Your grade is " + grade);
}, () => {
    console.log("No, you didn't work hard.");
});

ناتج الكود السابق (Output):

Congratulation. You got: B
No, you didn't work hard.

نلاحظ أنه تم تمرير الدالة كمعطى بطريقة مختصرة بدل الطريقة العادية وهي:

//Test 1
check_grade(81, function(grade) {
    console.log("Congratulation. You got: " + grade);
}, function() {
    console.log("Unfortunately, You failed.");
});

//Test 2
check_grade(55, function(grade) {
    console.log("Yes, you made it. Your grade is " + grade);
}, function() {
    console.log("No, you didn't work hard.");
});

الطريقة المختصرة التي تم اختصار كتابة دالة بها وتمريرها إلى الدالة تسمى (Lambda)، بينما تمرير دالة كمعطى داخل دالة يطلق عليه (callback function).

حسناً، السؤال هنا: هل يمكن تطبيق المثال السابق بلغة الجافا (Java) كما هو في الجافا سكربت؟ الجواب بالتأكيد نعم، ويمكن استخدام كذلك Lambda في الجافا (JDK 1.8).

في البداية، لأستخدام تعبير Lambda، نحتاج إلى تعريف واجهة (Interface) وبهذه الواجهة دالة واحدة فقط بأي اسم، بأي نوع، وباي عدد بارامترات تحتاجها. لتطبيق المثال السابق، سأقوم بعمل هذه الواجهة:

interface Function {
    void apply(String grade);
}

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

    public static void check_grade(int score, Function pass_callback, Function fail_callback) {
        String grade = null;
        if (score >= 90) {
            grade = "A";
        } else if (score >= 80 && score <= 89) {
            grade = "B";
        } else if (score >= 70 && score <= 79) {
            grade = "C";
        } else if (score >= 60 && score <= 69) {
            grade = "D";
        }

        if (grade == null) {
            fail_callback.apply(null);
        } else {
            pass_callback.apply(grade);
        }
    }

لاحظ كيف تم تعريف بارامتر استقبال الدالة وكذلك كيف تم استدعاء الدالة المناسبة من المعطيات داخل الدالة عن طريق الواجهة (Interface) التي قمنا بتعريفها. لنقم الآن بمعرفة كيفية استخدام الدالة التي قمنا بعملها وكيف نكتب تعبير Lambda، كالتالي:

    public static void main(String[] args) {
        //Test 1
        check_grade(81,
                (grade) -> {
                    System.out.println("Congratulation. You got: " + grade);
                },
                (grade) -> {
                    System.out.println("Unfortunately, You failed.");
                }
        );

        //Test 2
        check_grade(55,
                (grade) -> {
                    System.out.println("Yes, you made it. Your grade is " + grade);
                },
                (grade) -> {
                    System.out.println("No, you didn't work hard.");
                }
        );
    }

عند تشغيل البرنامج، سيكون الناتج كالتالي (Output):

Congratulation. You got: B
No, you didn't work hard.

وبهذا تعلمنا كيف نطبق مفهوم Callback Function وكذلك كيفية استخدام Lambda Expressions. في المرفقات تم إرفاق كامل ملف كود المثال الخاص بالجافا والجافا سكربت كذلك.

تم اخذ صورة العرض من callicoder.com

تحويل التاريخ من هجري إلى ميلادي والعكس بـ C#

عندما تتعامل مع التاريخ والوقت في C#.Net ستستخدم غالباً كلاس DateTime. مثلاً عندما تود أخذ التاريخ والوقت الحالي وطباعته في صندوق نص اسمه (txtOut)، ستكتب مثل هذا الأمر:

DateTime dateTime = DateTime.Now;
txtOut.Text = dateTime.ToString();

ناتج الكود السابق سيكون التاريخ والوقت الحالي عند تشغيل الكود، ولكن سيختلف تنسيق التاريخ ونوعه باختلاف اعدادات النظام للمستخدم. مثلاً: لو كان اعدادات تاريخ نظام المستخدم هو الميلادي (تنسيق الولايات المتحدة)، سيكون الناتج مشابه للتالي:

2/8/2019 10:24:04 PM

ولو كان اعدادت تاريخ نظام المستخدم هو الهجري (تنسيق المملكة العربية السعودية)، سيكون الناتج مشابه للتالي:

03/06/40 10:30:38 م

أحياناً، قد يكون هذا الأمر مزعج لك وترغب في توحيد عرض التواريخ بغض النظر عن اعدادات نظام المستخدم. لذلك أن كنت ترغب في تعديل الكود السابق ليظهر لك التاريخ بالهجري (تنسيق المملكة العربية السعودية)، يمكنك كتابة الكود التالي:

DateTime dateTime = DateTime.Now;
DateTimeFormatInfo format = new CultureInfo("ar-sa", false).DateTimeFormat;
format.Calendar = new HijriCalendar();
txtOut.Text = dateTime.ToString(format);

ليعمل الكود السابق، تحتاج إلى اضافة استخدام المكتبة (System.Globalization) في أعلى الملف:

using System.Globalization;

ناتج الكود السابق سيكون بالتقويم الهجري (تنسيق المملكة العربية السعودية) ولن يتم اخذ اعدادات النظام:

يمكنك تغيير التنسيق “ar-sa” في الكود السابق إلى كود البلد الذي توده. على سبيل المثال: “ar-kw” تنسيق دولة الكويت. أما بالنسبة للتاريخ الميلادي، بنفس الطريقة ولكن فقط نقوم بتغيير التقويم للميلادي، وفي حال الرغبة بعرض التاريخ الميلادي بتنسيق الولايات المتحدة (الشهر/اليوم/السنة) يمكن كتابة الكود التالي:

DateTime dateTime = DateTime.Now;
DateTimeFormatInfo format = new CultureInfo("en-us", false).DateTimeFormat;
format.Calendar = new GregorianCalendar(GregorianCalendarTypes.USEnglish);
txtOut.Text = dateTime.ToString(format);

ناتج الكود السابق سيكون بالتاريخ الميلادي:

تعلمنا مما سبق كيف نجلب التاريخ والوقت الحالي ثم نغير التاريخ بتغيير تنسيق التاريخ والتقويم. ولكن كيف لو كان لدينا تاريخ هجري محدد وليس الحالي ونرغب بتحويله لميلادي؟ الأمر بسيط. لنفرض أن التاريخ الهجري الذي نرغب بتحويله لميلادي هو (1/9/1440 هـ)، يمكنك كتابة الكود التالي:

HijriCalendar hijriCalendar = new HijriCalendar();
DateTime dateTime = new DateTime(1440, 9, 1, hijriCalendar);
DateTimeFormatInfo englishFormat = new CultureInfo("en-us", false).DateTimeFormat;
englishFormat.Calendar = new GregorianCalendar(GregorianCalendarTypes.USEnglish);
txtOut.Text = "Gregorian(US):  " + dateTime.ToString(englishFormat);
DateTimeFormatInfo arabicFormat = new CultureInfo("ar-sa", false).DateTimeFormat;
arabicFormat.Calendar = new HijriCalendar();
txtOut.Text += Environment.NewLine + "Hijri(SA):            " + dateTime.ToString(arabicFormat);

ناتج الكود السابق:

كما تلاحظ، نتيجة تحويل تاريخ (1/9/1440 هـ) إلى ميلادي هو (5/5/2019 م). لنجرب الآن التحويل من ميلادي إلى هجري (تحويل 5/5/2019م):

GregorianCalendar gregorianCalendar = new GregorianCalendar(GregorianCalendarTypes.USEnglish);
DateTime dateTime = new DateTime(2019, 5, 5, gregorianCalendar);
DateTimeFormatInfo arabicFormat = new CultureInfo("ar-sa", false).DateTimeFormat;
arabicFormat.Calendar = new HijriCalendar();
txtOut.Text = "Hijri(SA):            " + dateTime.ToString(arabicFormat);
DateTimeFormatInfo englishFormat = new CultureInfo("en-us", false).DateTimeFormat;
englishFormat.Calendar = new GregorianCalendar(GregorianCalendarTypes.USEnglish);
txtOut.Text += Environment.NewLine + "Gregorian(US):  " + dateTime.ToString(englishFormat);

نتيجة الكود السابق:

بهذه الطريقة يتم التبديل بين التواريخ في C#.Net، وهذه تعتبر أسهل طريقة بدون الحاجة لكتابة أو إضافة مكتبات خارجية لتحويل التواريخ إلى تقويمات مختلفة.

صورة العرض من موقع larutadelsorigens.ca