البحث في الموقع
المحتوى عن 'المتغيرات'.
-
في مقال اليوم نقدم دليلًا سريعًا مدعمًا بالأمثلة العملية شرح لغة PHP التي تعد واحدة من لغات البرمجة القوية المستخدمة بشكل أساسي لتطوير الويب، فعلى الرغم من ظهور العديد من لغات البرمجة الأحدث في مجال تطوير الويب إلا أن لغة PHP لا تزال اللغة الأكثر شيوعًا واستخدامًا، وهي تتطور باستمرار لإصلاح أي عيوب أو أخطاء فيها وتعزيز أمان وأداء المواقع المبنية باستخدامها. نبذة مختصرة عن لغة PHP يشير اسم PHP إلى عبارة Hypertext Pre-processor أي المعالج المسبق للنصوص الفائقة وكلمة النصوص الفائقة تعني النصوص التي تملك روابط تنقلك لنصوص أو صفحات صفحات ويب أخرى عند النقر فوقها. وهي لغة برمجة مفتوحة المصدر وشائعة الاستخدام تستخدم في إنشاء وتطوير مواقع الويب الديناميكية، وقد طور باستخدامها أكثر من 75% من المواقع الموجودة على شبكة الإنترنت بحسب w3techs. تتميز لغة PHP بسهولتها ودعمها لمجموعة متنوعة من قواعد البيانات وبروتوكولات الإنترنت وتوفيرها للعديد من مكتبات وأطر العمل المساعدة للمبرمجين، وما زاد من شهرتها وحافظ على مكانتها وأهميتها بين مطوري الويب اعتماد العديد من منصات إدارة المحتوى ومنصات التجارة الإلكترونية المشهورة عليها مثل ووردبريس WordPress ودروبال Drupal وماجنتو Magneto وغيرها الكثير، فإذا كنت مهتمًا بمجال تطوير الويب فإن تعلم لغة PHP خيار مناسب وغني بالفرص. تعمل لغة PHP على الخادم server-side بخلاف لغات برمجة أخرى تعمل على المتصفح أو العميل client-side مثل لغة جافا سكريبت JavaScript أي أنها لا تنفذ الشيفرة البرمجية في متصفح الويب، بل تشغلها مباشرة على خادم الويب وبعدها يرسل خادم الويب نتيجة التنفيذ إلى المتصفح لعرضها، وتفترض هذه المقالة أنك تمتلك معلومات أساسية عن لغة PHP وطريقة عملها في الخلفية عند زيارتك لصفحة ويب من خلال متصفح الويب الخاص بك، وبأنك تعرف مسبقًا كيف تحول جهاز حاسوبك إلى خادم محلي وكيف تعد كل الأدوات اللازمة لكتابة أوامر PHP وتنفيذها، فإذا لم تكن على دراية بهذه المعلومات الأساسية أنصحك قبل ذلك ان تقرأ مقال تعرف على لغة PHP ثم تتابع قراءة هذا المقال. ملاحظة: تعمل الأكواد الواردة في الفقرات التالية مع إصدار PHP 5 والإصدارات اللاحقة فقد قدم الإصدار الخامس من PHP العديد من التغييرات والتحسينات المهمة على الإصدارات السابقة وأبرز هذه التغييرات دعم البرمجة كائنية التوجه oop في PHP ووفر العديد من تحسينات الأداء والأمان، وتابعت الإصدارات اللاحقة التحسين وأضافت ميزات جديدة للغة. كيف تكتب أكواد PHP تكتب أكواد PHP بعد وسم الفتح php?> وقبل وسم الإغلاق <? ثم تحفظ داخل ملفات لها الامتداد .php على خادم الويب، ومن الأفضل عمليًا تجاهل كتابة وسم الإغلاق إذا كان ملف php يحتوي على شفرة php فقط، لتلافي نتيجة خرج غير متوقعة. <?php echo "Hello World!"; ?> كما يمكن أن تدمج أكواد PHP مع أكواد HTML لإنشاء صفحات ويب ديناميكية بالشكل التالي: <!DOCTYPE html> <html> <head> <title> HTML and PHP </title> </head> <body> <h1>PHP code mixed with HTML</h1> <?php echo "Hello World!"; ?> </body> </html> التعليقات Comments التعليقات هي أجزاء من الكود البرمجي لا تنفذ عند تشغيل البرنامج وهي تستخدم لشرح وتوثيق الكود وتوضيح عمله للمطورين الآخرين الذين يقرأون هذا الكود، أو لتوضيحها للمبرمج الذي كتبها نفسه فقد ينسى ما كتبه عند العودة لقراءة الكود بعد فترة من الزمن. يمكن كتابة التعليقات في لغة PHP بعدة طرق كما في المثال التالي: // استخدم علامة القوسين المائلين // لبدأ تعليق بسطر واحد // يمكن استخدام العلامة # لكن علامة // أكثر شيوعيا. /* إحاطة نص بالقوس المائل متبوعا بالنجمة بداية وبالنجمة متبوعة بالقوس المائل نهاية يجعل من هذا النص تعليقا متعدد الأسطر */ طباعة الخرج في PHP يمكنك استخدم تعليمة echo أو print لطباعة الخرج أو عرض البيانات المطلوبة على صفحة الويب. print('Hello '); // بدون سطر فاصل "Hello " طباعة استخدام القوسين ( ) اختياري مع تعليمتي echo و print echo "World\n"; // مع سطر فاصل "World" طباعة يجب أن تنتهي كل العبارات في لغة PHP بالفاصلة المنقوطة ";" وأي شيء مكتوب خارج وسوم php?> ستتم طباعته على الشاشة بشكل تلقائي. دورة تطوير تطبيقات الويب باستخدام لغة PHP احترف تطوير النظم الخلفية وتطبيقات الويب من الألف إلى الياء دون الحاجة لخبرة برمجية مسبقة اشترك الآن الأنماط Types والمتغيرات Variables تستخدم المتغيرات في PHP لتخزين وحفظ قيم البيانات، ويمكن أن تخزن هذه المتغيرات قيمًا مختلفة كالأرقام والنصوص والقيم المنطقية. يمكنك التصريحعن المتغيرات في PHP بكتابة الرمز $ يليه مباشرة اسم المتغير، ويجب ان تبدأ أسماء المتغيرات تبدأ بحرف أو تسطيرة سفلية _ يتبعها بأي عدد من الأحرف أو الأرقام أو التسطيرات السفلية. وتكون أسماء المتغيرات حساسة لحالة الأحرف case-sensitive. من أهم أنماط المتغيرات في لغة PHP نذكر: المتغيرات المنطقية Boolean ويملك المتغير من هذا النمط القيمة true أي صحيح أو القيمة false أي خاطئ. $boolean = true; // TRUE أو True $boolean = false; // FALSE أو False الأعداد الصحيحة Integer حيث يملك المتغير من هذا النمط قيمة صحيحة موجبة أو سالبة $int1 = 12; // => 12 $int2 = -12; // => -12 $int3 = 012; // => 10 (البادئة 0 تدل على عدد ثماني) $int4 = 0x0F; // => 15 (0x تدل على رقم ستة عشري) المحارف الصحيحة الثنائية Binary integer literals متاحة اعتبارًا من PHP 5.4.0 وهي تستخدم لتمثيل الأعداد الصحيحة بالنظام الثاني $int5 = 0b11111111; // 255 (0b يدل على عدد ثنائي) الأرقام العائمة Floats أو (المضاعفة doubles) وهي تستخدم لتمثيل الأعداد العشرية $float = 1.234; $float = 1.2e3; $float = 7E-10; لحذف متغير في PHP وتحرير موارد الذاكرة المحجوزة لأجله نستخدم تعليمة unset unset($int1); يمكن إجراء العمليات الحسابية على المتغيرات العددية كما يلي: $sum = 1 + 1; // 2 $difference = 2 - 1; // 1 $product = 2 * 2; // 4 $quotient = 2 / 1; // 2 كما يمكن إجراء العمليات الحسابية المختزلة بالشكل التالي: $number = 0; $number += 1; // بمقدار 1 $number زيادة echo $number++; // (طباعة 1 (الزيادة بعد التقييم echo ++$number; // (طباعة 3 (الزيادة قبل التقييم $number /= $float; // $number قسمة واسناد ناتج القسمة إلى السلاسل النصية Strings وهي تستخدم لتخزين النصوص ويجب أن تكون النصوص محدودة ضمن إشارة تنصيص واحدة ’ من كل جانب كما يلي: $sgl_quotes = '$String'; // => '$String' تجنب استخدام علامات التنصيص المزدوجة إلا في حالة تضمين متغيرات أخرى $dbl_quotes = "This is a $sgl_quotes."; // => 'This is a $String.' تحدد المحارف الخاصة حصريًا باقتباسات مزدوجة $escaped = "This contains a \t tab character."; $unescaped = 'This just contains a slash and a t: \t'; قم بإحاطة المتغيرات بالقوسين المائلين عند الحاجة $apples = "I have {$number} apples to eat."; $oranges = "I have ${number} oranges to eat."; $money = "I have $${number} in the bank."; منذ الإصدار الخامس يمكن استخدام nowdocs في حالة عدة أسطر غير مجمعة $nowdoc = <<<'END' Multi line string END; ستقوم heredocs بتجميع النصوص $heredoc = <<<END Multi line $sgl_quotes END; يتم وصل النصوص أو دمجها باستخدام النقطة "." echo 'This string ' . 'is concatenated'; يمكن تمرير النصوص كبارامتر لـ echo echo 'Multiple', 'Parameters', 'Valid';// 'MultipleParametersValid' يعيد الثوابت في PHP يعرّف الثابت باستخدام ()define ولا يمكن تغيير قيمته خلال فترة التشغيل. ويجب أن تبدأ أسماء الثوابت الصحيحة بحرف أو تسطيرة سفلية, متبوعة بأية حروف أو أرقام أو تسطيرات سفلية. define("FOO", "something"); الوصول إلى المتغير الثابت متاح دون الحاجة الى استخدام الرمز $ echo FOO; // 'something' يعيد echo 'This outputs ' . FOO; // 'This outputs something' يعيد المصفوفات Arrays المصفوفات في PHP هي هياكل بيانات تسمح بتخزين مجموعة من القيم ضمن متغير واحد، وكل المصفوفات في PHP هي مصفوفات مترابطة (تسمى hashmaps في بعض اللغات البرمجية) وتعمل في جميع إصدارات php $associative = array('One' => 1, 'Two' => 2, 'Three' => 3); تم إدخال تركيب قواعدي syntax جديد في PHP 5.4 $associative = ['One' => 1, 'Two' => 2, 'Three' => 3]; echo $associative['One']; // 1 تطبع إضافة عنصر للمصفوفة المترابطة $associative['Four'] = 4; القوائم المحرفية يسند لها مفاتيح صحيحة حكمًا $array = ['One', 'Two', 'Three']; echo $array[0]; // => "One" إضافة عنصر إلى نهاية المصفوفة $array[] = 'Four'; أو array_push($array, 'Five'); حذف عنصر من مصفوفة unset($array[3]); المزيد حول تعلميات الطباعة في PHP echo('Hello World!'); تقوم التعليمة السابقة بطباعة !Hello World على شاشة الاظهار القياسية stdout، وstdout هي صفحة الوب إذا كنت تستخدم متصفحًا print('Hello World!'); // echo مشابه لـ // هي أيضًا من تركيبات اللغة لذلك يمكنك إلغاء الأقواس echo و print echo 'Hello World!'; print 'Hello World!'; $paragraph = 'paragraph'; echo 100; // اطبع المتغيرات العددية مباشرة echo $paragraph; // أو المتغيرات إذا تم إعداد الوسوم القصيرة المفتوحة أو كانت نسخة PHP الخاصة بك 5.4.0 أو أحدث يمكنك استخدام قواعد بناء الجمل القصيرة لـ echo <p><?= $paragraph ?></p> $x = 1; $y = 2; $x = $y; المتغير x$ يحتوي نفس قيمة المتغير y$ $z = &$y; المتغير z$ يحتوي إسناد إلى قيمة y$، وأي تغيير في قيمة z$سيغير قيمة y$ وبالعكس المتغير x$ تبقى قيمته بدون تغيير كقيمة y$ الأصلية echo $x; // => 2 echo $z; // => 2 $y = 0; echo $x; // => 2 echo $z; // => 0 تخرج التفريغات Dumps نوع وقيمة المتحول إلى شاشة العرض القياسية stdout var_dump($z); // int(0) يطبع تطبع التعليمة Print المتغيرات إلى شاشة الإظهار القياسية stdout بصيغة مقروءة للإنسان print_r($array); // Array ( [0] => One [1] => Two [2] => Three ) يطبع العمليات المنطقية $a = 0; $b = '0'; $c = '1'; $d = '1'; يقوم assert بإصدار تنبيه إذا كانت معطياته غير صحيحة وهذه المقارنة ستكون دومًا صحيحة حتى إذا كانت الأنواع مختلفة assert($a == $b); // equality المساواة assert($c != $a); // inequality عدم المساواة assert($c <> $a); // alternative inequality عدم المساواة بشكل بديل assert($a < $c); assert($c > $b); assert($a <= $b); assert($c >= $d); التالي سيكون صحيحًا فقط في حال كانت القيم متساوية ومن نفس النوع assert($c === $d); assert($a !== $d); assert(1 === '1'); assert(1 !== '1'); المشغل `Spaceship` <=> (منذ PHP 7) يعيد 0 إذا كانت القيم على طرفيه متساوية، ويعيد 1 إذا كانت القيمة على اليسار أكبر، ويعيد -1 إذا كانت القيمة على اليمين أكبر $a = 100; $b = 1000; echo $a <=> $a; // النتيجة 0 كونهما متساويين echo $a <=> $b; // $a < $b النتيجة -1 كون echo $b <=> $a; // $b > $aالنتيجة 1 كون يمكن تحويل المتغيرات إلى أنواع مختلفة بحسب استخدامها $integer = 1; echo $integer + $integer; // => 2 $string = '1'; echo $string + $string; // => 2 (تم تحويل النصوص الى أعداد صحيحة) $string = 'one'; echo $string + $string; // => 0 النتيجة 0 لأن المشغل + لا يستطيع موائمة النص `one` الى رقم. موائمة الأنواع Type casting يمكن استخدامها للتعامل مع متغير من نوع معين كنوع آخر. $boolean = (boolean) 1; // => true $zero = 0; $boolean = (boolean) $zero; // => false يوجد أيضًا توابع مخصصة لموائمة أغلب الأنواع $integer = 5; $string = strval($integer); null هي قيمة خالية $var = null; بنى التحكم Control Structures تمكنك بنى التحكم في لغة PHP من التحكم في تنفيذ الكود البرمجي بناءً على شروط معينة أو تكرار تنفيذ جزء من الكود عدة مرات ومن أشهر بنى التحكم المستخدمة في لغة PHP العبارات الشرطية if وعبارات الحلقات مثل for و while وعبارات switch. if (true) { print 'I get printed'; } if (false) { print 'I don\'t'; } else { print 'I get printed'; } if (false) { print 'Does not get printed'; } elseif (true) { print 'Does'; } المشغل الثلاثي ternary print (false ? 'Does not get printed' : 'Does'); بدأ استخدام المشغل الثلاثي المختصر من PHP 5.3 وهو مكافئ لـ "$x ? $x : 'Does'"" $x = false; print($x ?: 'Does'); بدأ مشغّل دمج القيم الفارغة null coalesce operator من php 7 $a = null; $b = 'Does print'; echo $a ?? 'a is not set'; // 'a is not set' تطبع echo $b ?? 'b is not set'; // 'Does print' تطبع $x = 0; if ($x === '0') { print 'Does not print'; } elseif ($x == '1') { print 'Does not print'; } else { print 'Does print'; } القواعد البديلة لبناء الجمل مفيدة لبناء النماذج <?php if ($x): ?> This is displayed if the test is truthy. <?php else: ?> This is displayed otherwise. <?php endif; ?> استخدم التعليمة switch لتوفير كتابة جزء من العمليات المنطقية كما يلي switch ($x) { case '0': print 'Switch does type coercion'; break; // 'two' و'three' وإلا سينتقل الى الحالة break يجب تضمين case 'two': case 'three': // 'two' أو 'three' تساوي $variable قم بعمل شيء ما اذا كانت قيمة break; default: // قم بعمل الاجراء الافتراضي } الحلقات while، do…while، for هي حلقات مألوفة غالبًا وتستخدم لتكرار تنفيذ مجموعة من التعليمات البرمجية كما يلي: $i = 0; while ($i < 5) { echo $i++; } // "01234" اطبع $i = 0; do { echo $i++; } while ($i < 5); // "01234" اطبع for ($x = 0; $x < 10; $x++) { echo $x; } // "0123456789" اطبع $wheels = ['bicycle' => 2, 'car' => 4]; يمكن أن تعمل حلقات foreachعلى المصفوفة بالشكل التالي: foreach ($wheels as $wheel_count) { echo $wheel_count; } // "24"اطبع ويمكنك لحلقة foreach المرور إما عبر المفاتيح أو القيم الخاصة بالمصفوفة foreach ($wheels as $vehicle => $wheel_count) { echo "A $vehicle has $wheel_count wheels"; } $i = 0; while ($i < 5) { if ($i === 3) { break; // while اخرج من حلقة } echo $i++; } // "012" اطبع for ($i = 0; $i < 5; $i++) { if ($i === 3) { continue; // تجاوز هذا التكرار من الحلقة } echo $i; } // "0124" اطبع الدوال Functions الدوال في البرمجة هي مجموعات من الأوامر التي لها اسم محدد وتستخدم لأداء مهمة معينة وهي تساعد في تنظيم واختصار التعليمات البرمجية. ونعرّف الدالة PHP باستخدام الكلمة المفتاحية function function my_function () { return 'Hello'; } echo my_function(); // => "Hello" الاسم المقبول للدالة يجب أن يبدأ بحرف أو تسطيرة سفلية متبوعة بأي عدد من الأحرف ،الأرقام، أو التسطيرات السفلية function add ($x, $y = 1) { // اختيارية وقيمتها الإفتراضية تساوي 1 $y $result = $x + $y; return $result; } echo add(4); // => 5 echo add(4, 2); // => 6 لا يمكن الوصول للمتغير result$ من خارج الدالة واستخدام الأمر ;print $result هنا سيعطي خطأ منذ النسخة PHP 5.3 أصبح من الممكن تعريف دوال مجهولة anonymous functions أي دوال لا تملك اسمًا وتخزن قيمها في متغيرات كما يلي: $inc = function ($x) { return $x + 1; }; echo $inc(2); // => 3 function foo ($x, $y, $z) { echo "$x - $y - $z"; } يمكن للدوال أن تعيد دوالًا أخرى function bar ($x, $y) { // استخدم use لإحضار المتغيرات الخارجية return function ($z) use ($x, $y) { foo($x, $y, $z); }; } $bar = bar('A', 'B'); $bar('C'); // "A - B - C" يطبع يمكنك استدعاء الدوال المسماة named functions باستخدام النصوص $function_name = 'add'; echo $function_name(1, 2); // => 3 وهو هام من أجل تحديد أي دالة يتوجب تشغيلها برمجيًا، أو استخدم الدالة التالية call_user_func (callable $callback [, $parameter [, … ]]); يمكنك الحصول على كل البارمترات الممررّة للدالة function parameters() { $numargs = func_num_args(); if ($numargs > 0) { echo func_get_arg(0) . ' | '; } $args_array = func_get_args(); foreach ($args_array as $key => $arg) { echo $key . ' - ' . $arg . ' | '; } } parameters('Hello', 'World'); // Hello | 0 - Hello | 1 - World | يمكنك الحصول على عدد متغير من المعطيات ابتداء من PHP 5.6 function variable($word, ...$list) { echo $word . " || "; foreach ($list as $item) { echo $item . ' | '; } } variable("Separate", "Hello", "World"); // Separate || Hello | World | التضمين في PHP باستخدام include و require يفيد التضمين في PHP لتضمين محتوى ملف في ملف آخر ويمكن القيام بتضمين ملف في آخر باستخدام include كما يلي: include 'my-file.php'; وبهذا تكون الشفرة ضمن الملف my-file.php متاحة ضمن مجال الرؤية، إذا لم يكن بالإمكان تضمين الملف (على سبيل المثال إذا كان الملف غير موجود ) يظهر تنبيه. include_once 'my-file.php'; إذا كانت شفرة الملف my-file.php متضمنة في مكان أخر ، لن يتم تضمينها ثانية. وهذا يمنع الاخطاء التي تحدث بسبب تكرار التصريح عن الصفوف require 'my-file.php'; require_once 'my-file.php'; تقوم الدالة ()require بنفس عمل ()include إلا أنها ستتسبب بخطأ فادح إذا لم يتمكن من تضمين الملف. محتويات ملف my-include.php <?php return 'Anything you like.'; يمكن الدوال Includes، requires أيضًا أن تعيد قيمة $value = include 'my-include.php'; تضمن الملفات بالاعتماد على المسار المعطى، وإذا لم يحدد المسار يتم اعتماد مسار توجيه الاعداد include_path فإذا لم يتم إيجاد الملف يتم البحث في مسار استدعاء السكريبت ومسار العمل الحالي قبل الاعلان عن الاخفاق الأصناف Classes في PHP الأصناف classes في البرمجة هي أنماط بيانات خاصة تستخدم كقوالب لإنشاء الكائنات objects وتعرف الأصناف في PHP باستخدام العبارة class class MyClass { const MY_CONST = 'value'; // ثابت static $staticVar = 'static'; المتغيرات الثابتة Static ومجال رؤيتها public static $publicStaticVar = 'publicStatic'; يمكن الوصول إليها خلال الصنف فقط private static $privateStaticVar = 'privateStatic'; يمكن الوصول اليها من خلال الصنف والأصناف المشتقة protected static $protectedStaticVar = 'protectedStatic'; يجب تحديد مجال الرؤية للخصائص Properties public $property = 'public'; public $instanceProp; protected $prot = 'protected'; يمكن الوصول إليها من الصنف والأصناف الفرعية private $priv = 'private'; // يمكن الوصول إليها من الصنف فقط قم بإنشاء الباني باستخدام __construct public function __construct($instanceProp) { // $this قم بالوصول إلى متغيرات الحالة باستخدام $this->instanceProp = $instanceProp; } تعرًف الطرق methods كدوال داخل الصنف public function myMethod() { print 'MyClass'; } تجعل الكلمة المفتاحية final الدالة غير قابلة للتحميل الزائد unoverridable final function youCannotOverrideMe() { } الطرق السحرية Magic Methods الطرق السحرية في PHP هي طرق أو وظائف خاصة يبدأ اسمها بشرطة سفلية مزدوجة تسمح لك بإضافة وظائف إلى الأصناف دون الحاجة لتعريف هذه الدوال بشكل صريح. على سبيل المثال الدالة toString__ هي دالة سحرية تستدعى تلقائيًا عند استخدام عمليات أو وظائف معينة تتطلب تحويل الكائن إلى سلسلة نصية public function __toString() { return $property; } ومن الدوال السجرية لدينا دالة الهدم ()destruct__ وهي عكس دالة البناء()construct__ وتستدعى عندما لا تعود هنالك حاجة لأن يكون العنصر مشارًا إليه كمرجع public function __destruct() { print "Destroying"; } الكلمة المفتاحية static في PHP تستخدم الكلمة المفتاحية static في PHP لتعريف خصائص الصنف والطرق على أنها ثابتة وهذا يجعلها قابله للوصول بدون الحاجة إلى إنشاء كائن من الصنف. الخاصية المعرّفة بأنها ثابتة لا يمكن الوصول إليها من العنصر المشتق من الصنف (لكن يمكن ذلك في الطرق الثابتة) public static function myStaticMethod() { print 'I am static'; } ثوابت الصنف Class constants يمكن الوصول إليها دائمًا بشكل ثابت echo MyClass::MY_CONST; // 'value' الخرج; echo MyClass::$staticVar; // 'static' الخرج; MyClass::myStaticMethod(); // 'I am static' الخرج; اشتقاق حالات الأصناف instance باستخدام new $my_class = new MyClass('An instance property'); استخدام القوسين اختياري إذا كنت لا تريد تمرير أيّة معطيات يمكننك الوصول إلى أعضاء الصنف class members باستخدام <- echo $my_class->property; // => "public" echo $my_class->instanceProp; // => "An instance property" $my_class->myMethod(); // => "MyClass" قم بتوسيع الصنف باستخدام extends class MyOtherClass extends MyClass { function printProtectedProperty() { echo $this->prot; } // التحميل الزائد للطرق Override function myMethod() { parent::myMethod(); print ' > MyOtherClass'; } } $my_other_class = new MyOtherClass('Instance prop'); $my_other_class->printProtectedProperty(); // => "protected" يطبع $my_other_class->myMethod(); // "MyClass > MyOtherClass" يطبع final class YouCannotExtendMe { } يمكنك استخدام الطرق السحرية magic methods لإنشاء دوال الجلب أو الإرجاع getters وداول الاسناد setters class MyMapClass { private $property; public function __get($key) { return $this->$key; } public function __set($key, $value) { $this->$key = $value; } } $x = new MyMapClass(); echo $x->property; // __get() سوف تستخدم طريقة $x->property = 'Something'; // __set() سوف تستخدم طريقة يمكن أن تكون الأصناف تجريدية (باستخدام الكلمة المفتاحية abstract) أو واجهات تنفيذ (باستخدام الكلمة المفتاحية implements). الواجهات Interfaces في لغة PHP الواجهات في PHP هي طريقة مجردة لتعريف الكائنات، ويصرح عن الواجهة بالكلمة المفتاحية interface. interface InterfaceOne { public function doSomething(); } interface InterfaceTwo { public function doSomethingElse(); } يمكن أن توسّع الواجهات interface InterfaceThree extends InterfaceTwo { public function doAnotherContract(); } abstract class MyAbstractClass implements InterfaceOne { public $x = 'doSomething'; } class MyConcreteClass extends MyAbstractClass implements InterfaceTwo { public function doSomething() { echo $x; } public function doSomethingElse() { echo 'doSomethingElse'; } } يمكن أن يكون للأصناف أكثر من واجهة واحدة class SomeOtherClass implements InterfaceOne, InterfaceTwo { public function doSomething() { echo 'doSomething'; } public function doSomethingElse() { echo 'doSomethingElse'; } } السمات Traits trait هي ميزة تستخدم للسماح بإعادة استخدام مجموعة من الوظائف داخل الأصناف ويمكنك اعتبارها بمثابة طريقة لتضمين سلوك مشترك بين الأصناف وهي ميزة متوفره بدءًا من النسخة PHP 5.4.0 ويصرّح عنها باستخدام trait trait MyTrait { public function myTraitMethod() { print 'I have MyTrait'; } } class MyTraitfulClass { use MyTrait; } $cls = new MyTraitfulClass(); $cls->myTraitMethod(); // Prints "I have MyTrait" فضاءات الأسماء Namespaces في PHP تستخدم فضاءات الأسماء في PHP لتنظيم الشيفرة وتجنب تعارض أسماء الأصناف والوظائف والمتغيرات فهي تجمع العناصر ضمن في مجموعات أو مساحات لها نفس الاسم لتميزها عن غيرها وللقيام بذلك نكتب namespace كأول عبارة في الملف. دعونا نتظاهر بأن هذا ليس هو الحال <?php افتراضيًا, توجد الأصناف في namespace عام, ويمكن أن تستدعى صراحة بـ backslash $cls = new \MyClass(); ضبط namespace لملف namespace My\Namespace; class MyClass { } من ملف آخر $cls = new My\Namespace\MyClass; أو من داخل namespace آخر namespace My\Other\Namespace; use My\Namespace\MyClass; $cls = new MyClass(); أو يمكنك استبدال namespace باسم مستعار namespace My\Other\Namespace; use My\Namespace as SomeOtherNamespace; $cls = new SomeOtherNamespace\MyClass(); الربط الساكن المتأخر Late Static Binding class ParentClass { public static function who() { echo "I'm a " . __CLASS__ . "\n"; } public static function test() { // على الصف الذي عُرّف التابع ضمنه self تؤشر self::who(); // على الصف الذي استدعي التابع منه static تؤشر static::who(); } } ParentClass::test(); /* ParentClass أنا ParentClass أنا */ class ChildClass extends ParentClass { public static function who() { echo "But I'm " . __CLASS__ . "\n"; } } ChildClass::test(); /* ParentClass أنا ChildClass ولكن أنا */ الثوابت السحرية Magic constants الثوابت السحرية هي ثوابت معرفة مسبقًا في PHP يبدأ اسمها وينتهي بشرطتين سفليتين ولها استخداماات مختلفة، على سبيل المثال للحصول على اسم الصنف الحالي نستخدم الثابت السحري __CLASS__ داخل تصريح الصنف كما يلي: echo "Current class name is " . __CLASS__; وللحصول على مسار الدليل الكامل لملف نستخدم __DIR__ echo "Current directory is " . __DIR__; الاستخدام النموذجي require __DIR__ . '/vendor/autoload.php'; الحصول على المسار الكامل للملف echo "Current file path is " . __FILE__; الحصول على اسم الدالة الحالية echo "Current function name is " . __FUNCTION__; الحصول على رقم السطر الحالي echo "Current line number is " . __LINE__; الحصول على اسم الطريقة الحالية. يرجع قيمة فقط عندما يستخدم داخل تصريح سمة أو كائن echo "Current method is " . __METHOD__; الحصول على اسم namespace الحالي echo "Current namespace is " . __NAMESPACE__; الحصول على اسم السمة الحالية. ترجع قيمة فقط عندما تستخدم داخل التصريح عن السمة أو الكائن. echo "Current trait is " . __TRAIT__; معالجة الأخطاء Error Handling في PHP يمكن أن تتم معالجة أخطاء بسيطة باستخدام كتلة try catch try { // افعل شيء ما } catch (Exception $e) { // معالجة استثناء } عند استخدام كتلة try catch في بيئة namespace استخدم التالي try { // افعل شيء ما } catch (\Exception $e) { // معالجة استثناء } استثناءات مخصّصة class MyException extends Exception {} try { $condition = true; if ($condition) { throw new MyException('Something just happened'); } } catch (MyException $e) { // معالجة استثنائية } ترجمة -وبتصرّف- للمقال Learn PHP in Y Minutes
-
ما هي PHP؟ PHP هي اختصارٌ للعبارة PHP: Hypertext Preprocessor؛ وهي معروفةٌ كلغة سكربتات (scripting language) مُفسَّرة (interpreter) من جهة الخادوم تُستعمَل لتطوير الويب؛ وهذا يعني أنَّ PHP تُستعمَل على الخادوم لتوليد صفحات الويب الديناميكية. ما يُقصَد بصفحات الويب الديناميكية هي تلك الصفحات التي تُنشَأ أو تُعدَّل من الخادوم قبل إرسالها إلى العميل. إن لم تكن تعرف ما هو الخادوم وما هو العميل، فافتح لسانًا جديدًا في متصفح الويب الخاص بك واكتب فيه academy.hsoub.com، وبهذا يُصبِح متصفحك "عميلًا" (client) والمكان الذي تأتي منه صفحة الويب المطلوبة هو "الخادوم" (خادوم الويب أو web server). يمكن تضمين سكربتات PHP في مستندات HTML، وهي لغةٌ سهلة التعلم، وتشبه بنيتها كثيرًا البنية القاعدية للغات C و Java و Perl. التثبيت يمكنك بسهولة تثبيت WAMP (على نظام ويندوز) أو LAMP (على لينُكس) أو حزمة البرمجيات XAMPP (التي تعمل على ويندوز، ولينُكس، وماك) وذلك تبعًا لنظام تشغيل حاسوبك، راجع مقال كيفية تنصيب ووردبريس محليا باستخدام MAMP لمزيدٍ من المعلومات عن MAMP. تجربة الأمثلة يمكنك تجربة سكربتات PHP عبر تثبيت خادم أباتشي مع دعم PHP يدويًا أو عبر تثبيت حزم جاهزة كالتي ذكرناها آنفًا، لكن توفِّر PHP لك طريقتين لتنفيذ الملفات: عبر سطر الأوامر، وعبر الخادوم المدمج مع PHP؛ وسنشرح كلا الطريقتين في هذا الدرس. استخدام الخادوم المدمج وفَّرت PHP من الإصدار 5.4.0 خادومًا مدمجًا معها يُسهِّل تجربة السكربتات دون عبء تثبيت خادوم ويب كامل مثل أباتشي وضبطه؛ لكن الغرض منه هو تجربة البرامج فقط وليس مصممًا للاستعمال كخادوم إنتاجي على الشبكة. ستُخدَّم الملفات الموجودة في مجلد العمل الحالي عبر ذاك الخادوم المدمج، الذي يُشغَّل كالآتي: cd ~/public_html php -S localhost:8000 PHP 5.5.9-1ubuntu4.5 Development Server started at Fri Jan 8 13:58:31 2016 Listening on http://localhost:8000 Document root is /home/user/public_html Press Ctrl-C to quit. الأمر php -S localhost:8000 سيشغِّل الخادوم في مجلد العمل الحالي (~/public_html) وسيكون متاحًا للوصول من الجهاز المحلي فقط، وسيعمل على المنفذ 8000؛ إن أردت أن يعمل على البطاقات الشبكية، فضع 0.0.0.0 بدلًا من localhost؛ اضغط على Ctrl+C لإيقاف الخادوم. أنشِئ الملف test.php وضع فيه المحتويات الآتية لتجربه: <?php // هذه تجربة لتنفيذ سكربتات PHP من الخادوم المدمج echo "test"; ?> افتح الآن المتصفح وأدخِل http://localhost:8000/test.php في شريط العنوان، وستحصل على نتيجة مشابهة لما يلي: يجدر بالذكر أنَّه بإمكانك تخديم صفحات HTML الثابتة عبر هذا الخادوم، وليس بالضرورة أن تكون الصفحة بلاحقة PHP. ملاحظة: اختر رقمًا أكبر من 1024 كرقم للمنفذ الذي سيستعمله الخادوم، كي لا تحتاج إلى امتيازات إدارية لتشغيله. استعمال سطر الأوامر من البدهي أن تستطيع تنفيذ شيفرات PHP (كغيرها من لغات السكربتات) من سطر الأوامر؛ كل ما عليك فعله هو استدعاء السكربت من الأمر PHP كالآتي: php ~/public_html/test.php أو يمكن تضمين ما يسمى shebang في الأنظمة الشبيهة بيونكس (كنظامَي لينُكس وماك) في بداية السكربت ليبدو كما يلي: #!/usr/bin/php <?php // هذه تجربة لتنفيذ سكربتات PHP من سطر الأوامر echo "test"; ?> وعندها تستطيع تنفيذ السكربت مباشرةً دون استدعاء مفسر PHP كما يلي: ~/public_html/test.php ملاحظة: لا تنسَ إعطاء الملف امتيازات التنفيذ قبل محاولة تنفيذه: chmod +x ~/public_html/test.php يمكن أيضًا تنفيذ الشيفرات تفاعليًا، عبر تنفيذ الأمر php -a ثم كتابة الأوامر تفاعليًا كما يظهر في الصورة الآتية (لاحظ أنه بإمكانك استعمال زر tab لإكمال أسماء الدوال والأصناف وغيرها): البنية القاعدية للغة لنأخذ مثالًا بسيطًا لنشرح فيه القواعد الأساسية للكتابة. <?php /* author: Abd Allatif Eymsh website: academy.hsoub.com version: 1.0 date: 22 12 2015 */ // هذا تعليقٌ ذو سطرٍ وحيد // يتم تجاهل التعليقات من المفسِّر $variable = 'variable'; // نستعمل عبارة echo لطباعة الجمل echo "string"; echo $variable; ?> انسخ ما سبق والصقه في ملفٍ باسم index.php في مجلد htdocs (في نظام ويندوز) أو www (في لينُكس) أو أي مكان تريده إن كنت تستعمل الخادوم المدمج، ثم افتح ذاك الملف في متصفح الويب (بعد أن تُشغِّل الخادوم) وستحصل على النتيجة "stringvariable"، لنُقسِّم الآن الملف ولنشرح بنيته. تُكتَب شيفرات PHP في ملفٍ بامتداد .php تُكتَب شيفرات PHP بين وسمَيّ البداية <?php والنهاية ?> وكما في لغتَيّ C أو Perl، تتطلب PHP أن تنتهي التعليمات البرمجية بفاصلة منقوطة ";" يُشير الرمز $ إلى متغير (variable)، سنتحدث عن المتغيرات في هذا الدرس لاحقًا تكافئ علامات الاقتباس المفردة والمزدوجة بعضها (لكنها ليس لها نفس المعنى تمامًا، سنشرح الاختلاف البسيط لاحقًا) يُستعمَل الأمر echo لطباعة البيانات التعليقات التعليقات جزءٌ من البرنامج لا ينفِّذها مُفسِّر اللغة؛ وهي ملاحظاتٌ لكي يتذكر القارئ أو يفهم أمرًا برمجيًا ما لاحقًا. وهنالك نوعان من التعليقات: التعليقات ذات السطر الوحيد: وهذه التعليقات قصيرةٌ تمتد لسطرٍ وحيدٍ فقط، وتبدأ عادةً بالرمز "//"، ولا بأس أن يكون قبلها تعليماتٌ برمجية، لكن التعليق سينتهي مع نهاية السطر؛ ويستحق أن نذكر أننا نستطيع أن نبدأ التعليقات ذات السطر الوحيد برمز المربع (#) بدلًا من شرطتين مائلتين. التعليقات متعددة الأسطر: وهي تعليقاتٌ طويلة، قد تمتد لأكثر من سطر؛ وتوضع بين علامة البداية /* وعلامة النهاية */، ومن استعمالات هذا النوع من التعليقات أيضًا هي تعطيل جزء من الشيفرة ﻷغراض التجربة. يمكنك أن ترى نوعَيّ التعليقات السابقين في المثال أعلاه. المتغيرات تسمح لك المتغيرات بتخزين ومعالجة البيانات في السكربتات، ويمكنك اعتبارها شبيهةً بالمتغيرات التي تستعملها في الرياضيات؛ إذ أنَّ المتغير -ببساطة- هو حاويةٌ تحوي قيمةً معيّنة؛ أُطلِقَت تسمية "المتغيرات" عليها لأنها القيمة المُسنَدة إليها قد تتغير أثناء تنفيذ السكربت، وقابلية التغير تلك تجعل من المتغيرات أداةً مفيدةً جدًا. وكما ذكرنا سابقًا، يبدأ اسم المتغير بالإشارة $. هذه بعض الأمثلة عن أسماءٍ صحيحةٍ للمتغيرات: $simple $_start_with_underscore $_includes_numbers_23 $_includes_UPPERCASE_2 $_23_it_is_valid ملاحظة: لا يمكنك بدء اسم المتغير برقمٍ كالاسم الآتي $2_invalid_variable_name، ولا يمكن أيضًا أن تستعمل الشرطة "-" في اسمه. أنواع البيانات أنواع البيانات (data types) هي أنواع المتغيرات في PHP؛ وأنواعُ البياناتِ البسيطة في PHP هي: الأعداد الصحيحة (integers) الأعداد ذات الفاصلة (floats) السلاسل النصيّة (strings) القيم المنطقية البوليانية (صح [true] أو خطأ [false])؛ وهنالك أنواع بياناتٍ أخرى مثل الكائنات (objects) والمصفوفات (arrays) سنتحدث عنها لاحقًا في هذه السلسلة. ملاحظة: إن كانت لديك معلوماتٌ سابقة عن إحدى لغات البرمجة التي تُحدِّد نوع البيانات المُخزَّنة في المتغيرات (أي strongly typed)، فاعلم أنَّ PHP هي لغةٌ لا تتطلب منك تحديد النوع (أي أنها loosely typed)؛ فلا تحتاج إلى أن تخبر اللغة ما نوع البيانات التي ستُخزَّن في المتغير. الثوابت الثابت هو مُعرِّف (أي اسم) يحمل قيمةً معيّنة، وكما يوحي اسمه، لن تتغير قيمته أثناء تنفيذ السكربت بعد أن تُعرَّف لأول مرة، واسمه حساسٌ لحالة الأحرف، وتُستعمَل الأحرف الكبيرة فيه عادةً؛ وله نفس قواعد تسمية المتغيرات (لا يجوز أن يبدأ برقم، أو يحتوي على شرطة…)، لكن لا يوضع الرمز $ قبله. <?php /* author: Abd Allatif Eymsh website: academy.hsoub.com version: 1.0 date: 22 12 2015 */ // هذا تعليقٌ ذو سطرٍ وحيد $variable = 'variablename'; echo "string"; echo $variable; define( "MY_CONSTANT", "19" ); define('PI', 3.14); echo PI; echo MY_CONSTANT; ?> تكون PI و MY_CONSTANT ثوابت في المثال السابق. ملاحظة: يمكن استعمال الكلمة المحجوزة const لتعريف الثوابت بدلًا من الدالة define() في إصدار PHP 5.3 وما بعده كما يلي: <?php // تعمل في إصدار PHP 5.3 const CONSTANT = 'Hello World'; echo CONSTANT; ?> لكن لا يمكن تعريف الثوابت باستخدام const داخل الدوال أو الحلقات، لأنها تُعرَّف في وقت التصريف (compile-time). المعاملات يأخذ المعامل قيمةً أو أكثر (أو "تعبيرًا" [expressions] كما نقول في الاصطلاحات البرمجية) ويُنتِج قيمةً أخرى. يمكن أن تُجمَّع المعاملات بناءً على عدد القيم التي تأخذها، فمثلًا المعاملات الأحادية (unary operators) تأخذ قيمةً واحدةً فقط (مثل معامل الزيادة الذي شرحناها أدناه)، والمعاملات الثنائية تأخذ قيمتين مثل المعاملات الحسابية البسيطة (الجمع والطرح والضرب…) وتُصنَّف أغلبية المعاملات في PHP ضمن هذا القسم؛ وهنالك معامل ثلاثي وحيد يأخذ ثلاث قيم سنشرحه في درسٍ لاحق. هنالك عددٌ كبيرٌ من المعاملات في لغة PHP، أهمها: المعاملات الحسابية الإسناد معاملات الأعداد الثنائية (Bitwise) معاملات المقارنة الزيادة أو الإنقاص معامل السلاسل النصية المعاملات الحسابية هي تلك المعاملات التي تستعملها في الحسابات الرياضية، مثل الجمع (+)، والطرح (-)، والضرب (*)، والقسمة (/)، وباقي القسمة (%)؛ مثال: $a = 4 + 5; // + هو معامل حسابي لاحظ الفرق بين المعاملين / و %، إذ أنَّ / هو معامل القسمة بينما % هو باقي القسمة؛ أي 8 /5 = 1.6 ، بينما 8 % 5 = 3 (إذ أنَّ 8 = 5 *1 +3، فإن باقي القسمة هو 3). أما معامل الإسناد، فيسند القيم إلى المتغيرات، وهو إشارة = كما في المثال الآتي: $a = 4;. تجري المعاملات الثنائية العمليات الحسابية على البتات الموجودة في الأعداد الصحيحة، وهي & (and)، و | (or)، و >> (الإزاحة نحو اليسار)، و << (الإزاحة نحو اليمين)، و ^ (XOR)، و ~ (NOR)؛ مثال على ذلك هو 14 & 3 = 2، لأنَّ 00001110 & 00000011 = 00000010. أما معاملات المقارنة، فهي -كما يدل اسمها- تقارن بين قيمتين؛ يُظهِر الجدول الآتي معاملات المقارنة الشائعة: المثال اسم المعامل النتيجة $a == $b المساواة TRUE إن كان المتغير $a مساويًا بالقيمة إلى المتغير $b $a === $b مطابقة TRUE إن كان المتغير $a مساويًا المتغير $b بالقيمة والنوع $a != $b $a <> $b عدم مساواة TRUE إن لم يكن المتغير $a مساويًا للمتغير $b $a !== $b عدم مطابقة TRUE إن لم يكن المتغير $a مساويًا للمتغير $b بالقيمة أو بالنوع $a < $b أصغر من TRUE إن كان المتغير $a أصغر تمامًا من المتغير $b $a > $b أكبر من TRUE إن كان المتغير $a أكبر تمامًا من المتغير $b $a <= $b أصغر من أو يساوي TRUE إن كان المتغير $a أصغر أو يساوي المتغير $b $a >= $b أكبر من أو يساوي TRUE إن كان المتغير $a أكبر أو يساوي المتغير $b تستعمل معاملات الزيادة أو الإنقاص لزيادة أو إنقاص قيمة المتغير بقيمة 1. المثال شرحه ++$x; إضافة القيمة 1 إلى المتغير $x ثم إعادة القيمة النهائية $x++; إعادة قيمة المتغير $x ثم إضافة 1 –-$x; إنقاص القيمة 1 من المتغير $x ثم إعادة القيمة النهائية $x–-; إعادة قيمة المتغير $x ثم إنقاص 1 <?php echo "Postincrement"; $a = 5; echo "Should be 5: " . $a++; echo "Should be 6: " . $a; echo "Preincrement"; $a = 5; echo "Should be 6: " . ++$a; echo "Should be 6: " . $a; echo "Postdecrement"; $a = 5; echo "Should be 5: " . $a--; echo "Should be 4: " . $a; echo "Predecrement"; $a = 5; echo "Should be 4: " . --$a; echo "Should be 4: " . $a; ?> معامل السلاسل النصية (.) يستعمل لجمع سلسلتين نصيتين أو متغيرين مع بعضهما بعضًا؛ انظر إلى الشيفرة الآتي: <?php $a = 4; $q = 5; $c = $a.$q; // المتغيران $a و $b أصبحا مدمجين مع بعضهما echo $c; $n = 'myname'; echo "string ".$n.' $n inserted'; // دمج ثلاث سلاسل نصيّة ?> حاول أن تتدَّرب على تعريف وطباعة قيمة المتغيرات الخاصة بك، وأن تستعمل مختلف المعاملات السابقة عليها. المصادر مقال Introduction to PHP لصاحبه Harish Kumar. صفحة Operators من دليل PHP، وغيرها.
-
كوتلن (Kotlin) هي لغة برمجة مخصّصة لمنصّة جافا الافتراضية (Java Virtual Machine أو اختصارًا JVM)، الأندرويد، والمتّصفح. تنتمي للغات statically typed (التي تفحص الأنواع وقت الترجمة). وهي متوافقة مع جافا 100%. // (//)التعليقات على سطر واحد تبدأ بـ/* التعليقات المؤلفة من عدة أسطر تبدو كهذه */ تعمل الكلمة المفتاحية package بنفس طريقة جافا package com.learnxinyminutes.kotlin نقطة الإدخال لبرامج Kotlin هي دالة(تابع) تسمى main ،يمرر التّابع مصفوفة تحتوي على وسطاء arguments لسطر الأوامر. fun main(args: Array<String>) { التّصريح عن القيم يتم باستخدام إما var أو val ،تصريح val لا يمكن إعادة تعيينه، في حين يمكن ذلك في var val fooVal =10 لا يمكننا لاحقًا إعادة تعيين قيمة fooVal لقيمة أخرى. var fooVar =10 fooVar =20 من الممكن إعادة تعيين قيمة fooVar. في أغلب الحالات، يمكن لكوتلن أن تحدّد ما هو نوع المتغير، لذلك لا داعي لتحديده صراحة في كل مرّة. يمكننا أن نصرّح بنوع المتغير بوضوح كالتالي: val foo:Int=7 يمكن تمثيل السلاسل بطريقة مماثلة في java. يتم الهروب باستخدام backslash. val fooString ="My String Is Here!" val barString ="Printing on a new line?\nNo Problem!" val bazString ="Do you want to add a tab?\tNo Problem!" println(fooString) println(barString) println(bazString) تُحدد السلسلة الخام string raw باستخدام triple quote ("""). ويمكن أن تحتوي أسطر جديدة و أية محارف أخرى. val fooRawString =""" fun helloWorld(val name :String){ println("Hello, world!")}""" println(fooRawString) السلاسل ممكن أن تحتوي تعابير القالب template expressions. تبدأ تعابير القالب بالرمز $. val fooTemplateString ="$fooString has ${fooString.length} characters" println(fooTemplateString)// => My String Is Here! has 18 characters من أجل المتغيرات التي تحوي قيمة فارغة null يجب تحديد ذلك صراحة nullable. يمكن تحديد المتغير قابلاً للقيمة null بإلحاق? بنوعه. ويمكننا الوصول إلى المتحولات القابلة لـ null باستخدام مُعامل التشغيل? ،ويمكننا استخدام عامل التشغيل :? لتحديد قيمة بديلة للاستخدام إذا كان المتغير فارغ null. var fooNullable:String?="abc" println(fooNullable?.length)// => 3 println(fooNullable?.length ?:-1)// => 3 fooNullable = null println(fooNullable?.length)// => null println(fooNullable?.length ?:-1)// => -1 يمكن التصريح عن الدوال باستخدام الكلمة المفتاحية fun. وتحدد وسطاء الدالة بين قوسين بعد اسم الدالة. ويمكن لوسطاء الدالة اختياريًا الاحتواء على قيمة افتراضية. ويحدّد نوع إرجاع الدالة، إذا لزم الامر بعد المعطيات. fun hello(name:String="world"):String{return"Hello, $name!"} println(hello("foo"))// => Hello, foo! println(hello(name ="bar"))// => Hello, bar! println(hello())// => Hello, world! يمكن لبارامتر الدالة أن يوسم بالكلمة المفتاحية vararg للسماح بتمرير عدد متغير من المعطيات إلى الدالة. fun varargExample(vararg names:Int){ println("Argument has ${names.size} elements")} varargExample()// => لا يحوي الوسيط أية عناصر varargExample(1)// => يحوي الوسيط عنصر واحد varargExample(1,2,3)// => يحوي الوسيط 3 عناصر عندما تتكوّن الدّالة من تعبير واحد فقط، يمكن حذف الأقواس المنحنية { } ويتم تحديد الجسم(العملية) بعد رمز = fun odd(x:Int):Boolean= x %2==1 println(odd(6))// => false println(odd(7))// => true إذا كان نوع الاسترجاع يمكن استنتاجه فلسنا بحاجة لتحديده. fun even(x:Int)= x %2==0 println(even(6))// => true println(even(7))// => false الدوال يمكن أن تأخذ دوال كوسطاء وترجع دالة. fun not(f:(Int)->Boolean):(Int)->Boolean{return{n ->!f.invoke(n)}} يمكن للدوال المسمّاة أن تحدّد كوسائط باستخدام معامل التشغيل val notOdd = not(::odd) val notEven = not(::even) يمكن تحديد تعابير Lambda كوسائط val notZero = not {n -> n ==0} إذا احتوت lambda على بارامتر واحد فقط يمكن حذف التصريح عنه (جنبًا إلى جنب مع<- )وسيكون اسم البارامتر الوحيد it. val notPositive = not {it >0}for(i in 0..4){ println("${notOdd(i)} ${notEven(i)} ${notZero(i)} ${notPositive(i)}")} تستخدم الكلمة المفتاحية class للتّصريح عن الأصناف. class ExampleClass(val x:Int){ fun memberFunction(y:Int):Int{return x + y } infix fun infixMemberFunction(y:Int):Int{return x * y }} لإنشاء حالة instance جديدة نستدعي الباني. ولاحظ أن Kotlin لا تحوي الكلمة المفتاحية new. val fooExampleClass =ExampleClass(7) يمكن استدعاء دوال المستخدم باستخدام التنويت النقطي . println(fooExampleClass.memberFunction(4))// => 11 إذا وُسمت الدالة بالكلمة المفتاحية infix يمكن عندها أن يستدعى باستخدام التنويت الداخلي println(fooExampleClass infixMemberFunction 4)// => 28 أصناف البيانات Data classes هي طريقة مختصرة لإنشاء الأصناف التي تحتوي بيانات فقط وتنشئ دوال hashCode،equals و toString تلقائيًا. data classDataClassExample(val x:Int, val y:Int, val z:Int) val fooData =DataClassExample(1,2,4) println(fooData)// => DataClassExample(x=1, y=2, z=4) أصناف البيانات لديها دالة copy val fooCopy = fooData.copy(y =100) println(fooCopy)// => DataClassExample(x=1, y=100, z=4) يمكن أن تفكَك الكائنات Objects في متغيرات متعددة val (a, b, c)= fooCopy println("$a $b $c")// => 1 100 4 التفكيك باستخدام حلقة for for((a, b, c) in listOf(fooData)){ println("$a $b $c")// => 1 100 4} val mapData = mapOf("a" to 1,"b" to 2) Map.Entry قابل للتفكيك كذلك for((key, value) in mapData){ println("$key -> $value")} الدالة with مشابهة لعبارة with في جافا data classMutableDataClassExample(var x:Int, var y:Int, var z:Int) val fooMutableData =MutableDataClassExample(7,4,9) with (fooMutableData){ x -=2 y +=2 z--} println(fooMutableData)// => MutableDataClassExample(x=5, y=6, z=8) يمكننا إنشاء قائمة (لائحة) باستخدام الدالة listOf. ستكون القائمة غير قابلة للتغيير ولا يمكن إضافة عناصر أو إزالتها. val fooList = listOf("a","b","c") println(fooList.size)// => 3 println(fooList.first())// => a println(fooList.last())// => c// index يمكن الوصول إلى عناصر القائمة من خلال فهرسها println(fooList[1])// => b يمكن إنشاء قائمة قابلة للتعديل باستخدام الدالة mutableListOf val fooMutableList = mutableListOf("a","b","c") fooMutableList.add("d") println(fooMutableList.last())// => d println(fooMutableList.size)// => 4 يمكن إنشاء تعيين set باستخدام الدّالة setOf val fooSet = setOf("a","b","c") println(fooSet.contains("a"))// => true println(fooSet.contains("z"))// => false يمكننا إنشاء خريطة map باستخدام الدّالة mapOf val fooMap = mapOf("a" to 8,"b" to 7,"c" to 9) يمكن الوصول لقيم الـ Map من خلال مفاتيحها println(fooMap["a"])// => 8 تُمثل المتتالية Sequences مجموعات تقييمها مؤجل إلى حين الحاجة lazily-evaluated collections. ويمكننا إنشاء متتالية باستخدام الدّالة generateSequence. val fooSequence = generateSequence(1,{ it +1}) val x = fooSequence.take(10).toList() println(x)// => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] مثال لاستخدام متتالية لتوليد أرقام فيبانوتشي fibonacci fun fibonacciSequence():Sequence<Long>{ var a =0L var b =1L fun next():Long{ val result = a + b a = b b = result return a }return generateSequence(::next)} val y = fibonacciSequence().take(10).toList() println(y)// => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] تزوّد Kotlin دوال عالية الرتبة للعمل مع المجموعات collections val z =(1..9).map{it *3}.filter {it <20}.groupBy {it %2==0}.mapKeys {if(it.key)"even"else"odd"} println(z)// => {odd=[3, 9, 15], even=[6, 12, 18]} يمكن استخدام حلقة for مع أي شيء يؤمن التكرار. for(c in "hello"){ println(c)} تعمل حلقات while بشكل مشابه لعملها في اللغات الأخرى var ctr =0while(ctr <5){ println(ctr) ctr++}do{ println(ctr) ctr++}while(ctr <10) يمكن استخدام if كتعبير يرجع القيم. لهذا السبب ليس هناك حاجة لمعامل التشغيل :? في Kotlin. val num =5 val message =if(num %2==0)"even"else"odd" println("$num is $message")// => 5 is odd يمكن استخدام when كبديل لسلاسل if-else if val i =10 when { i <7-> println("first block") fooString.startsWith("hello")-> println("second block")else-> println("else block")} يمكن استخدام when مع المعطيات. when (i){0,21-> println("0 or 21") in 1..20-> println("in the range 1 to 20")else-> println("none of the above")} يمكن استخدام when كدالة ترجع القيم. var result = when (i){0,21->"0 or 21" in 1..20->"in the range 1 to 20"else->"none of the above"} println(result) يمكننا التحقق فيما إذا كان الكائن object من نوع محدّد باستخدام معامل التشغيل is. إذا مرر الكائن فحص النوع type check عندها يمكن استخدام هذا النوع دون الموائمة بشكل صريح. fun smartCastExample(x:Any):Boolean{if(x is Boolean){ X توائم أوتوماتيكيًا إلى Boolean return x }elseif(x is Int){ X توائم أوتوماتيكيًا إلى Int return x >0}elseif(x is String){ X توائم أوتوماتيكيًا إلى String return x.isNotEmpty()}else{returnfalse}} println(smartCastExample("Hello, world!"))// => true println(smartCastExample(""))// => false println(smartCastExample(5))// => true println(smartCastExample(0))// => false println(smartCastExample(true))// => true تعمل الموائمة الذكيّة smartcast أيضًا مع كتلة when fun smartCastWhenExample(x:Any)= when (x){ is Boolean-> x is Int-> x >0 is String-> x.isNotEmpty()else->false} الملحقات Extensions هي طريقة لإضافة وظائف جديدة للصنف class. وهي مشابهة لدوال ملحقات #C fun String.remove(c:Char):String{returnthis.filter {it != c}} println("Hello, world!".remove('l'))// => Hello, word! println(EnumExample.A)// => A println(ObjectExample.hello())// => hello أصناف Enum مشابهة لأنواع enum في جافا. enumclassEnumExample{ A, B, C } يمكن استخدام الكلمة المفتاحية object لإنشاء كائنات وحيدة. لا يمكننا تمثيلها ولكن يمكن أن نشير لحالتها الفريدة من خلال اسمها. وهذا مشابه لكائنات Scala singleton object ObjectExample{ fun hello():String{return"hello"}} fun useObject(){ObjectExample.hello() val someRef:Any=ObjectExample//كماهي تمامًا objects نستخدم أسماء الكائنات } ترجمة -وبتصرّف- للمقال Learn kotlin in Y Minutes
-
جافا هي لغة برمجة حاسوبية للأغراض العامة، المتزامنة، المعتمدة على الصفوف، وغرضية التوجه // يبدأ التعليق المكتوب على خط واحد ب .// /* يبدو التعليق المكتوب على عدة سطور بهذا الشكل. */ /** *تبدو التعليقات في ملفات لغة البرمجة جافا بهذا الشكل *و تستخدم لوصف الصف أو الصفات المختلفة لكائن معين. *:الصفات الرئيسية * *@author- اسم مؤلف الكود. *ويحتوي على معلومات الاتصال كالبريد الالكتروني لمؤلف الكود أو للمؤلفين. *@version- النسخة الحالية من البرنامج. *@since -الوقت الذي تم فيه إضافة هذا الجزء من البرنامج. *@param -من أجل وصف البارامترات المختلفة للمنهج (method). *@return -لوصف القيمة التي يرجعها المنهج. *@deprecated -لإظهار انتهاء صلاحية الكود أو عدم وجوب استخدامه. *@see - روابط إلى جزء آخر من المستندات */ استورد الصف ArrayList بدلاً من استيراد الرزمة java.util كلّها import java.util.ArrayList; استورد جميع الصفوف الموجود داخل الرزمة import java.security.*; يحوي أي ملف جافا على صف عام، على المستوى الخارجي، له نفس اسم الملف public class LearnJava { ليعمل برنامج جافا يجب أن يحوي على تابع رئيسي بمثابة نقطة البدء public static void main(String[] args) { الدخل/الخرج دورة تطوير التطبيقات باستخدام لغة Python احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة اشترك الآن الخرج استخدم ()System.out.println لطباعة السطور النصيّة System.out.println("Hello World!"); System.out.println( "Integer: " + 10 + " Double: " + 3.14 + " Boolean: " + true); استخدم ()System.out.print للطباعة بدون سطر جديد System.out.print("Hello "); System.out.print("World"); استخدم ()System.out.printf لتنسيق الطباعة بسهولة System.out.printf("pi = %.5f", Math.PI); // => pi = 3.14159 الدخل استخدم scanner لقراءة الدخل، يجب استيراد الصف ;java.util.Scanner Scanner scanner = new Scanner(System.in); لقراءة السلاسل المحرفية المُدخلة String name = scanner.next(); لقراءة البايتات المُدخلة byte numByte = scanner.nextByte(); لقراءة العدد الصحيح المُدخل int numInt = scanner.nextInt(); لقراءة العدد الحقيقي المٌدخل float numFloat = scanner.nextFloat(); لقراءة العدد الحقيقي مضاعف الدقة المُدخل double numDouble = scanner.nextDouble(); لقراءة القيمة المنطقية المُدخلة boolean bool = scanner.nextBoolean(); المتغيرات التصريح عن المتغيرات يتم التصريح عن متغير باستخدام // <type> <name> int fooInt; يتم التصريح عن مجموعة من المتغيرات من نفس النوع // <type> <name1>, <name2>, <name3> int fooInt1, fooInt2, fooInt3; تهيئة المتغير يتم تهيئة متغير باستخدام // <type> <name> = <val> int barInt = 1; يتم تهيئة مجموعة من المتغيرات من نفس النوع بنفس القيمة باستخدام // <type> <name1>, <name2>, <name3> // <name1> = <name2> = <name3> = <val> int barInt1, barInt2, barInt3; barInt1 = barInt2 = barInt3 = 1; أنواع المتغير البايت (Byte) : وهو 8bit ويستخدم لترميز الأعداد الصحيحة بين -128 و 127 byte fooByte = 100; إذ كنت ترغب بتفسير البايت كعدد صحيح موجب (بدون إشارة). فإن هذه العملية البسيطة من الممكن أن تساعد int unsignedIntLessThan256 = 0xff & fooByte; هذا يناقض عمل cast الذي من الممكن أن يعطي عدد سالب int signedInt = (int) fooByte; القصير (Short): وهو 16bit ويستخدم لترميز الأعداد الصحيحة بين -32,768 و 32,767 short fooShort = 10000; الصحيح (Integer): وهو 32bit ويستخدم لترميز الأعداد الصحيحة بين -2,147,483,648 و 2,147,483,647 int bazInt = 1; الطويل (Long): وهو 64bit ويستخدم لترميز الأعداد الصحيحة بين -9,223,372,036,854,775,808 و 9,223,372,036,854,775,807 long fooLong = 100000L; يستخدم المحرف L للدلالة على أن قيمة المتحول هي من النوع Long. و أي قيمة مُسندة للمتحول بدون استخدام L هي عبارة عن عدد صحيح int بشكل افتراضي. ملاحظة: إن الأنواع byte، short، int، long، هي أنواع ذات إشارة signed. أي من الممكن أن تحوي على قيم موجبة أو قيم سالبة. لا يوجد متغيرات بقيمة موجبة فحسب. ولكن المحارف تعتبر من نوعية unsigned ذات القيمة الموجبة فقط ذات 16bit. العائم (Float): وهو ذو دقة أحادية 32 bit IEEE 754 ويستخدم لترميز الأعداد الحقيقية ذات الفاصلة العائمة بين 149-^2 و 127^2 - (23-^2-2) float fooFloat = 234.5f; يستخدم المحرف f أو F للدلالة على أن قيمة المتحول هي من النوع Float. و إلا سيعتبر المتغير من النوع الحقيقي المضاعف. المضاعف (Double): وهو ذو دقة مضاعفة 64 bit IEEE 754 ويستخدم لترميز الأعداد الحقيقية ذات الفاصلة العائمة بين 1074-^2 و 1023^2 - (52-^2-2) double fooDouble = 123.4; القيم المنطقية (Boolean) ذات القيمة: true و false boolean fooBoolean = true; boolean barBoolean = false; Char هو محرف يونيكود 16بت أحادي char fooChar = 'A'; لا يمكن أعادة تهيئة المعطيات من النوع final final int HOURS_I_WORK_PER_WEEK = 9001; ولكن من الممكن تهيئتها بعد عملية التصريح عنها final double E; E = 2.71828; BigInteger -عبارة عن نوع الأعداد الصحيحة الثابتة بمستويات دقّة مختلفة. يستخدم نوع المعطيات BigInteger للسماح للمبرمج بالتعامل مع الأعداد الصحيحة التي هي أكبر من 64-بت. حيث تُخزّن الأعداد الصحيحة في مصفوفة من البايتات، التي يمكن التلاعب بها باستخدام التوابع المبنيّة في الصف BigInteger . يمكن تهيئة المتغير من النوع BigInteger بمصفوفة من البايتات أو بمصفوفة من السلاسل المحرفية. BigInteger fooBigInteger = new BigInteger(fooByteArray); BigDecimal عبارة عن نوع الأعداد الحقيقية الثابتة بمستويات دقة مختلفة. ويأخذ المتغير من النوع BigDecimal معاملين: عدد صحيح ذو حجم غير مقّيّد، يمثل مستوى الدقّة، وعدد صحيح أخر بحجم 32 بت. يسمح النوع BigDecimal بالتحكم بتقريب الأعداد الحقيقية. عندما يكون مطلوب مستوى دقة محدد للعدد الحقيقي يُنصح باستخدام BigDecimal . من الممكن تهيئة المتغير من النوع BigDecimal بأحد الأنواع التالية: int ،long ،double ،String ،BigInteger BigDecimal fooBigDecimal = new BigDecimal(fooBigInteger, fooInt); كُن حذرًا عند استخدامك للأنواع float ,double لأن عدم الدقّة في تحديد النوع سوف تُنسخ في الـ BigDecimal ويُفضل استخدام سلسلة محرفية ثابتة عندما تحتاج إلى قيمة دقيقة. BigDecimal tenCents = new BigDecimal("0.1"); String -السلاسل المحرفية String fooString = "My String Is Here!"; الحرف الخاص n\ يُعرف بحرف السطر الجديد الذي يحرك مؤشر الكتابة إلى بداية السطر التالي. String barString = "Printing on a new line?\nNo Problem!"; الحرف الخاص t\ يُعرف بالمسافة الأفقية، يقوم بتحريك مؤشر الكتابة مسافة معينة إلى النقطة التالية في السطر. String bazString = "Do you want to add a tab?\tNo Problem!"; System.out.println(fooString); System.out.println(barString); System.out.println(bazString); بناء السلاسل المحرفية: باستخدام عامل الجمع (+) تُعتبر الطريقة الأساسية (الأمثل) للقيام ببناء السلاسل المحرفية. String plusConcatenated = "Strings can " + "be concatenated " + “via + operator.”; System.out.println(plusConcatenated); // Output: Strings can be concatenated via + operator. باستخدام الصف StringBuilder لا تشكل هذه الطريقة أية سلاسل محرفية وسيطة، فقط تقوم بتخزين قطع السلاسل المحرفية و ربطها مع بعضها عندما يتم استدعاء التابع ()toString. تلميح: لا يعتبر الصف StringBuilder إجرائية آمنة. يوجد صف بديل آمن StringBuffer (مع تأثير بسيط على الأداء). StringBuilder builderConcatenated = new StringBuilder(); builderConcatenated.append("You "); builderConcatenated.append("can use "); builderConcatenated.append("the StringBuilder class."); System.out.println(builderConcatenated.toString()); // فقط الآن تمّ بناء السلسة المحرفية // Output: You can use the StringBuilder class. تكون StringBuilder فعاله عندما السلسلة المحرفية الكاملة المبنيّة مطلوبة في نهاية عملية ما. StringBuilder stringBuilder = new StringBuilder(); String inefficientString = ""; for (int i = 0 ; i < 10; i++) { stringBuilder.append(i).append(" "); inefficientString += i + " "; } System.out.println(inefficientString); System.out.println(stringBuilder.toString()); تتطلب inefficientString عمل أكثر، حيث أنها تنتج سلسلة محرفية عند كل دورة للحلقة. يتم ترجمة تجميع السلاسل المحرفية البسيطة بالعامل (+) إلى: toString() و StringBuilder تجنب استخدام تجميع السلاسل المحرفية داخل الحلقات. لاستخدام منسق السلاسل المحرفية طريقة أخرى بديلة لتوليد السلاسل المحرفية سريعة و قابلة للقراءة. String.format("%s may prefer %s.", “Or you”, “String.format()”); // Output: Or you may prefer String.format(). المصفوفات يجب تحديد حجم المصفوفة بشكل فوري بمجرد التصريح عنها. تستخدم الصيغة التالية للتصريح عن مصفوفة. // <datatype>[] <var name> = new <datatype>[<array size>]; // <datatype> <var name>[] = new <datatype>[<array size>]; int[] intArray = new int[10]; String[] stringArray = new String[1]; boolean boolArray[] = new boolean[100]; طريقة أخرى للتصريح عن مصفوفة و تهيئتها int[] y = {9000, 1000, 1337}; String names[] = {"Bob", "John", "Fred", "Juan Pedro"}; boolean bools[] = {true, false, false}; فهرسة المصفوفة - الوصول إلى عنصر فيها System.out.println("intArray @ 0: " + intArray[0]); تبدأ المصفوفات بالفهرس (0)، و هي قابله للتغيير . intArray[1] = 1; System.out.println("intArray @ 1: " + intArray[1]) ArrayLists; // => 1 مجموعة من أنواع المعطيات الأخرى، التي تستحق التدقيق فيها: ArrayLists : مشابهة للمصفوفات، إلا أنها تحوي على وظائف إضافية، وحجمها قابل للتعديل. LinkedLists : عبارة عن تنفيذ لقائمة مترابطة بشكل مضاعف، التي تنفذ جميع العمليات التي من المتوقع لقائمة مترابطة بشكل مضاعف أن تنفذها. Maps : عبارة عن وصل كائنات المفتاح إلى كائنات القيمة. وهي عبارة عن واجهة، و بالتالي لا يمكن أن يتم تشكيل كائنات منها. يجب أن يتم تحديد نوع المفاتيح و القيم بناءً على الكائن المُشكل من الصف. من الممكن ان يُربط كل مفتاح إلى قيمة واحدة فقط، ومن الممكن أن يظهر كل مفتاح مرة واحدة فقط. (لا يوجد نسخ) لتنفيذ الخريطة/الواجهة Map hashtable : يُستخدم هذا الصف الجدول ، HashMaps وينفذ الخريطة Map ، هذا يسمح بتثبيت زمن العمليات الأساسية، مثل الحصول على عنصر أو إدخال عنصر، حتى في المجموعات الكبيرة. TreeMap: عبارة عن Map ، مصنفة حسب مفاتيحها. يُحافظ كل تعديل على ترتيبه، إما باستخدام المقارن المزود عند عملية توليد الكائن، أو باستخدام المقارنات لكل كائن، اذا كان ينفذ واجهة قابلة للمقارنة. سوف يؤدي الفشل المدمج في تنفيذ واجهة قابلة للمقارنة، مع الفشل في تزويد مقارن إلى رمي ClassCastExceptions تأخذ عمليات إدخال و حذف عناصر زمن من الدرجة ((O(log(n ، لذلك تجنب استخدام بنى المعطيات هذه، إلا أذا كنت ترغب من الاستفادة من ميزة الترتيب. العمليات System.out.println("\n->Operators"); int i1 = 1, i2 = 2; // الطريقة المختصرة للتصريح عن عدة متغيرات في نفس الوقت العمليات الحسابية بسيطة System.out.println("1+2 = " + (i1 + i2)); // => 3 System.out.println("2-1 = " + (i2 - i1)); // => 1 System.out.println("2*1 = " + (i2 * i1)); // => 2 System.out.println("1/2 = " + (i1 / i2)); // => 0 (int يعيد int/int) System.out.println("1/2.0 = " + (i1 / (double)i2)); // => 0.5 باقي القسمة System.out.println("11%3 = "+(11 % 3)); // => 2 عمليات المقارنة System.out.println("3 == 2? " + (3 == 2)); // => false System.out.println("3 != 2? " + (3 != 2)); // => true System.out.println("3 > 2? " + (3 > 2)); // => true System.out.println("3 < 2? " + (3 < 2)); // => false System.out.println("2 <= 2? " + (2 <= 2)); // => true System.out.println("2 >= 2? " + (2 >= 2)); // => true العمليات المنطقية System.out.println("3 > 2 && 2 > 3? " + ((3 > 2) && (2 > 3))); // => false System.out.println("3 > 2 || 2 > 3? " + ((3 > 2) || (2 > 3))); // => true System.out.println("!(3 == 2)? " + (!(3 == 2))); // => true العمليات على مستوى البت ~ عامل إيجاد المتمم على مستولى البتات. << عامل الإزاحة الحسابية نحو اليسار (مع الأخذ بعين الاعتبار بت الإشارة). >> عامل الإزاحة الحسابية نحو اليمين (مع الأخذ بعين الاعتبار بت الإشارة). >>> عامل الإزاحة المنطقية نحو اليمين (بدون الأخذ بعين الاعتبار بت الإشارة). & على مستوى البتّات and العملية المنطقية ^ على مستوى البتّات xor العملية المنطقية | على مستوى البتّات or العملية المنطقية عمليات الزيادة int i = 0; System.out.println("\n->Inc/Dec-rementation"); العامل ++ يقوم بالزيادة بمقدار واحد. العامل – يقوم بالإنقاص بمقدار واحد. إذا تم وضع هذين العاملين قبل المتغير، فإنهن يقومان بالزيادة أو الإنقاص، ومن ثم يعيدان قيمة المتحول الجديدة. أما إذا تم وضع هذين العاملين بعد المتغير، فإنهما يقومان بإعادة قيمة المتحول، ومن ثم يقومان بالزيادة أو الإنقاص. System.out.println(i++); // i = 1, يطبع 0 (زياده لاحقة). System.out.println(++i); // i = 2, يطبع 2 (زيادة سابقة). System.out.println(i--); // i = 1, يطبع 2 (إنقاص لاحق) System.out.println(--i); // i = 0, يطبع 0 (إنقاص سابق) بنى التحكم System.out.println("\n->Control Structures"); عبارة if مشابهة لتلك الموجودة في لغة البرمجة C int j = 10; if (j == 10) { System.out.println("I get printed"); } else if (j > 10) { System.out.println("I don't"); } else { System.out.println("I also don't"); } حلقة While int fooWhile = 0; while(fooWhile < 100) { System.out.println(fooWhile); //زيادة العدّاد //(في المجال (0،1،..99 foowhile مكررة 100 مرة ، حيث أن قيم المتغير fooWhile++; } System.out.println("fooWhile Value: " + fooWhile); حلقة Do While int fooDoWhile = 0; do { System.out.println(fooDoWhile); //زيادة العدّاد //في المجال 0-99 foowhile مكررة 99 مرة ، حيث أن قيم المتغير fooDoWhile++; } while(fooDoWhile < 100); System.out.println("fooDoWhile Value: " + fooDoWhile); حلقة For : هيكلية الحلقة على الشكل التالي: for(<start_statement>; <conditional>; <step>) مثال for (int fooFor = 0; fooFor < 10; fooFor++) { System.out.println(fooFor); // fooFor 0->9 مكررة 10 مرات، حيث } System.out.println("fooFor Value: " + fooFor); //outerيتم الخروج من الحلقة الداخلية باستخدام جملة الهروب المعنونّة بـ outer: for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { if (i == 5 && j ==5) { break outer; //يخرج من الحلقة الخارجية أيضا، بدلاً من الخروج فقط من الحلقة الداخلية } } } حلقة For Each : قادرة على العمل مع المصفوفات و الكائنات for إن حلقة التي تنفذ الواجهات القابلة للتكرار. int[] fooList = {1, 2, 3, 4, 5, 6, 7, 8, 9}; هيكلية الحلقة على الشكل التالي: for (<object> : <iterable>) تقرأ هكذا: لكل عنصر في ال iterable ملاحظة: يجب أن يتطابق نوع الكائن مع نوع عناصر ال iterable for (int bar : fooList) { System.out.println(bar); //سوف تتكرر 9 مرات و يطبع 1-9 على سطور جديدة. } Switch تعمل Switch مع أنواع المعطيات: int، char، short ،byte وأيضًا من الممكن أن تعمل مع النوع Enum و String و مجموعة من الصفوف الخاصة التي تجمع الأنواع البدائية: Character, Byte, Short, Integer. بدءًا من جافا 7، من الممكن استخدام النوع String. ملاحظة: تذكر عدم استخدام التعليمة "break" في نهاية حالة جزئية إذا كان هناك يوجد حالة أخرى تستوفي الشرط أيضًا. int month = 3; String monthString; switch (month) { case 1: monthString = "January"; break; case 2: monthString = "February"; break; case 3: monthString = "March"; break; default: monthString = "Some other month"; break; } System.out.println("Switch Case Result: " + monthString); عبارة (Try-with-resources) في Java الإصدار +7 تُستخدم في جافا عبارات Try-catch-finally، و لكن في جافا +7 أيضا ً مُتاح استخدام try-with-resources إن عبارات try-with-resources تُبسط عمل عبارات try-catch-finally عن طريق إغلاق المصادر بشكل أتوماتيكي. لاستخدام try-with-resources ضمّن كائن من صف في تعليمة try. try (BufferedReader br = new BufferedReader(new FileReader("foo.txt"))) { // يمكنك أن تجرّب شيئًا يحتاج إلى استثناء System.out.println(br.readLine()); // في جافا 7 المصادر مغلقة دومًا حتى لو كان هناك استثناء } catch (Exception ex) { //catch سوف يتم إغلاق المصدر قبل أن تنفذ عبارة System.out.println("readLine() failed."); } في هذه الحالة، لا يوجد حاجة إلى عبارة finally حيث أن BufferedReader تم إغلاقه للتو. يتم استخدام هذا لتجنب الحالات الحرجة، عندما عبارة finally لا يمكن استدعاءها. الاختزال الشرطي يمكنك استخدام العامل "?" لسرعة الأسناد . تُقرأ كما يلي: "إذا كانت العبارة صحيحة ، استخدم <أول قيمة> و إلا استخدم <القيمة الثانية> int foo = 5; String bar = (foo < 10) ? "A" : "B"; System.out.println("bar : " + bar); // "bar : A"تطبع // صحيحة (foo < 10) لأن العبارة //أو بشكل مبسط: System.out.println("bar : " + (foo < 10 ? "A" : "B")); التحويل بين أنواع المعطيات تحويل المعطيات تحويل السلاسل المحرفية إلى عدد صحيح Integer.parseInt("123");//يعيد نسخة العدد الصحيح من السلسة المحرفية "123" تحويل العدد الصحيح إلى سلسلة محرفية Integer.toString(123);//يعيد النسخة المحرفية من العدد الصحيح 123 راجع تحويل المعطيات للأنواع التالية من الصفوف: Double Long String الصفوف و التوابع للتصريح عن كائن من الصف Bicycle استخدم الكلمة المفتاحية new Bicycle trek = new Bicycle(); استدعاء توابع الكائن trek.speedUp(3); // setter , getter يجب عليك استخدام توابع الواضع و الآخذ trek.setCadence(100); يُعيد التابع ()toString التمثيل النصي للكائن. System.out.println("trek info: " + trek.toString()); التهيئة مزدوجة الأقواس لا تملك جافا تراكيب لتشكيل مجموعات ساكنة من المعطيات بطريقة سهلة عادةً يتم استخدام الطريقة التالية: private static final Set<String> COUNTRIES = new HashSet<String>(); static { COUNTRIES.add("DENMARK"); COUNTRIES.add("SWEDEN"); COUNTRIES.add("FINLAND"); } ولكن يوجد طريقة أنيقة لإنجاز الشيء نفسه بشكل أسهل، تُدعى هذه الطريقة مزدوجة الأقواس private static final Set<String> COUNTRIES = new HashSet<String>() {{ add("DENMARK"); add("SWEDEN"); add("FINLAND"); }} يولد القوس الاول { } صف داخلي بدون اسم AnonymousInnerClass ويعرّف القوس الثاني{ } كتلة مهيئ الحالة instance. يتم استدعاء هذه الكتلة، عندما يتم توليد الصف الداخلي لا يعمل هذا فقط للمجموعات، إنما يعمل أيضًا لكل الصفوف غير النهائي non-final يمكنك تضمين صفوف أخرى خارجية غير عامة في ملف جافا ، ولكنها ليست بممارسة جيدة. بدلاً من ذلك يفضّل فصل الصفوف في ملفات جافا منفصلة صيغة التصريح عن صف: // <public/private/protected> class <class name> { // } حقول البيانات, أنماط الباني, الوظائف كلها في الداخل تستدعى الوظائف كالدوال في جافا class Bicycle { حقول و متغيرات الصف Bicycle Public: يمكن الوصول إليه من أي مكان public int cadence; Private: يمكن الوصول إليه فقط من داخل الصف private int speed; Protected: يمكن الوصول إليه فقط من داخل الصف و الصفوف الفرعية protected int gear; default: يمكن الوصول إليه فقط من داخل الرزمة. String name; Static متغير صف ساكن static String className; البلوك الساكن Static لا تملك جافا تنفيذ للبواني الساكنة، ولكن جافا تملك كتل برمجية لتعريف متغيرات الصف (المتغيرات لساكنة) سيتم استدعاء هذا البلوك عند تحميل الصف. static { className = "Bicycle"; } الباني هي الطريقة التي يتم فيها توليد الصفوف. هذا هو الباني public Bicycle() { //يمكنك أيضا استدعاء باني آخر: // this(1, 50, 5, "Bontrager"); gear = 1; cadence = 50; speed = 5; name = "Bontrager"; } هذا عبارة عن باني ذو معاملات public Bicycle(int startCadence, int startSpeed, int startGear, String name) { this.gear = startGear; this.cadence = startCadence; this.speed = startSpeed; this.name = name; } صيغة الدالة // <public/private/protected> <return type> <function name>(<args>) أصناف الجافا غالبًا ما تنفّذ getters وsetters في حقولها صيغة التصريح عن الدالة // <access modifier> <return type> <method name>(<args>) public int getCadence() { return cadence; } دوال void لا تحتاج تصريح عودة public void setCadence(int newValue) { cadence = newValue; } public void setGear(int newValue) { gear = newValue; } public void speedUp(int increment) { speed += increment; } public void slowDown(int decrement) { speed -= decrement; } public void setName(String newName) { name = newName; } public String getName() { return name; } دالة لتوليد قيم الصفات لهذا الكائن @Override // موروثة من الصف لهذا الكائن. public String toString() { return "gear: " + gear + " cadence: " + cadence + " speed: " + speed + " name: " + name; } الصف PennyFarthing عبارة عن صنف فرعي من Bicycle class PennyFarthing extends Bicycle { -Penny Farthings //هي عباره عن نوع من الدراجات بعجلات أمامية كبيرة. public PennyFarthing(int startCadence, int startSpeed) { // super استدعي الباني الأب باستخدام الكلمة المفتاحية super(startCadence, startSpeed, 0, "PennyFarthing"); } يجب عليك تعليم المنهج الذي تُعيد كتابته بـ annotation@. @Override public void setGear(int gear) { this.gear = 0; } Object casting بما أن الصف PennyFarthing يرث الصف Bicycle : فمن الممكن القول أن: Bicycle هو PennyFarthing، و يمكن أن نكتب: // Bicycle bicycle = new PennyFarthing(); هذا يُدعى"object casting" حيث يتم تشكيل كائن من كائن آخر. الواجهات صيغة التصريح عن واجهة // <access-level> interface <interface-name> extends <super-interfaces> { // // Constants // // Method declarations // } مثال - الطعام: public interface Edible { public void eat(); //يجب على أي صف ينفذ هذه الواجهة // ان ينفذ هذه الدالة } public interface Digestible { public void digest(); // منذ جافا 8، من الممكن أن تملك الواجهات دالة افتراضية. public default void defaultMethod() { System.out.println("Hi from default method ..."); } } الآن يمكنك تشكيل صف يُنفذ كلا من هاتين الواجهتين. public class Fruit implements Edible, Digestible { @Override public void eat() { // ... } @Override public void digest() { // ... } } في جافا، يمكنك أن تمدد(ترث) صف واحد فقط، ولكن يمكنك أن تنفذ عدّة واجهات . على سبيل المثال: public class ExampleClass extends ExampleClassParent implements InterfaceOne, InterfaceTwo { @Override public void InterfaceOneMethod() { } @Override public void InterfaceTwoMethod() { } } الصفوف التجريدية صيغة التصريح عن صف تجريدي: // <access-level> abstract class <abstract-class-name> extends // <super-abstract-classes> { // // Constants and variables // // Method declarations // } لا يمكن تشكيل كائنات من الصفوف المجردة (تمثيل الصفوف المجردة) يمكن للصفوف المجردة أن تعرف عن دوال مجرّدة. المناهج المجردة لا تحوي على جسم ويجب تعليمها بالكلمة المفتاحية abstract . يجب على الصفوف الأبناء غير المجردة أن تعيد كتابة Override@ جميع الدوال المجردة في صفوفها العليا. من الممكن أن تكون الصفوف المجرّدة مفيدة عند الجمع بين منطق التكرار و السلوك المخصص، ولكن بما أن الصف المجرد يتطلب الوراثة، فإن الصفوف المجردة تنتهك منطق "التركيب عبر الوراثة". لذلك خُذ بعين الاعتبار الطرق الأخرى التي تستخدم هذا التركيب public abstract class Animal { private int age; public abstract void makeSound(); من الممكن أن يحوي المنهج جسم public void eat() { System.out.println("I am an animal and I am Eating."); //(private)ملاحظة: من الممكن هنا الوصول للمتغيرات الخاصة age = 30; } public void printAge() { System.out.println(age); } يمكن أن تحوي الصفوف المجردة على دالة رئيسية public static void main(String[] args) { System.out.println("I am abstract"); } } class Dog extends Animal { لاحظ بأنه لا يزال هناك حاجة لإعادة كتابة الدوال التجريدية في الصف التجريدي // @Override public void makeSound() { System.out.println("Bark"); // age = 30; ==> ERROR! // Animal خاص بالنسبة للصف age خطأ- و ذلك لأن المتغير } ملاحظة: من الممكن أن تحصل على خطأ إذا قمت هنا باستخدام Override@ لأن جافا لا تسمح لك بإعادة كتابة الدوال الساكنة. ما يحدث هنا يُدعى إخفاء الدالة METHOD HIDING public static void main(String[] args) { Dog pluto = new Dog(); pluto.makeSound(); pluto.eat(); pluto.printAge(); } الصفوف النهائية final class صيغة التصريح عن الصفوف النهائية // <access-level> final <final-class-name> { // // Constants and variables // // Method declarations // } الصفوف النهائية هي عبارة عن الصفوف التي لا يمكن وراثتها، (أي هي الولد النهائي). بطريقة ما، تُعد الصفوف النهائية معاكس الصفوف المجردة. لأنه يجب توسيع (وراثة) الصفوف المجردة، أما الصفوف النهائية لا يمكن توسيعها. public final class SaberToothedCat extends Animal { لاحظ بأنه ما يزال يوجد حاجة لإعادة كتابة الدوال المجردة في الصف المجرد.// @Override public void makeSound() { System.out.println("Roar"); } } الدوال النهائية public abstract class Mammal() { // صيغة الدالة النهائية: // <access modifier> final <return type> <function name>(<args>) // لا يمكن إعادة كتابة الدوال النهائية بواسطة صف ابن، // وبالتالي الدالة النهائية هو أخر تنفيذ للدالة . public final boolean isWarmBlooded() { return true; } } النوع Enum еnum هي طريقة تنظيمية للثوابت (Constants) في الكود بحيث تجمع الثوابت التي لها علاقة ببعضها تحت فئة واحدة بطريقة تنظم الوصول إليها .بما أن هذه القيم هي عبارة عن ثوابت، لذلك فإن أسماء الحقول من النوع enum يجب أن تكتب بحروف كبيرة. في لغة جافا يتم تعريف هذا النوع باستخدام الكلمة المفتاحية enum. لتعريف متحول عن يوم من أيام الأسبوع، باستخدام النوع enum : public enum Day { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY } من الممكن استخدام متغيرنا Day من النوع enum كما يلي: public class EnumTest { //Enumالمتغير من النوع Day day; public EnumTest(Day day) { this.day = day; } public void tellItLikeItIs() { switch (day) { case MONDAY: System.out.println("Mondays are bad."); break; case FRIDAY: System.out.println("Fridays are better."); break; case SATURDAY: case SUNDAY: System.out.println("Weekends are best."); break; default: System.out.println("Midweek days are so-so."); break; } } public static void main(String[] args) { EnumTest firstDay = new EnumTest(Day.MONDAY); firstDay.tellItLikeItIs(); // => Mondays are bad. EnumTest thirdDay = new EnumTest(Day.WEDNESDAY); thirdDay.tellItLikeItIs(); // => Midweek days are so-so. } } الأنواع enum هي فعالة بشكل أكبر مما تم توضيحه فوق. من الممكن أن يحتوي جسم المتغير enum على دوال و حقول أخرى. ترجمة -وبتصرّف- للمقال Learn Java in Y Minutes
-
يشرح هذا المقال طريقة كتابة تعليمات تحوي متغيرات تخزن فيها قيمًا مختلفة، كالأرقام والكلمات، وتحوي أيضًا عوامل حسابية (operators)، وهي رموز تمثل عمليات حسابية. ثم يُعرّج على فكرة التركيب (أي استخدام مزايا اللغة التي تعرفنا عليها سابقاً مع بعضها)، كما يتحدث عن ثلاثة أنواع من الأخطاء البرمجية ونذكر فيه المزيد من النصائح عن تنقيح البرامج من الأخطاء. التصريح عن المتغيرات أحد أقوى المزايا لأي لغة برمجة هي القدرة على تعريف ومعالجة المتغيرات (variables). المتغير هو منطقة ذات اسم تخزّن قيمة (value). قد تكون القيم أرقامًا، أو نصوصًا، أو صورًا، أو مقاطع صوتية، وغيرها من أنواع البيانات. عليك أولًا التصريح عن متغير ثم تخزين القيم فيه. String message; هذا النوع من التعليمات يدعى تصريح (declaration)، لأنها تصرح أن نوع المتغير المدعو message هو String. لكل متغير نوع (type) يحدد نوع القيم التي يمكنه تخزينها. مثلًا، النوع int يخزن الأعداد الصحيحة، والنوع String يخزن السلاسل المحرفية. بعض الأنواع تبدأ بحرف كبير وبعضها الآخر يبدأ بحرف صغير. سنعرف معنى هذا التمييز لاحقًا، لكن الآن عليك الانتباه لكتابتها بشكل صحيح. ليس هناك نوع Int ولا string. لإنشاء متغير من النوع الصحيح، التعليمة هي: int x; حيث x هو اسم كيفي اخترناه للمتغير. بشكل عام، عليك اختيار أسماء المتغيرات بحيث تدل على دور المتغير في البرنامج. مثلًا، عندما ترى التصريحات التالية عن المتغيرات: String firstName; String lastName; int hour, minute; هذا المثال يصرح عن متغيرين من نوع String ومتغيرين من نوع int. عندما يتكون اسم المتغير من كلمتين أو أكثر، مثل المتغير firstName، جرت العادة على جعل الحرف الأول من كل كلمة حرفًا كبيرًا عدا الكلمة الأولى. أسماء المتغيرات حساسة لحالة الأحرف، ولذلك فالمتغير firstName مختلف عن المتغير firstName أو FirstName. يوضح هذا المثال أيضًا صيغة التصريح عن عدة متغيرات من نفس النوع في سطر واحد: كلًا من hour و minute هو عدد صحيح (متغير من النوع int). لاحظ أن كل تعليمة تصريح تنتهي بفاصلة منقوطة. يمكنك تسمية متغيراتك كما تشاء. لكن هناك حوالي 50 كلمة محجوزة، تدعى الكلمات المفتاحية (keywords)، ولا يسمح لك باستخدامها كأسماء لمتغيراتك. هذه الكلمات تشمل public، وclass، وstatic، وvoid، وint، التي يستخدمها المترجم لتحليل بنية البرنامج. هناك قائمة كاملة بالكلمات المفتاحية موجودة، لكن لا حاجة لحفظها. معظم المحررات المستخدمة في البرمجة توفر ميزة "تلوين الشفرة" (syntax highlighting)، التي تجعل الأجزاء المختلفة من البرنامج تظهر بألوان مختلفة. الإسناد بعد أن صرحنا عن بعض المتغيرات، سنستخدمها لتخزين بعض القيم فيها. يمكننا عمل ذلك باستخدام تعليمة الإسناد (assignment). message = "Hello!"; // give message the value "Hello!" hour = 11; // assign the value 11 to hour minute = 59; // set minute to 59 يبين هذا المثال ثلاث تعليمات إسناد، والتعليقات تظهر ثلاثة أساليب يستخدمها الناس أحيانًا عندما يقرؤون تعليمات الإسناد. قد تكون المفردات مربكة هنا، لكن الفكرة واضحة : عندما تصرح عن متغير، أنت تنشئ منطقة تخزينية لها اسم. عندما تطبق تعليمة الإسناد على متغير، فأنت تغير القيمة التي يحويها. كقاعدة عامة، يجب أن يكون نوع المتغير من نفس نوع القيمة التي تسندها إليه. مثلًا، لا يمكنك تخزين سلسلة محرفية في المتغير minute أو عددًا صحيحًا في message. من ناحية أخرى، هذه القاعدة قد تكون مصدرًا للإرباك أحيانًا، بسبب وجود العديد من الطرق التي تسمح لك بتحويل القيم من نوع لآخر، وأحيانًا تحول Java الأشياء تلقائيًا. لكن الآن عليك فقط أن تتذكر القاعدة العامة بأن المتغيرات والقيم يجب أن تكون من نفس النوع، وسنتحدث عن الحالات الخاصة لاحقًا. أحد مصادر الإرباك هو أن بعض السلاسل المحرفية تبدو مثل الأرقام، لكنها ليست كذلك. مثلًا، يمكن أن يخزن المتغير message السلسلة المحرفية "123"، المكونة من المحارف '1' و '2' و '3'، لكنها ليست مثل العدد الصحيح 123. message = "123"; // legal message = 123; // not legal يجب تهيئة المتغيرات (initialize)، أي إسناد قيمة لها أول مرة، قبل أن تستخدمها. يمكنك التصريح عن متغير ثم إسناد قيمة له لاحقًا، كما في المثال السابق. كما يمكنك أيضًا التصريح عن المتغير وتهيئته بسطر واحد: String message = "Hello!"; int hour = 11; int minute = 59; مخططات الحالة قد تظن أن تعليمة a = b هي تعليمة مساواة لأن Java تستخدم الرمز = لعملية الإسناد. لكنها ليست مساواة! المساواة عملية تبديلية، أما الإسناد فلا. على سبيل المثال، في الرياضيات إذا كان a = 7 إذًا 7 = a. لكن في Java a = 7; تعليمة إسناد مشروعة، لكن 7 = a غير مشروعة. يجب أن يكون الطرف الأيسر من تعليمة الإسناد متغيرًا (اسمًا لموقع تخزيني). في الرياضيات أيضًا، جملة المساواة صحيحة دائمًا. إذا كان a = b الآن، فإن a سيبقى مساويًا لـ b دائمًا. أما في Java، فتعليمة الإسناد قد تجعل قيمتي متغيرين متساويتان، لكن قد لا يستمران على هذه الحال. int a = 5; int b = a; // a and b are now equal a = 3; // a and b are no longer equal في السطر الثالث تغيرت قيمة a، لكن قيمة b لم تتغير، وبالتالي لم يبق المتغيران متساويان. المتغيرات في البرنامج مع قيمها الحالية تشكل حالة البرنامج (state). يُظهِر الشكل 2.1 حالة البرنامج بعد تنفيذ هذه التعليمات. تدعى هذه المخططات التي تظهر حالة البرنامج بمخططات الحالة (state diagrams). يُمثَّل كل متغير بصندوق يظهر اسم المتغير خارجه وقيمة المتغير داخله. أثناء تنفيذ البرنامج تتغير الحالة، لذلك عليك اعتبار مخططات الحالة كتمثيل لحظي لنقطة محددة في مسار التنفيذ. طباعة المتغيرات يمكنك عرض قيمة متغير باستخدام println أو print. في التعليمات التالية صرحنا عن متغير اسمه firstLine، وأسندنا له القيمة "!Hello, again"، وعرضنا تلك القيمة. String firstLine = "Hello, again!"; System.out.println(firstLine); عندما نتحدث عن عرض متغير فنحن نقصد قيمة المتغير عمومًا. أما لعرض اسمالمتغير، فعليك أن تضعه بين علامتي اقتباس. مثلًا: System.out.print("The value of firstLine is "); System.out.println(firstLine); خرج هذا البرنامج هو: The value of firstLine is Hello, again! بنية تعليمة عرض المتغير هي نفسها بغض النظر عن نوع المتغير. مثلًا: int hour = 11; int minute = 59; System.out.print("The current time is "); System.out.print(hour); System.out.print(":"); System.out.print(minute); System.out.println("."); خرج هذا البرنامج هو: The current time is 11:59. لوضع عدة قيم على نفس السطر، من الشائع استخدام عدة تعليمات print ثم تتبعها تعليمة println في النهاية. لكن لا تنسَ تعليمة println على العديد من الحواسيب، يتم تخزين خرج تعليمات print دون عرضه على الشاشة حتى استدعاء println؛ وعندها يظهر السطر كله دفعة واحدة. إذا أغفلت تعليمة println، فقد يعرض البرنامج الخرج المخزن في أوقات غير متوقعة أو ربما انتهى البرنامج دون طباعة أي شيء. العوامل الحسابية العوامل (operators) هي رموز تمثل حسابات بسيطة. مثلًا، عامل الجمع هو +، وعامل لطرح -، والضرب *، والقسمة /. يحول البرنامج التالي الوقت إلى دقائق: int hour = 11; int minute = 59; System.out.print("Number of minutes since midnight: "); System.out.println(hour * 60 + minute); في هذا المثال، لدينا التعبير (expression) التالي: hour * 60 + minute، الذي يمثل قيمة وحيدة بعد الحساب. عند تنفيذ البرنامج، يستبدل كل متغير بقيمته الحالية، ثم تطبق العوامل عليها. تدعى القيم التي تعمل العوامل عليها بالمعاملات (operands). نتيجة المثال السابق هي: Number of minutes since midnight: 719 التعابير هي تراكيب تتألف بشكل عام من أرقام، ومتغيرات، وعوامل. عند ترجمة وتنفيذ التعابير ستنتج لدينا قيمة وحيدة. مثلًا، التعبير 1 + 1 قيمته 2. وفي التعبير hour - 1 تستبدل Java المتغير بقيمته، وبذلك ينتج 11 - 1، الذي قيمته 1. أما في التعبير hour * 60 + minute فيستبدل المتغيران بقيمتيهما، وهذا يعطي 11 * 60 + 59. تنفذ عملية الضرب أولًا، معطية التعبير 660 + 59. بعد ذلك تجرى عملية الجمع التي تنتج 719. عمليات الجمع والطرح والضرب كلها تعمل كما تتوقع منها تمامًا، لكن عملية القسمة قد تفاجئك. مثلًا، نحاول في التعليمتين التاليتين حساب الجزء الذي مضى من الساعة: System.out.print("Fraction of the hour that has passed: "); System.out.println(minute / 60); الخرج هو: Fraction of the hour that has passed: 0 هذه النتيجة تحير الناس عادة. قيمة minute هي 59، وناتج قسمة 59 على 60 هو 0.98333، وليس 0. المشكلة هي أن Java تنفذ عملية "القسمة الصحيحة" عندما يكون المعاملين عددين صحيحين. عملية القسمة الصحيحة تقرب الناتج دومًا إلى العدد الصحيح السابق، حتى في الحالات التي يكون العدد الصحيح التالي أقرب مثل حالتنا هذه. يمكننا كحل بديل حساب النسبة المئوية بدلًا من العدد العشري: System.out.print("Percent of the hour that has passed: "); System.out.println(minute * 100 / 60); الخرج الجديد هو: Percent of the hour that has passed: 98 لقد قربت النتيجة للأسفل هنا أيضًا، لكن النتيجة الآن صحيحة تقريبًا على الأقل. النقطة العائمة هناك حل مناسب أكثر وهو استخدام أعداد النقطة العائمة (floating-point)، التي تمثل الأعداد العشرية كما تمثل الأعداد الصحيحة أيضًا. في Java، يستخدم النوع double (اختصارًا لعبارة double-precision) افتراضيًا لأعداد النقطة العائمة. يمكنك إنشاء المتغيرات من نوع double وإسناد القيم لها باستخدام نفس الصيغ التي استخدمناها للأنواع الأخرى: double pi; pi = 3.14159; تنفذ Java عملية "قسمة النقطة العائمة" (floating-point division) إذا كان أحد المعاملات أو كلاهما من النوع double. وهكذا يمكننا حل المشكلة التي واجهتنا في القسم السابق: double minute = 59.0; System.out.print("Fraction of the hour that has passed: "); System.out.println(minute / 60.0); الخرج هو: Fraction of the hour that has passed: 0.9833333333333333 ورغم فائدة أعداد النقطة العائمة، إلا أنها قد تسبب الإرباك. مثلًا، Java تفرّق بين القيمة الصحيحة 1 وبين القيمة العشرية 1.0، حتى لو بدا أنهما نفس العدد، فهما يختلفان بالنوع، وعلى وجه الدقة، لا يسمح لك بتنفيذ عمليات إسناد بين النوعين. مثلًا، ما يلي ليس مسموحًا لأن المتغير على الطرف الأيسر من النوع int أما القيمة المسندة له على الطرف الأيمن هي double: int x = 1.1; // compiler error من السهل نسيان هذه القاعدة لأن هناك حالات عديدة تحول فيها Java أحد الأنواع إلى النوع الآخر تلقائيًا. مثلًا: double y = 1; // legal, but bad style من المفترض ألا تكون التعليمة السابقة مشروعة، لكن Java تسمح بها عن طريق التحويل القيمة الصحيحة 1 إلى القيمة العشرية 1.0 تلقائيًا. هذا التساهل مريح، لكنه يسبب المشاكل للمبتدئين غالبًا. مثلًا: double y = 1 / 3; // common mistake قد تتوقع أن يعطى المتغير y القيمة 0.333333، وهي قيمة عشرية مشروعة، لكنه في الواقع سيعطى القيمة 0.0. السبب هو أن العبارة على اليمين هي نسبة بين عددين صحيحين، لذلك تجري Java عملية قسمة صحيحة، والتي تنتج القيمة الصحيحة 0. ثم يتم تحويلها إلى قيمة عشرية، الناتج هو 0.0. إحدى الطرق لحل هذه المشكلة (بعد أن تكتشف أن هذه هي المشكلة) هو جعل الطرف الأيمن عبارة عشرية. التعليمة التالية ستعطي y القيمة 0.333333، كما هو متوقع. double y = 1.0 / 3.0; عليك دائمًا إسناد قيم عشرية لمتغيرات النقطة العائمة في كتابتك. لن يجبرك المترجم على ذلك، لكنك لا تعرف أبدًا متى تظهر لك غلطة بسيطة وتعود عليك وبالًا. أخطاء التقريب معظم أرقام النقطة العائمة صحيحة تقريبيًا. يمكن تمثيل بعض الأرقام بدقة، مثل القيم الصحيحة ذات الأحجام المعقولة. أما الكسور الدورية، مثل 1/3، أو الأرقام غير النسبية، مثل π، فلا يمكن تمثيلها بدقة. الفرق بين العدد الذي نريد والعدد الذي نحصل عليه يدعى خطأ التقريب (rounding error). مثلًا، يجب أن تكون التعليمتان التاليتان متكافئتين: System.out.println(0.1 * 10); System.out.println(0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1); لكن الخرج سيكون كما يلي على معظم الحواسيب: 1.0 0.9999999999999999 المشكلة هي أن 0.1، وهو عدد عشري منته في الأساس 10، هو كسر دوري في الأساس 2، ولذلك يكون تمثيله في النقطة العائمة تقريبي حتمًا. وعندما نجمع الأعداد التقريبية معًا تتراكم أخطاء التقريب. الحساب بالنقطة العائمة له مزايا تفوق عيوبه في العديد من التطبيقات، كالرسوميات الحاسوبية، والتشفير، والتحليل الإحصائي، وإظهار (rendering) الوسائط المتعددة. لكن إذا أردت دقة مطلقة، عليك استخدام الأعداد الصحيحة بدلًا منها. خذ على سبيل المثال حساب بنك فيه رصيد قيمته 123.45$: double balance = 123.45; // potential rounding error في هذا المثال، ستصبح الأرصدة غير دقيقة مع الوقت واستخدام المتغير في العمليات الحسابية كالسحب والإيداع. ستكون النتيجة سخط العملاء أو دعاوى قضائية. يمكنك تفادي المشكلة بتمثيل الرصيد كعدد صحيح: int balance = 12345; // total number of cents هذا الحل صحيح طالما أن عدد السنتات لا يتجاوز أكبر قيمة صحيحة يمكن تخزينها، وهي حوالي 2 مليار. العمليات على السلاسل المحرفية بشكل عام، لا يمكنك تطبيق العمليات الرياضية على السلاسل المحرفية، حتى لو كانت السلاسل المحرفية تبدو وكأنها أرقام. التعابير التالية غير مشروعة: "Hello" - 1 "World" / 123 "Hello" * "World" العامل + يعمل مع السلاسل المحرفية، لكنه قد لا ينتج ما تتوقعه. يجري عامل + عملية ربط السلاسل (concatenation) أي دمج المعاملين بوصلهما معًا. وهكذا فإن "Hello" + "World" ستعطي السلسلة "Hello World". أو إذا كان لديك متغير اسمه name من النوع String، فسوف يدمج التعبير "Hello" + "Name" قيمة name مع كلمة الترحيب. بما أن عملية الجمع معرفة للأرقام والسلاسل المحرفية أيضًا، فإن Java تجري عمليات تحويل تلقائية قد لا تتوقعها: System.out.println(1 + 2 + "Hello"); // the output is 3Hello System.out.println("Hello" + 1 + 2); // the output is Hello12 تنفذ Java هذه العمليات من اليسار إلى اليمين. في السطر الأول، 1 + 2 يساوي 3، و 3 + "Hello" يساوي "3Hello". أما في السطر الثاني، Hello" + 1" يساوي "Hello1"، و Hello1" + 2" يعطي "Hello12". ترتيب الحساب عندما يظهر أكثر من عامل في تعبير حسابي فسوف تنفذ حسب ترتيب العمليات (order of operation). بشكل عام، تنفذ Java العمليات حسب ترتيب ورودها من اليسار إلى اليمين (كما رأينا في القسم السابق). لكن Java تتبع قواعد الرياضيات في العمليات الحسابية: عمليتي الضرب والقسمة لهما ”أولوية“ (precedence) على الجمع والطرح. لذا فإن 3 * 2 + 1 سيعطي 7، وليس 9، كما أن 2 / 4 + 2 تعطي 4، وليس 3. إذا كان للعوامل نفس الأولوية فسوف تنفذ بالترتيب من اليسار إلى اليمين. ففي التعبير الحسابي minute * 100 / 60، يتم تنفيذ عملية الضرب أولًا، وإذا كانت قيمة minute هي 59 فسوف ينتج لدينا 60 / 5900، والذي بدوره يعطي 98. لو أن تنفيذ العملية الحسابية جرى من اليمين لليسار، ستكون النتيجة 1 * 59 والذي هو 59، وهو جواب خاطئ. في أي وقت ترغب فيه بتجاوز قواعد الأولوية (أو أنك لم تكن واثقًا من تلك القواعد) يمكنك استعمال الأقواس. يتم تنفيذ العمليات ضمن الأقواس أولًا، لهذا فإن 3 * (2 + 1) يعطي 9. يمكنك استعمال الأقواس أيضًا لجعل العبارات الحسابية أسهل للقراءة، كما في 60 / (minute * 100)، مع أنها لا تغير النتيجة. لا تجهد نفسك في حفظ ترتيب تنفيذ العمليات، خصوصًا مع العوامل الأخرى. إذا لم يكن ترتيب التنفيذ واضحًا عند النظر إلى التعبير، فاستخدم الأقواس لجعله واضحًا. التركيب في الأجزاء السابقة كنا نتعرف على مكونات لغة البرمجة: المتغيرات، والتعابير، والتعليمات بشكل مستقل، دون أن نناقش طريقة استخدامها معاً. أحد أهم ميزات لغات البرمجة هي قدرتها على تركيب (compose) الأجزاء الصغيرة مع بعضها. مثلاً، نحن نعرف كيف نضرب الأرقام ونعرف كيف نعرض القيم. يمكننا دمج هاتين العمليتين في تعليمة واحدة: System.out.println(17 * 3); يمكن استخدام أي تعبير حسابي داخل تعليمات الطباعة. لقد شاهدنا مثالاً على هذا من قبل: System.out.println(hour * 60 + minute); يمكنك أيضاً وضع تعابير حسابية متنوعة على الطرف الأيمن لعملية الإسناد: int percentage; percentage = (minute * 100) / 60; لكن الطرف الأيسر لا بد أن يكون اسم متغير، وليس تعبيراً. ذلك لأن الطرف الأيسر يدل على موقع تخزين النتيجة، والتعابير لا تمثل مواقع تخزينية. hour = minute + 1; // correct minute + 1 = hour; // compiler error قد لا تبهرك القدرة على تركيب العمليات الآن، لكننا سنرى لاحقاً أمثلة تسمح لنا بكتابة حسابات معقدة بشكل مرتب وأنيق. لكن لا تبالغ كثيراً، فالتعابير الكبيرة المعقدة قد تصعب قراءتها وتنقيحها من الأخطاء. أنواع الأخطاء هناك ثلاثة أنواع يحتمل أن تحدث في البرنامج: أخطاء الترجمة. أخطاء التنفيذ. الأخطاء المنطقية. من المفيد التمييز بينها في سبيل تتبعها بشكل أسرع، وقبل أن نتحدث عنها واحدة واحدة، يمكنك الاطلاع على الفيديو الآتي الذي يشرحها بالتفصيل: تحدث أخطاء الترجمة (compile-time errors) عندما تخالف التراكيب النحوية (syntax) للغة Java. مثلاً، يجب أن تكون أزواج الأقواس متناظرة. ولذلك فإن (2 + 1) صيغة مقبولة أما (8 ليست كذلك. في الحالة الثانية، لن تتمكن من ترجمة البرنامج، وسيعرض المترجم رسالة خطأ. تدل رسائل الخطأ التي يعرضها المترجم على موقع حدوث الخطأ في البرنامج عادة، وأحياناً تخبرك بطبيعة الخطأ بدقة. على سبيل المثال، لنعد إلى برنامج hello world: public class Hello { public static void main(String[] args) { // generate some simple output System.out.println("Hello, World!"); } } إذا نسيت الفاصلة المنقوطة في نهاية تعليمة الطباعة، فقد تظهر لك رسالة خطأ كما يلي: File: Hello.java [line: 5] Error: ‘;’ expected هذا جيد جداً: موقع الخطأ صحيح، ورسالة الخطأ تخبرك بالمشكلة. لكن رسائل الأخطاء ليست يسيرة الفهم دوماً. أحياناً يعطي المترجم مكان اكتشاف الخطأ في البرنامج، وليس مكان حدوثه حقاً. وأحياناً يكون وصف المشكلة محيراً أكثر مما هو مفيد. مثلاً، إذا نسيت قوس الإغلاق المعقوف في نهاية main (سطر 6)، قد تحصل على رسالة تشبة الرسالة التالية: File: Hello.java [line: 7] Error: reached end of file while parsing هناك مشكلتان هنا. أولاً، رسالة الخطأ مكتوبة من وجهة نظر المترجم، وليس وجهة نظرك أنت. عملية الإعراب (parsing) هي عملية قراءة البرنامج قبل الترجمة؛ فإذا وصل المترجم لنهاية الملف قبل انتهاء الإعراب، فهذا يدل على نقصان شيء ما. لكن المترجم لا يعرف ما هو. كما أنه لا يعرف أين. يكتشف المترجم الخطأ عند نهاية البرنامج (سطر 7)، لكن القوس الناقص يجب أن يكون على السطر السابق. تحوي رسائل الأخطاء معلومات مفيدة، لذلك عليك محاولة قراءتها وفهمها. لكن لا تأخذها بشكل حرفي تماماً. ستمضي غالباً وقتاً طويلاً خلال الأسابيع الأولى في سيرتك البرمجية وأنت تتابع أخطاء الترجمة. لكن مع زيادة خبرتك، سوف ترتكب أخطاءً أقل وستعثر عليها أسرع. النوع الثاني من الأخطاء هي أخطاء التنفيذ (run-time errors)، وقد سمّيت كذلك لأنها لا تظهر قبل تنفيذ البرنامج. في Java، تظهر هذه الأخطاء عندما ينفذ المفسر شفرة بايت ويحدث خطأ ما. تدعى هذه الأخطاء "استثناءات" (exceptions) لأنها تدل عادة على حدوث شيء استثنائي (وسيء). أخطاء التنفيذ نادرة في البرامج البسيطة التي ستراها في الفصول القليلة الأولى، لذلك قد لا ترى واحداً إلا بعد حين. عند حدوث خطأ تنفيذي، يعرض المفسر رسالة خطأ تحوي معلومات تشرح ما حدث وتحدد مكان حدوثه. مثلاً، إذا أجريت عملية قسمة على صفر عن غير قصد فسوف تظهر رسالة تشبه ما يلي: Exception in thread “main” java.lang.ArithmeticException: / by zero at Hello.main(Hello.java:5) بعض هذه المعلومات مفيد في تنقيح البرنامج من الأخطاء. يتضمن السطر الأول اسم الاستثناء java.lang.ArithmeticException ورسالة تبين ما حدث بدقة أكبر "by zero /". يظهر السطر التالي العملية التي حدث فيها الخطأ؛ حيث يقصد بعبارة Hello.main العملية main في الصنف Hello. كما أنه يذكر أيضاً اسم الملف الذي عرفت فيه العملية (Hello.java) ورقم السطر الذي حدث فيه الخطأ (5). أحياناً تحوي رسائل الأخطاء معلومات إضافية لن تفهم معناها الآن. لذلك سيكون أحد التحديات معرفة الأجزاء المفيدة دون أن تغرق بالمعلومات الإضافية. وعليك أن تنتبه أيضاً أن السطر الذي سبب انهيار تنفيذ البرنامج قد لا يكون السطر الذي يحتاج للتصحيح. النوع الثالث من الأخطاء هو الأخطاء المنطقية (logic error). إذا كان هناك خطأ منطقي في برنامجك، فستتم ترجمته وتشغيله دون تولد أي رسالة خطأ، لكنه لن يعطي الناتج الصحيح المطلوب، بل سينفذ ما طلبته منه حرفياً. مثلاً، هذا برنامج hello world فيه خطأ منطقي: public class Hello { public static void main(String[] args) { System.out.println("Hello, "); System.out.println("World!"); } } هذا البرنامج يترجم وينفذ بشكل سليم، لكن الخرج هو: Hello, World! على فرض أننا أردنا أن يكون الخرج على سطر واحد، فهذا الناتج غير صحيح. المشكلة هي أن السطر الأول يستخدم println، بينما نحن أرنا غالباً استخدام print. قد يصعب التعرف على الأخطاء المنطقية لأنك ستضطر للعمل بالمقلوب: تنظر إلى مخرجات البرنامج وتحاول استنتاج السبب الذي يجعله يعطي هذه النتائج الخاطئة، وكيف تجعله يعطي النتائج الصحيحة. عادة لا يستطيع المترجم ولا المفسر مساعدتك هنا، لأنهما لا يعرفان ما هو الشيء الصحيح المطلوب. ترجمة -وبتصرف- للفصل Variables and operators من كتاب Think Java: How to Think Like a Computer Scientist لكاتبيه Allen B. Downey و Chris Mayfield.
-
المتغيرات Variables سبق وأن ذكرنا في الدرس الأوّل أنّه يوجد نمطان أساسيّان لأنواع المتغيّرات في سي شارب، وهما: أنواع قيمة value types وأنواع مرجعيّة reference types. تشتمل أنواع القيمة على الأنواع المُدمجة built-in في اللغة مثل int و float و decimal و double و bool وجميع الأنواع المُعرّفة كبنية struct. سنتحدّث عن البنى في درس لاحق. في حين تشتمل الأنواع المرجعيّة على أيّ نوع آخر وهذا يشتمل على عدد لا يحصى من الأنواع، فيكفيك أن تعرف مثلًا أنّ جميع الأنواع الموجودة في مكتبة FCL هي أنواع مرجعيّة، بالإضافة إلى أنّ أي نوع جديد (على شكل صنف class) ينشئه المستخدم يُعتبر نوعًا مرجعيًّا. ومن المفيد أن تعلم أنّ النوع المُضمّن string هو نوع مرجعيّ أيضًا. يكمن الفرق الأساسي بين أنواع القيمة والأنواع المرجعيّة في مكان وطريقة تخزين قيم المتغيّرات المصرّح عنها بواسطتها. فعند التصريح عن متغيّر من نوع قيمة، تُخزّن أي قيمة يتمّ إسنادها إليه ضمن المتغيّر نفسه أو بمعنى أدق تُخزّن في ذاكرة المُكدّس Stack Memory، أمّا المتغيّر المصرّح عنه على أنّه نوع مرجعيّ فالّذي يُخزّن ضمنه هو العنوان إلى موقع في الذاكرة. هذا الموقع موجود ضمن ما يُسمّى بذاكرة الكَوْمَة Heap Memory. انظر إلى الشكل التوضيحي التالي. حيث صرّحنا عن المتغيّر x من النوع int وخزّنّا القيمة 5 ضمنه. وبما أنّ int هو نوع قيمة، لذلك سيكون المتغيّر مع القيمة المخزّنة فيه ضمن المكدّس Stack. أمّا بالنسبة للمتغيّر s فهو من النوع string وقد أسندنا إليه النص "!Hello" وبما أنّ النوع string هو نوع مرجعيّ كما أسلفنا لذلك فالقيمة التي ستكون مخزّنة ضمن المتغيّر s في الحقيقة ليست النص "!Hello" إنّما العنوان address الذي يُشير إلى موقع ضمن الكومة Heap موجود ضمنه النص "!Hello"، وهذا بالمناسبة ليس سلوكًا تنفرد به سي شارب، بل هو موجود في لغات أخرى مثل ++C و C. سنتوسّع في هذا الموضوع قليلًا عندما نتحدّث عن الأصناف والكائنات لاحقًا في هذه السلسلة. يمكن استخدام أيّ مزيج من الحروف والأرقام عند تسمية المتغيّرات، بشرط أن يكون أوّل محرف في اسم المتغيّر حرفًا وليس رقمًا. كما لا يجوز أن يحتوي اسم المتغيّر على فراغات ولا يجوز أيضًا أن يكون مماثلًا لكلمة محجوزة في سي شارب مثل new أو class أو غيرها، ولا يجوز أن يحتوي على رموزًا خاصّة مثل & و$ و#، ولكن يُعتبر الرمز (_) underscore حرفًا ويجوز الابتداء به. دورة تطوير التطبيقات باستخدام لغة Python احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة اشترك الآن الأنواع المضمنة ومجالاتها الأنواع المُضمّنة هي الأنواع الموجودة ضمنًا في لغة سي شارب وفي إطار عمل دوت نت عمومًا. سنستعرض في الجدول التالي هذه الأنواع. لاحظ أنّ عمود "الاسم" يحتوي على أسماء الأنواع المستخدمة في سي شارب، في حين يحتوي العمود الذي يليه مباشرةً على اسم نفس النوع ولكن ضمن منصّة دوت نت. في الحقيقة يمكننا استخدام أي تسمية نرغبها ولكنّ الأسهل والأفضل هي في استخدام أسماء الأنواع في سي شارب. يعود سبب وجود أسماء أنواع مختلفة في إطار عمل دوت نت هو أنّه بإمكان أي لغة برمجة ضمن منصة دوت نت استخدام نفس هذه الأنواع ضمنها. الاسم النوع الموافق في منصّة دوت نت القيم التي يقبلها الحجم في الذاكرة 1 bool System.Boolean true أو false 2 sbyte System.SByte من128- حتى 127 8 bits 3 byte System.Byte من 0 حتى 255 8 bits 4 short System.Int16 من 32,768- حتى 32,767 16 bits 5 ushort System.UInt16 من 0 حتى 65,535 16 bits 6 int System.Int32 من 2,147,483,648- حتى 2,147,483,647 32 bits 7 uint System.UInt32 من 0 حتى 4,294,967,295 32 bits 8 long System.Int64 من 9,223,372,036,854,775,808- حتى 9,223,372,036,854,775,807 64 bits 9 ulong System.UInt64 من 0 حتى 18,446,744,073,709,551,615 64 bits 10 char System.Char من U+0000 حتى U+ffff 16 bits 11 float System.Single من 3.4*1038- حتى +3.4*1038 32 bits 12 double System.Double من ±5.0*10-324 حتى ±1.7*10308 64 bits 13 decimal System.Decimal (-7.9*1028 to 7.9*1028)/(100 to 28) 128 bits 14 string System.String حسب حدود الذاكرة 15 object System.Object يمكن تخزين بيانات من أيّ نوع ضمن المتغيّرات من النوع object الأنواع من 2 حتى 9 هي أنواع صحيحة لا تقبل أعدادًا ذات فاصلة عشريّة. أما الأنواع من 11 حتى 13 فهي أنواع تقبل أعداد ذات فاصلة عشريّة وتختلف فيما بينها في مجالات الأعداد التي تقبلها ودقّة تلك الأعداد بالنسبة لعدد الخانات على يمين الفاصلة العشريّة. النوع char مخصّص للمتغيّرات التي تسمح بتخزين محرف character واحد فقط، وهو نوع يدعم ترميز Unicode، يُعتبر أي محرف موضوع ضمن علامتي اقتباس مفردتين مثل 'a' من نوع char. في الحقيقة أنّ النوع string يُعبّر عن سلسلة من المحارف من نوع char. النوع object هو الأب العام لجميع الأنواع في إطار العمل دوت نت ومنه تنحدر جميع الأنواع الأخرى مهما كانت، سنصادفه في هذه السلسلة مرّةً أخرى. العوامل Operators تدعم سي شارب نوعين من العوامل بشكل أساسيّ: عوامل أحاديّة unary operators وعوامل ثنائيّة binary operators. سنتحدّث في هذا الدرس عن أكثر العوامل استخدامًا في سي شارب. العوامل الأحادية لهذه العوامل أسبقيّة أعلى في الحساب من العوامل الثنائيّة، وهي تشمل العديد من العوامل يلخّص الجدول التالي أهمّها، انظر إلى الأمثلة العمليّة التي ستأتي بعد الجدول لمعرفة كيفيّة استخدامها: العامل الوصف الاستخدام ! عامل النفي المنطقي وهو عامل يُطبّق على القيم المنطقيّة من النوع bool. x! ~ عامل المتمّم الثنائي bitwise complement وهو عبارة عن عامل نفي ولكن على مستوى البتّات bits. x~ ++ لهذا العامل شكلان يعمل كلّ منها على زيادة قيمة متغيّر عددي بمقدار 1، ويختلفان فقط في توقيت هذه الزيادة. x++ عامل زيادة بادئ. ++x عامل زيادة لاحق. -- لهذا العامل شكلان أيضًا، يعمل كلّ منها على إنقاص قيمة متغيّر عددي بمقدار 1، ويختلفان فقط في توقيت هذا الإنقاص. x-- عامل إنقاص بادئ. --x عامل إنقاص لاحق. (T) وهو عامل التحويل بين الأنواع casting. وهو عامل مهم جدًّا سنصادفه مرارًا في هذه السلسلة. يمكن استبدال الحرف T باسم أيّ نوع يخطر على بالك مثل int وdouble وstring وغيرها. طريقة استخدامه هو في وضع النوع المراد التحويل إليه بين قوسين ونضعها جميعًا قبل القيمة التي نريد تحويلها مثل (int(x لتحويل قيمة x إلى قيمة من النوع int. فهم عاملي الزيادة والإنقاص شغّل برنامج Visual Studio 2015 Community وأنشئ مشروعًا جديدًا من النوع Console Application سمّه UnaryOperatorsTest1 ثم استبدل محتويات الملف Program.cs بالشيفرة التالية: 1 using System; 2 3 4 namespace UnaryOperatorsTest 5 { 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 int i = 1; 11 12 Console.WriteLine("Using of pre-increment operator (++i):"); 13 Console.WriteLine("Current value of i is {0}, and after applying ++i, the value of i becomes {1}", i, ++i); 14 Console.WriteLine(new string('-', 40)); 15 16 Console.WriteLine(); 17 i = 1; 18 19 Console.WriteLine("Using of post-increment operator (i++):"); 20 Console.WriteLine("Current value of i is {0}, and after applying i++, the value of i becomes {1}", i, i++); 21 Console.WriteLine(new string('-', 40)); 22 } 23 } 24 } نفّذ البرنامج باستخدام Ctrl+F5 (أو من القائمة Debug > Start Without Debugging) ستحصل على الخرج التالي: sing of pre-increment operator (++i): Current value of i is 1, and after applying ++i, the value of i becomes 2 ---------------------------------------- Using of post-increment operator (i++): Current value of i is 1, and after applying i++, the value of i becomes 1 ---------------------------------------- يوضّح هذا البرنامج البسيط استخدام عامل الزيادة البادئ وعامل الزيادة اللاحق. يبدأ البرنامج بالتصريح عن المتغيّر i من النوع int وإسناد القيمة 1 إليه. تعمل العبارة في السطر 13 على إظهار قيمتين، الأولى هي القيمة الحاليّة للمتغيّر i وتساوي 1، والقيمة الثانيّة هي قيمة المتغيّر i مضافًا إليها 1 باستخدام عامل الزيادة البادئ ++i أي هي القيمة 2، إذًا يقوم هذا العامل بزيادة قيمة المتغيّر i بمقدار 1 قبل تمرير القيمة النهائيّة إلى التابع WriteLine لذلك نحصل على الخرج: Current value of i is 1, and after applying ++i, the value of i becomes 2 ولكن على النقيض من ذلك، نلاحظ أنّ العبارة الموجودة في السطر 20 تعمل على إظهار قيمتين أيضًا، الأولى هي القيمة الحالية للمتغيّر i وتساوي 1 (أعدنا إسناد القيمة 1 للمتغيّر i في السطر 17)، والقيمة الثانيّة هي قيمة المتغيّر i مضافًا إليها 1 باستخدام الزيادة اللاحق i++ ولكن لن تمرَّر القيمة 2 هذه المرّة إلى التابع WriteLine. والسبب في ذلك أنّ البرنامج سيعمل على تمرير قيمة i الأصلية (القيمة 1) ثمّ يطبّق بعد ذلك عامل الزيادة اللاحق. وهذا هو سبب الحصول على الخرج التالي: Current value of i is 1, and after applying i++, the value of i becomes 1 لعلّ هذا السلوك يُسبّب بعض الإرباك للمبرمجين الجدد في سي شارب، وعلى أيّة حال أنصح بتجنّب تمرير القيمة إلى التوابع عمومًا بهذا الأسلوب. إذا احتجت لزيادة (أو إنقاص) قيمة متغيّر ما قبل تمرير لأحد التوابع فاعمل على ذلك ضمن سطر منفصل قبل استدعاء هذا التابع وأرح نفسك. في الحقيقة يُطبّق نفس المفهوم السابق بالنسبة لعامليّ الإنقاص البادئ والإنقاص اللاحق. ملاحظة: انظر إلى طريقة التنسيق الجديدة التي استخدمتها في السطر 13: Console.WriteLine("Current value of i is {0}, and after applying ++i, the value of i becomes {1}", i, ++i); مرّرت إلى التابع WriteLine ثلاثة وسائط: الأوّل هو النص التنسيقي وقد حُجز ضمنه مكانين مخصّصين لقيمتين سأمرّرهما لاحقًا لهذا التابع، هذان المكانان على الشكل {0} و {1}. الوسيط الثاني هو المتغيّر i، والوسيط الثالث هو ++i. سيعمل البرنامج على وضع القيمة الممرّرة للتابع WriteLine والتي تلي النص التنسيقي مباشرةً (في حالتنا هذه قيمة i) في المكان {0}، أمّا المكان {1} فسيُوضع ضمنه القيمة التالية وهي ++i. ينطبق نفس الكلام تمامًا على العبارة الموجودة في السطر 20. كما يحتوي السطران 14 و21 على أسلوب جميل لطباعة سطر فاصل في خرج البرنامج بغرض توضيحه. أنشأنا كائنًا من النوع string باستخدام العامل new ومرّرنا لبانيته وسيطين: الأوّل المحرف '-' من نوع char والثاني القيمة 40: new string('-', 40) سيولّد ذلك نصّا يحتوي على 40 محرف '-' مكرّر (لاحظ علامتي الاقتباس المفردتين ' ')، يمرَّر هذا النص بعد ذلك إلى التابع WriteLine. لا تقلق إن بدا هذا الكلام غير مفهومًا الآن، فسنتحدّث عن الكائنات فيما بعد. فهم عامل النفي المنطقي وعامل التحويل بين الأنواع أنشئ مشروعًا جديدًا من النوع Console Application سمّه UnaryOperatorsTest2 ثم استبدل محتويات الملف Program.cs بالشيفرة التالية: 1 using System; 2 3 4 namespace UnaryOperatorsTest2 5 { 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 bool b = true; 11 double d = 8.9; 12 int i; 13 14 Console.WriteLine("b = {0}, !b = {1}", b, !b); 15 16 i = (int)d; 17 Console.WriteLine("d = {0}, after applying casting to (int), i = {1}", d, i); 18 } 19 } 20 } نفّذ البرنامج باستخدام Ctrl+F5 لتحصل على الخرج التالي: b = True, !b = False d = 8.9, after applying casting to (int), i = 8 استخدمنا في هذا البرنامج المتغير b من النوع bool وهو نوع منطقيّ تحمل المتغيّرات المصرّح عنها بواسطته إحدى قيمتين true أو false. أسندنا للمتغيّر b القيمة true عند التصريح عنه في السطر 10، ثمّ عرضنا للمستخدم قيمة b الأصليّة وقيمته بعد تطبيق عامل النفي المنطقي عليه b! لنحصل على الخرج التالي: b = True, !b = False يعكس هذا العامل الحالة المنطقيّة، فإذا كانت true تصبح false، أمّا إذا كانت false فتصبح true. ولكن إذا لاحظت أنّ الخرج يُظهر القيمتين المنطقيتين true و false بحرفين كبيرين في بداية كل منهما: True و False. السبب في ذلك أن التابع WriteLine في السطر 14 يعمل بشكل ضمني على استدعاء التابع ToString لكل من الوسيطين الممرّرين له، أي الوسيطين b و b! فيحصل بذلك على التمثيل النصّي للقيمة المنطقيّة الموجودة في كلّ منهما، والذي يبدأ بحرف طباعي كبير. جرّب استبدال العبارة البرمجيّة في السطر 14 بالعبارة التالية: Console.WriteLine("b = {0}, !b = {1}", b.ToString(), (!b).ToString()); التعديل الذي أجريناه في السطر السابق هو استدعاء التابع ToString بشكل صريح لكلّ وسيط قبل تمريره إلى التابع WriteLine. ستحصل بذلك على نفس الخرج دون أيّ تغيير. بالنسبة لعمليّة التحويل بين الأنواع فقد أجريناها بين المتغيّر d من النوع double (السطر 11) والمتغيّر i من النوع int (السطر 12)، حيث سنحوّل القيمة ذات الفاصلة العشرية 8.9 الموجودة في d إلى قيمة صحيحة بدون فاصلة ونخزّنها ضمن i. تجري عملية التحويل هذه في السطر 16 على الشكل التالي: i = (int)d; لاحظ أنّ القوسين المحيطين بـ int ضروريين. إذا حاولت إزالة عامل التحويل (int) من العبارة السابقة وحاولت تنفيذ البرنامج فستحصل على الخطأ التالي: CS0266 Cannot implicitly convert type 'double' to 'int'. An explicit conversion exists (are you missing a cast?) يُشير هذا الخطأ إلى عدم إمكانيّة إسناد قيمة متغيّر من النوع double إلى متغيّر من النوع int مباشرةً بدون تحويل لأنّ ذلك سيؤدّي إلى ضياع في البيانات (ستضيع القيمة 0.9). يقترح عليك هذا الخطأ استخدام التحويل بين الأنواع cast في الجزء الأخير من الرسالة. أعد وضع عامل التحويل (int) أمام المتغيّر d ونفّذ البرنامج لتحصل في الخرج على ما يلي: d = 8.9, after applying casting to (int), i = 8 انظر كيف أصبحت قيمة i تساوي 8. في الواقع سيصادفنا عامل التحويل كثيرًا في هذه السلسلة. العوامل الثنائية تشتمل هذه العوامل على معظم العوامل الموجودة في سي شارب ولها العديد من الأصناف، تحتاج هذه العوامل إلى وجود مُعاملَين operands على طرفيها لكل تعمل، يلخّص الجدول التالي أهم هذه العوامل مع التصنيف الذي تقع ضمنه. العامل الوصف الاستخدام التصنيف + عملية الجمع العددي x + y عوامل - عملية الطرح العددي x - y حسابيّة * عملية الضرب العددي x * y / عملية القسمة العددية (إذا كان كل من المعاملين من نوع صحيح فسيكون ناتج القسمة صحيحًا بدون فاصلة، حيث تُهمل الأجزاء العشرية في حال وجودها). x / y % عمليّة باقي القسمة x % y > عامل اختبار "أصغر من" يُرجع القيمة true إذا كان المُعامل الأيسر أصغر من الأيمن، وإلّا يُرجع false. x < y عوامل مقارنة < عامل اختبار "أكبر من" يُرجع القيمة true إذا كان المُعامل الأيسر أكبر من الأيمن، وإلّا يُرجع false. x > y => عامل اختبار "أصغر من أو يساوي" يُرجع القيمة true إذا كان المُعامل الأيسر أصغر من أو يساوي الأيمن، وإلّا يُرجع false. x <= y =< عامل اختبار "أكبر من أو يساوي" يُرجع القيمة true إذا كان المُعامل الأيسر أكبر من أو يساوي الأيمن، وإلّا يُرجع false. x >= y == عامل اختبار "المساواة" بين قيمتين، يُرجع true إذا كانت القيمتين متساويتين وإلّا يُرجع false. x == y عوامل اختبار المساواة =! عامل اختبار "عدم المساواة" بين قيمتين، يُرجع true إذا كانت القيمتين غير متساويتين وإلّا يُرجع false. x != y && تطبيق منطق AND على قيمتين (أو تعبيرين) منطقيين. x && y العوامل || تطبيق منطق OR على قيمتين (أو تعبيرين) منطقيين. x || y الشرطية = عامل الإسناد للقيمة (أو التعبير) الموجودة في اليمين إلى المتغيّر الموجود في اليسار. x = y عوامل إسناد =+ عامل الجمع ثم الإسناد. x += y =- عامل الطرح ثم الإسناد. x -= y =* عامل الضرب ثم الإسناد. x *= y =/ عامل القسمة ثم الإسناد. x /= y =% عامل باقي القسمة ثم الإسناد. x %= y فهم العوامل الحسابية تُعتبر هذه العوامل بسيطة وواضحة وهي مشتركة بين جميع لغات البرمجة. على أيّة حال إليك برنامجًا بسيطًا يتعامل معها ويوضّح وظائفها. 1 using System; 2 3 namespace ArithmeticOperators 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 int x, y; 10 string str_x, str_y; 11 12 //input operands. 13 Console.Write("Input left operand (x) : "); 14 str_x = Console.ReadLine(); 15 16 Console.Write("Input right operand (y) : "); 17 str_y = Console.ReadLine(); 18 19 //convert each operand to integer representative. 20 x = int.Parse(str_x); 21 y = int.Parse(str_y); 22 23 24 Console.WriteLine(); 25 26 //perform arithmetic calculations and display results. 27 Console.WriteLine("x + y = {0}", x + y); 28 Console.WriteLine("x - y = {0}", x - y); 29 Console.WriteLine("x * y = {0}", x * y); 30 Console.WriteLine("x / y = {0}", x / y); 31 Console.WriteLine("x % y = {0}", x % y); 32 33 } 34 } 35 } نفّذ البرنامج باستخدام Ctrl+F5. سيطلب منك البرنامج إدخال المُعامل الأيسر left operand، ثم المُعامل الأيمن right operand، وبعدها ينفّذ العمليّات الحسابيّة الأربع عليهما. جرّب إدخال القيمتين 9 و 2 على الترتيب لتحصل على الخرج التالي: Input left operand (x) : 9 Input right operand (y) : 2 x + y = 11 x - y = 7 x * y = 18 x / y = 4 x % y = 1 العمليّات الثلاث الأولى واضحة. بالنسبة لعمليّة القسمة يجب أن يكون الناتج 4.5، ولكن بما أنّ عملية القسمة تجري بين قيمتين صحيحتين فإنّ النتيجة يجب أن تكون صحيحة، وبالتالي يُهمل الجزء العشري 0.5 ويكون الناتج 4 فقط. بالنسبة لعمليّة باقي القسمة x % y فإنّ النتيجة 1 هي باقي قسمة 9 على 2. ملاحظة: إذا لم ترغب بحذف الجزء العشري من ناتج عملية القسمة الصحيحة ودون أن تغيّر أنوع المتغيّرات، يمكنك استخدام عامل التحويل بين الأنواع (T). استبدال العبارة الموجودة في السطر 30 بالعبارة التالية: Console.WriteLine("x / y = {0}", x /(double)y); وضعت عامل التحول (double) قبل المتغيّر y لتحويل قيمته العدديّة إلى قيمة من نوع double (دون المسّ بقيمة y الأصليّة بالطبع)، فعندما يرى البرنامج أنّه يُجري عملية القسمة بين قيمة صحيحة (قيمة x) وقيمة من النوع double فسيعطي الناتج على شكل قيمة من نوع double تُمرّر بدورها إلى التابع WriteLine ليعرض القيمة 4.5 بدلًا من 4. ويمكن فعل نفس الأمر مع المتغيّر x بدلًا من y إذا أحببت. فهم عوامل المقارنة سنتناول عوامل المقارنة > و < و => و =< و == و =! في البرنامج التالي: 1 using System; 2 3 4 namespace RelationalOperators 5 { 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 int x, y; 11 string str_x, str_y; 12 13 14 //input operands. 15 Console.Write("Input left operand : "); 16 str_x = Console.ReadLine(); 17 18 Console.Write("Input right operand : "); 19 str_y = Console.ReadLine(); 20 21 //convert each operand to integer representative. 22 x = int.Parse(str_x); 23 y = int.Parse(str_y); 24 25 Console.WriteLine(); 26 27 //perform comparing operations and display results. 28 Console.WriteLine("{0} == {1} evaluates to {2}", x, y, x == y); 29 Console.WriteLine("{0} != {1} evaluates to {2}", x, y, x != y); 30 Console.WriteLine("{0} > {1} evaluates to {2}", x, y, x > y); 31 Console.WriteLine("{0} >= {1} evaluates to {2}", x, y, x >= y); 32 Console.WriteLine("{0} < {1} evaluates to {2}", x, y, x < y); 33 Console.WriteLine("{0} <= {1} evaluates to {2}", x, y, x <= y); 34 } 35 } 36 } نفّذ البرنامج وأدخل القيمتين 3 و 4 على الترتيب لتحصل على الخرج التالي: Input left operand : 3 Input right operand : 4 3 == 4 evaluates to False 3 != 4 evaluates to True 3 > 4 evaluates to False 3 >= 4 evaluates to False 3 < 4 evaluates to True 3 <= 4 evaluates to True تكون نتيجة تنفيذ عوامل المقارنة قيمة منطقية true أو false. جرّب إدخال قيم متنوّعة، كما جرّب إدخال قيمتين متساويتين وانظر إلى الخرج. فهم العوامل الشرطية العاملين الشرطيين && (AND) و || (OR) هما عاملان مهمّان جدًّا ويستخدمان بكثرة في بنى القرار في سي شارب. ولهما وجود في جميع لغات البرمجة. يوضّح البرنامج التالي استخدام هذين العاملين بصورة مبسّطة. 1 using System; 2 3 4 namespace RelationalOperators 5 { 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 int a, b, c, d; 11 bool and_operator, or_operator; 12 13 a = 1; 14 b = 2; 15 c = 5; 16 d = 9; 17 18 and_operator = (a > b) && (c <= d); 19 Console.WriteLine("({0} > {1}) && ({2} <= {3}) evaluates to {4}", a, b, c, d, and_operator); 20 21 or_operator = (a > b) || (c <= d); 22 Console.WriteLine("({0} > {1}) || ({2} <= {3}) evaluates to {4}", a, b, c, d, or_operator); 23 } 24 } 25 } لا نستخدم العوامل الشرطيّة بهذا الأسلوب في البرامج الحقيقيّة، ولكنّ هذا الأسلوب مفيد في توضيح آلية عمل العوامل الشرطيّة وتفاعلها مع عوامل المقارنة. نفّذ البرنامج لتحصل على الخرج التالي: (1 > 2) && (5 <= 9) evaluates to False (1 > 2) || (5 <= 9) evaluates to True تفسير هذا الخرج يسير للغاية. لنبدأ بالسطر الأوّل، نتيجة حساب التعبير الأول هو false: (1 > 2) && (5 <= 9) وسبب ذلك هو أنّ نتيجة التعبير (2 < 1) هو false، أمّا نتيجة حساب (9 => 5) هو true وبالتالي سيعمل العامل الشرطي && بالنتيجة على حساب التعبير false && true والذي يعطي بكلّ تأكيد القيمة المنطقية false. بالنسبة للسطر الثاني، وهو التعبير: (1 > 2) || (5 <= 9) والذي يعطي true. والسبب هو أنّ العامل الشرطي || سيعمل على حساب التعبير false || true والذي يعطي القيمة المنطقيّة true. لاحظ استخدام الأقواس على أطراف عوامل المقارنة، يمكن الاستغناء عنها، ولكن لا أنصح بذلك، استخدم الأقواس دومًا حتى ولو لم يكن استخدامها ضروريًا لتوضيح منطق البرنامج، ولكن استخدمها بحكمة. السبب في انتفاء الحاجة إلى استخدام الأقواس في هذا البرنامج، هو أنّ عوامل المقارنة لها أسبقيّة تنفيذ أعلى من العوامل الشرطيّة، لذلك فهي تُقيّم قبل تقييم العوامل الشرطيّة. فهم عوامل الإسناد استخدمنا حتى الآن عامل الإسناد (=). توجد عوامل إسناد أخرى تُسهّل البرمجة في سي شارب وهي =+ و =- و =* و =/ و =%. الأمر بسيط، بالنسبة لعامل الإسناد =+ يمكن توضيح عمله بالشيفرة التالية: int x = 3; x += 5; بعد تنفيذ الشيفرة السابقة ستصبح قيمة x تساوي 8. لأنّ العبارة x += 5 تكافئ تمامًا العبارة x = x + 5 ويمكننا استبدالها بها. يُطبّق نفس الأسلوب تمامًا على العوامل الباقية. فمثلًا انظر إلى الشيفرة التالية: int x = 21; x /= 4; x %= 3; هل تستطيع تخمين قيمة x بعد تنفيذ هذه الشيفرة؟ إذا كانت النتيجة 2 فقد أصبت. السبب في ذلك بسيط. فقد بدأنا بقيمة x تساوي 21 ثم نفّذنا العبارة x /= 4 التي تكافئ العبارة x = x / 4 وهي قسمة صحيحة، لذلك سيحمل x القيمة 5 (بدون فاصلة عشرية). بعد تنفيذ العبارة الأخيرة x %= 3 التي تكافئ العبارة x = x % 3 ستصبح قيمة x تساوي 2 لأنّ باقي قسمة 5 على 3 يساوي 2. وهذا كلّ ما في الأمر. تمارين داعمة تمرين 1 حاول تخمين القيمة المنطقيّة التي ستُطبع على الشاشة باستخدام القلم والورقة فقط: int a = 30; a /= 3; a %= 3; Console.WriteLine(a == 1); تمرين 2 حاول تخمين قيمة f التي ستُطبع على الشاشة باستخدام القلم والورقة فقط: int x; double f; x = 9; f = (double)x / 2; f *= 10; Console.WriteLine("f = {0}", f); الخلاصة لفد تعرّفنا في هذا الدرس على الأنواع المُضمّنة في سي شارب وعلى مجالات كلٍّ منها، وعلى الفرق الأساسي بين أنواع القيمة value types والأنواع المرجعيّة reference types. كما تحدّثنا عن معظم العوامل التي تدعمها سي شارب وتصنيفاتها. وتناولنا بعض الأمثلة التوضيحيّة على استخدامها. سنتحدّث في الدرس التالي عن بنى القرار وتغيير مسار البرنامج وهو موضوع مهم في جميع لغات البرمجة.