البحث في الموقع
المحتوى عن 'حلقة'.
-
تُعتبر الحلقات التكراريّة من البنى المهمّة في لغات البرمجة، حيث نستطيع من خلالها تنفيذ عبارة أو عدّة عبارات برمجيّة لعدد من المرّات. تدعم سي شارب مثل باقي لغات البرمجة نوعين من الحلقات التكراريّة من حيث عدد التكرار، فهناك الحلقات ذات العدد المحدّد من المرّات (حلقة for) والتي نعلم فيها عدد مرّات التكرار بشكل مسبق، والحلقات ذات العدد غير المحدّد من المرّات (حلقة do-while وحلقة while) التي يكون فيها عدد مرّات التكرار غير مُحدّدًا. حلقة for التكرارية يمكن من خلال هذه الحلقة تكرار تنفيذ عبارة برمجيّة أو أكثر عددًا محدّدًا من المرّات، ولهذه الحلقة الشكل العام التالي: for ([init_counter]; [loop_condition]; [counter_expression]) { statement1; statement2; ... } القسم [init_counter] هو قسم التهيئة الذي يتمّ من خلاله تهيئة متغيّر الحلقة بقيمة ابتدائيّة (وغالبًا ما يتمّ التصريح عنه في هذا القسم أيضًا)، يسمّى متغيّر الحلقة أيضًا بعدّاد الحلقة loop counter، أمّا القسم [loop_condition] فيمثّل شرط التكرار أو الاستمرار للحلقة، فهو تعبير مقارنة يعطي true أو false بحيث تستمرّ الحلقة بالتكرار طالما كان هذا الشرط محقّقًا (يعطي true). القسم الأخير [counter_expression] ويتمّ فيه عادةً إجراء عملية حسابية على متغيّر الحلقة وغالبًا ما تكون هذه العمليّة هي زيادة متغيّر الحلقة بمقدار واحد. انظر الشيفرة البسيطة التالية التي تعمل على تنفيذ العبارة التي تحوي التابع WriteLine ثلاث مرّات: for (int i = 0; i < 3; i++) { Console.WriteLine("i = {0}", i); } عند تنفيذ الشيفرة السابقة ستحصل على الخرج التالي: i = 0 i = 1 i = 2 لاحظ أنّنا صرّحنا عن المتغيّر i من النوع int وأسندنا إليها القيمة 0 في قسم التهيئة، وبالنسبة لشرط الاستمرار للحلقة i < 3 فمن الواضح أنّ الحلقة ستستمرّ بالتكرار طالما كانت قيمة i أصغر تمامًا من 3. أمّا بالنسبة للقسم الأخير فنعمل على زيادة قيمة متغيّر الحلقة i بمقدار 1 في كلّ دورة عن طريق عامل الزيادة اللاحق ++i. آلية عمل هذه الحلقة بسيطة: عندما يصل تنفيذ البرنامج إلى حلقة for يتمّ التصريح عن المتغيّر i وإسناد القيمة 0 إليه. يختبر البرنامج شرط استمرار الحلقة i < 3 فإذا كان true يبدأ بتنفيذ العبارات البرمجيّة الموجودة ضمن حاضنة for. وإلّا يخرج فورًا من الحلقة. بعد الانتهاء من تنفيذ العبارات ضمن حاضنة for، ينتقل البرنامج إلى التعبير ++i ليزيد قيمة i بمقدار 1. تتكرّر نفس الخطوتين 2 و 3. سيتكرّر في هذا المثال البسيط تنفيذ العبارة الموجودة في الحاضنة ثلاث مرّات لأنّ العدّ يبدأ من الصفر (قيمة i الابتدائيّة تساوي الصفر). لنتناول الآن برنامجًا عمليًّا وظيفته إيجاد مجموع سلسلة من الأعداد المتتالية. أنشئ برنامجًا جديدًا اسمه Lesson04_1 ثمّ استبدل محتويات الملف Program.cs بالشيفرة التالية: 1 using System; 2 3 namespace Lesson04_1 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 int n, sum = 0; 10 string str_n; 11 12 Console.WriteLine("This Program calculates the series: sum = 1 + 2 + 3 + ... + n"); 13 Console.Write("Input 'n' please: "); 14 str_n = Console.ReadLine(); 15 16 n = int.Parse(str_n); 17 18 for (int i = 1; i <= n; i++) 19 { 20 sum += i; 21 } 22 23 Console.WriteLine("sum = {0}", sum); 24 } 25 } 26 } يعمل البرنامج السابق على جمع الأعداد من 1 حتى القيمة المدخلة n. أي سيحسب برنامجنا مجموع السلسلة: 1 + 2 + 3 + … + n. لاحظ القيمة الابتدائيّة للمتغيّر i (تساوي 1) وشرط استمرار الحلقة i <= n في السطر 18. تذكّر أنّ العبارة الموجودة في السطر 20 تُكافئ العبارة sum = sum + i. نفّذ البرنامج وجرّب إدخال قيم مختلفة للمتغيّر n لتحصل على المجاميع الموافقة. ملاحظة: المتغيّر i في البرنامج السابق مرئي فقط ضمن الحلقة التكراريّة ولا وجود له خارجها، يعرف هذا بمجال الرؤية للمتغيّر variable scope. ستؤدّي محاولة الوصول للمتغيّر i خارج الحلقة إلى خطأ أثناء بناء البرنامج. سنكتب الآن برنامجًا آخرًا لحساب مجموع السلسلة: 2 + 4 + 6 + 8 + … + n. لن يختلف البرنامج في هذا المثال عن البرنامج Lesson04_1 باستثناء أنّنا سنجمع الأعداد الزوجية فقط. إليك البرنامج كما سيبدو: 1 using System; 2 3 namespace Lesson04_2 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 int n, sum = 0; 10 string str_n; 11 12 Console.WriteLine("This Program calculates the series: sum = 2 + 4 + 6 + ... + n"); 13 Console.Write("Input 'n' please: "); 14 str_n = Console.ReadLine(); 15 16 n = int.Parse(str_n); 17 18 for(int i = 0; i <= n; i += 2) 19 { 20 sum += i; 21 } 22 23 Console.WriteLine("sum = {0}", sum); 24 } 25 } 26 } لاحظ كيف نزيد قيمة المتغيّر i في كلّ تكرار للحلقة for بمقدار 2 باستخدام التعبير i += 2 (السطر 18)، وبالتالي نتفادى جمع الأعداد الفردية (لاحظ أنّ قيمة i بدأت من الصفر). فيما عدا ذلك يبدو هذا البرنامج مطابقًا لبنية البرنامج Lesson04_1. حلقة while التكرارية لهذه الحلقة التكراريّة الشكل العام التالي: while (loop_condition) { statement1; statement2; ... } ستتكرّر العبارات البرمجيّة الموجودة ضمن حاضنة while طالما كان الشرط loop_condition محقّقًا (أي true) وبمجرّد أن يصبح الشرط loop_condition غير محقّق تتوقّف الحلقة عن التكرار. سنعدّل البرنامج Lesson04_1 السابق لكي يسمح باستخدام الحلقة while. أنشئ مشروعًا جديدًا وسمّه Lesson04_3 ثم استبدل محتويات Program.cs بما يلي: 1 using System; 2 3 namespace Lesson04_3 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 int n, sum = 0, i = 1; 10 string str_n; 11 12 Console.WriteLine("This Program calculates the series: sum = 1 + 2 + 3 + ... + n"); 13 Console.Write("Input 'n' please: "); 14 str_n = Console.ReadLine(); 15 16 n = int.Parse(str_n); 17 18 while (i <= n) 19 { 20 sum += i; 21 22 i++; 23 } 24 25 Console.WriteLine("sum = {0}", sum); 26 } 27 } 28 } صرّحنا عن المتغيّر i وأسندنا له القيمة 1 في السطر 9 والذي سيمثّل متغيّر حلقة while. استبدلنا حلقة for بحلقة while في السطر 18 مع ملاحظة أنّ شرط استمرار الحلقة i <= 5 بقي دون تعديل. لاحظ العبارة المهمّة في السطر 22 والتي تحوي التعبير ++i الذي سيزيد قيمة i بمقدار واحد في كلّ دورة. إنّ إغفال هذه العبارة سيؤدّي إلى الدخول في حلقة لا نهائيّة، لأنّ شرط الاستمرار في هذه الحالة لن يعطي false أبدًا لأنّ قيمة i لن تتغيّر. نفّذ البرنامج وأدخل قيم مختلفة للمتغيّر n لاختبار البرنامج. جرّب الآن إدخال القيمة 0 للمتغيّر n ستحصل في الخرج على المجموع sum = 0 وهذا منطقيّ. إذ أنّنا نخبر البرنامج بأنّنا لا نريد جمع أي عدد. سبب الحصول على هذا الخرج في الواقع هو أنّ البرنامج أثناء التنفيذ لن يدخل إلى حلقة while مطلقًا لأنّ شرط الاستمرار i <= n سيكون غير محقّقًا منذ البداية (تذكّر أنّ قيمة i الابتدائيّة هي 1). حلقة do-while التكرارية لهذه الحلقة الشكل العام التالي: do { statement1; statement2; ... } while (loop_condition) وهي تشبه الحلقة while باستثناء أنّ شرط استمرار الحلقة loop_condition يجري اختباره في نهايتها وليس في بدايتها كما هو الحال مع حلقة while. قد لا يبدو هذا الأمر مهمًّا في البداية ولكنّه في الحقيقة عكس ذلك تمامًا. لفهم الفرق أنشئ مشروعًا جديدًا وسمّه Lesson04_04 ثمّ استبدل الشيفرة الموجودة في Program.cs بالشيفرة التالية: 1 using System; 2 3 namespace Lesson04_4 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 int n, sum = 0, i = 1; 10 string str_n; 11 12 Console.WriteLine("This Program calculates the series: sum = 1 + 2 + 3 + ... + n"); 13 Console.Write("Input 'n' please: "); 14 str_n = Console.ReadLine(); 15 16 n = int.Parse(str_n); 17 18 Do 19 { 20 sum += i; 21 22 i++; 23 } 24 while (i <= n); 25 26 Console.WriteLine("sum = {0}", sum); 27 } 28 } 29 } لا يختلف هذا البرنامج عن سابقيه في حساب مجموع السلسلة 1 + 2 + 3 + … + n، نفّذ البرنامج وأدخل القيمة 0 للمتغيّر n ستحصل في الخرج على المجموع sum = 1 وهذا خطأ بالطبع! السبب في ذلك أنّ اختبار شرط الاستمرار في حلقة do-while يجري بعد انتهاء الحلقة من تنفيذ أوّل دورة لها، حيث تؤدّي هذه الدورة إلى جعل قيمة المتغيّر sum تساوي 1 وقيمة i تساوي 2، وبعد ذلك يأتي اختبار الشرط i <= n والذي سيعطي false بالطبع وتتوقف الحلقة عن التكرار ولكن بعد فوات الأوان. في حلقة while (وحتى في حلقة for) لم نواجه هذه المشكلة لأنّ شرط استمرارها يجري اختباره في بداية الحلقة وقبل تنفيذ أي دورة تكراريّة. تمارين داعمة تمرين 1 اكتب برنامجًا يطبع الأعداد من 1 حتى 100 على الشاشة باستثناء الأعداد من مضاعفات العدد 5 أي على الشكل التالي: 1, 2, 3, 4, 6, 7, 8, 9, 11, … , 14, 16, … تلميح: ستحتاج في هذا التمرين إلى استخدام بنية if ضمن حلقة for واختبار قيمة التعبير المنطقي i % 5 == 0 على افتراض أنّ i هو عدّاد الحلقة. تمرين 2 اكتب برنامجًا يطلب من المستخدم إدخال عدد صحيح موجب ثمّ يوجد مضروب هذا عدد (قيمة العاملي له). فإذا أدخل عددًا سالبًا يجب على البرنامج أن ينبّه المستخدم على ذلك ويُنهي التنفيذ. تلميح: تذكّر أنّ مضروب عدد يُعبّر عن الجداءات للقيم المتناقصة لهذا العدد فمثلًا لإيجاد مضروب 5 (!5) نكتب: 5! = 5 * 4 * 3 * 2 * 1 تذكّر أيضًا أنّ !1 =1 و !0 = 1. الخلاصة تحدثنا في هذا الدرس عن الحلقات التكراريّة بأنواعها المختلفة. تدعم سي شارب عدة حلقات تكراريّة تُعتبر حلقة for من أهمّها. في الحقيقة توجد حلقة تكراريّة أخرى لم نتحدّث عنها في هذا الدرس، وهي حلقة foreach، وهي حلقة مفيدة جدًّا أجلّت الحديث عنها إلى أن نتعرّف على المجموعات Collections بأنواعها ونتعلّم التعامل معها.
-
سنعلم في هذا الدرس عن المصفوفات وحلقة foreach. لنفترض أنَّك تريد أن تُخزِّن مقدارًا كبيرًا من البيانات؛ أول 10000 من مضاعفات الرقم 5 على سبيل المثال، فعليك أنَّ تُعرِّف 10000 متغير لهم 10000 اسم؛ وهذه مهمة صعبة ومضنية بالتأكيد. أحد الحلول هو تخزين البيانات في مصفوفة، إذ يمكننا تخزين أي عدد نريده من القيم في مصفوفة وحيدة فقط! لنلقِ نظرةً عن آلية عمل المصفوفات. <?php // تعريف مصفوفة ذات ثلاثة عناصر $type1 = array('first element', 'second element', 'third element'); // نستعمل الدالة print_r لطباعة جميع عناصر المصفوفة // الناتج: Array ( [0] => first element [1] => second element [2] => third element ) print_r($type1); echo "<br>"; // نستعمل المفاتيح (الفهارس) للوصول إلى قيم معيّنة في المصفوفة // لاحظ أنَّ المفاتيح رقمية من 0 حتى 2 echo $type1[0]; echo "<br>"; echo $type1[1]; echo "<br>"; echo $type1[2]; echo "<br>"; ?> في السطر الثالث من الشيفرة السابقة، عرَّفنا مصفوفةً اسمها $type1وملأناها بثلاث قيم (first element، و second element، و third element). كيف يمكننا استعمالها الآن؟ كل قيمة من القيم المُخزَّنة في المصفوفة مرتبطة بمُعرِّف فريد يسمى الفهرس (index)؛ يمكنك الوصول إلى تلك القيم عبر الفهرس الخاص بها. يطبع السطر السابع في المثال السابق المصفوفة بأكملها باستعمال الدالة print_r()، وسترى أنَّه على الرغم من أنَّك قد ملأت القيم فقط، لكنك ستحصل أيضًا على أرقامٍ تبدأ من الصفر وترتبط بتلك القيم؛ وفي الواقع، تلك الأرقام هي الرقم التسلسلي للقيمة (تذكر أننا نبدأ العد في البرمجة من الرقم 0 بدلًا من 1). ثم قمنا بطباعة القيم التي خزناها في الموضع 0 و 1 و 2 على التوالي وبالترتيب. المصفوفات الترابطية استخدمنا الفهارس الافتراضية في المثال السابق (0، و 1، و2)، لكن يمكننا أن نُعرِّف الفهارس يدويًا كما في المثال الآتي: <?php // طريقةٌ أخرى لتعريف مصفوفة $type2 = array('key'=>'value', 'key2'=>'value2', 'key3'=> 'value3'); // الناتج: Array ( [key] => value [key2] => value2 [key3] => value3 ) print_r($type2); echo "<br>"; // المفاتيح هنا نصيّة، وهي key1، و key2، و key3 echo $type2['key3']; ?> عرَّفنا في المثال السابق الفهارس الخاصة بنا (تُسمى أيضًا المفاتيح [keys]) التي ترتبط مع تلك القيم. يسمى هذا النوع من المصفوفات بالمصفوفات ترابطية (associative arrays). الوصول إلى عناصر المصفوفات تُعتبَر كل قيمة في المصفوفة متغيرًا مستقلًا، فيمكننا أن نجري جميع العمليات التي يمكن القيام بها على المتغيرات (كالزيادة والنقصان وإسناد القيم …إلخ.): <?php // نعرِّف مصفوفةً فارغةً $arr = array(); // يمكننا إسناد البيانات إلى عناصر المصفوفة كما يلي $arr[0] = 'academy'; $arr[1] = 'hsoub'; // قد لا تكون المفاتيح ذات أرقام متتالية (لاحظ عدم وجود المفتاحين 2 و 3) $arr[4] = 'com'; // يمكننا استعمال نمطي المفاتيح (الرقمية والنصية) في نفس المصفوفة $arr['day'] = 'Monday'; $arr['num1'] = 4; $arr['num2'] = 5; // كل عنصر في المصفوفة مستقل تمامًا عما سواه $arr['num3'] = $arr['num1'] + $arr['num2']; // سنطبع كامل المصفوفة باستعمال print_r print_r($arr); ?> لاحظ كيف أننا عرفنا بدايةً مصفوفةً فارغة، ثم أضفنا عناصرها بذكر المتغير الحاوي على مصفوفة متبوعًا بأقواسٍ مربعة ([]) بداخلها مفتاح العنصر. يجدر بالذكر أننا نستطيع أن نستعمل مفاتيح رقمية ونصية في نفس المصفوفة. إذا أردنا أن نجعل PHP تُرقِّم عناصر المصفوفة تلقائيًا عند إسناد قيم لها، فيكفي أن نضع أقواسًا مربعة فارغة كما يلي: <?php // نعرِّف مصفوفةً فارغةً $arr = array(); // يمكننا جعل PHP تُرقِّم العناصر تلقائيًا كما يلي $arr[] = 'academy'; $arr[] = 'hsoub'; $arr[] = 'com'; // سنطبع كامل المصفوفة باستعمال print_r // الناتج: Array ( [0] => academy [1] => hsoub [2] => com ) print_r($arr); ?> يجدر بالذكر أننا نستطيع أن ندرج قيمة عنصر من عناصر المصفوفة في سلسلةٍ نصيةٍ مباشرةً كما في المتغيرات (أي يجب أن تكون علامة الاقتباس مزدوجةً) لكن علينا في المصفوفات أن نحيط اسم العنصر بقوسين معقوفين كما يلي (لاحظ أننا استعملنا الطريقة الأكثر شيوعًا الآن في تعريف المصفوفات، ألا وهي إحاطة عناصر المصفوفة بقوسين مربعين [] كما يظهر في المثال أدناه): المصفوفات والحلقات لنفترض أننا نريد إجراء بعض العمليات على كمية كبيرة من البيانات مخزَّنة في مصفوفة (ولنقل أننا نريد طباعة البيانات، أو حساب مجموع الأرقام الموجودة في المصفوفة)؛ فسنستعمل لهذا الغرض الحلقات، لنلقِ نظرةً على مثالٍ عن حلقة for: <?php $a = [1, 21, 23, 43, 32, 23, 4]; // عناصر المصفوفة هي: $a[0], $a[1], $a[2]... // سنُهيِّئ متغيرًا للمجموع ونسند إليه القيمة 0 $sum = 0; for($i = 0; $i < 7; $i++) { // ستزداد قيمة المتغير $i في كل تكرار للحلقة // أي أن قيمته ستتغير إلى قيمة مفتاح العنصر التالي في المصفوفة $sum += $a[$i]; } echo $sum; ?> أنصحك بأن تُعيد حلّ المثال السابق باستعمال حلقتَيّ while و do-while من درس الحلقات لكي تتدرب على استعمالها. هنالك حلقةٌ إضافيةٌ في PHP وُضِعَت خصيصًا للمصفوفات، اسمها حلقة foreach. إذ تبدأ هذه الحلقة من أول عنصر في المصفوفة وتنتهي بآخر عنصر فيها. لنعد كتابة المثال السابق باستخدام حلقة foreach: <?php $a = [1, 21, 23, 43, 32, 23, 4]; // عناصر المصفوفة هي: $a[0], $a[1], $a[2]... // سنُهيِّئ متغيرًا للمجموع ونسند إليه القيمة 0 $sum = 0; foreach ($a as $v) { // لو كنا نستعمل حلقة من نوع آخر، لكنا قد كتبنا // $v = $a[$i]; // $sum += $v; // i++; $sum += $v; } echo $sum; ?> نُسنِد -في كل تكرار للحلقة- قيمةً جديدةً للمتغير الذي اسمه $v ونعطيه قيمة العنصر التالي في المصفوفة $a؛ حلقة foreach مفيدة جدًا عندما لا تعرف عدد العناصر الموجودة في المصفوفة، أو إذا كنت تستعمل مصفوفة ترابطية (associative arrays). حلقة foreach مع المصفوفات الترابطية أما في حالة المصفوفات الترابطية، فعلينا تعريف متغيرين (بدلًا من واحد) لأننا لا نعلم قيمة الفهرس (أو المفتاح [key]) لكل عنصر. أول متغير نُعرِّفه هو المفتاح، والثاني هو القيمة التي ترتبط به. الشكل العام لحلقة foreach هو: foreach (array as $key => $value) { statements } تمعّن في المثال الآتي وانظر كيف أُسنِد المفتاح إلى متغير، وأُسنِدَت قيمته إلى متغيرٍ آخر؛ يجدر بالذكر أنَّ المصفوفة المستعملة هي مصفوفة متعددة الأبعاد، أي أنَّ قيمة العنصر هي بدورها مصفوفةٌ أخرى، ولا مانع من استعمال حلقة foreach داخل حلقة foreach أخرى إن دعت الضرورة إلى ذلك. <?php // نُعرِّف مصفوفة ذات بعدين $arr = [ 'student1' => array('SN' => 16472, 'math_grade' => 'A+'), 'student2' => array('SN' => 16483, 'math_grade' => 'C'), 'student3' => array('SN' => 16587, 'math_grade' => 'A') ]; // حلقة foreach تمر على جميع عناصر المصفوفة الرئيسية foreach ($arr as $student => $info) { // لاحظ كيف أنَّ القيمة المُنسدَة إلى المتغير $info هي مصفوفة أخرى echo "the serial number for $student is {$info['SN']}, and he got {$info['math_grade']} grade in math!"; } ?> دوال التعامل مع المصفوفات ألم تتساءل كيف سنعلم إن كانت المصفوفة تحتوي عنصرًا ما أم لا؟ ربما فكرت باستعمال حلقةٍ ما للمرور على جميع عناصر المصفوفة، لكن PHP تسهِّل عليك الأمر وتوفر لك دالةً لاختبار وجود عنصرٍ ما في المصفوفة ألا وهي الدالةin_array() التي تقبل وسيطين أولهما هو القيمة التي سيُبحَث عنها، وثانيهما هي المصفوفة التي سيُبحَث فيها؛ وستعيد هذه الدالة القيمة TRUE إن وجِدَت تلك القيمة في المصفوفة، و FALSE خلا ذلك. أما لو أردت معرفة إن كان مفتاحٌ ما موجودًا في المصفوفة، فاستعمل الدالةarray_key_exists() التي تقبل وسيطين أيضًا أولهما هو المفتاح الذي يُبحَث عنه، وثانيهما هو المصفوفة التي سيُبحَث فيها؛ وستعيد هذه الدالة TRUE إن وجد ذاك المفتاح، و FALSE عدا ذلك. مثالٌ عن استعمالهما: <?php $arr = ['subdomain' => 'academy', 'domain' => 'hsoub', 'root_domain' => 'com']; // التحقق إن كانت القيمة «hsoub» موجودةً في المصفوفة if (in_array('hsoub', $arr)) { echo '"hsoub" value found'; } if (array_key_exists('subdomain', $arr)) { echo '"subdomain" key found'; } ?> هل تتذكر كيف قسّمنا السلاسل النصية إلى قسمين يفصل بينهما محرف معيّن في درس السلاسل النصية وحصلنا على الجزء الواقع بعد ذاك المحرف؟ الدالة explode() تفعل المِثل، لكنها تُخزِّن الناتج في مصفوفة، حيث تقبل وسيطين أولهما هو السلسلة النصية التي تُمثِّل الفاصل بين عناصر المصفوفة، وثانيهما هو السلسلة النصية التي تريد تقسيمها؛ انظر إلى ناتج المثال الآتي لإزالة الغموض عن الكلام السابق: <?php $str = 'hsoub academy is the best academy ever'; // سنقسم السلسلة النصية السابقة عند كلمة is، ثم سنطبع مخرجاتها $arr1 = explode('is', $str); // الناتج: Array ( [0] => hsoub academy [1] => the best academy ever ) print_r($arr1); // سنقسم السلسلة النصية السابقة عند كلمة academy، مما يُنتِج ثلاثة عناصر في المصفوفة $arr2 = explode('academy', $str); // الناتج: Array ( [0] => hsoub [1] => is the best [2] => ever ) print_r($arr2); ?> أما لو أردت دمج عناصر المصفوفة وتحويلها إلى سلسلة نصية، فاستعمل الدالة implode()؛ التي لها شكلان عامان أحدهما يقبل وسيطًا وحيدًا هو المصفوفة التي تريد تحويلها إلى سلسلة نصية؛ والشكل الثاني يقبل وسيطين أولهما هو السلسلة النصية التي ستوضع بين عناصر المصفوفة وثانيهما هو المصفوفة التي تريد تحويلها إلى سلسلة نصية. <?php $arr = ['academy', 'hsoub', 'com']; // إن استعملنا implode ذات الشكل الأول، فسنحصل على academyhsoubcom $str1 = implode($arr); echo $str1; // أما لو استعملنا الشكل الثاني، ومررنا «.» كوسيط، فسنحصل على academy.hsoub.com $str2 = implode('.', $arr); echo $str2; ?> يُلخِّص الجدول الآتي بعض دوال المصفوفات الشهيرة:. table{border: 1px solid, black; border-collapse: collapse;} th, td{border: 1px solid black;padding: 5px 10px;} th{background-color: #fbfcfc;} الدالة شرحها array_merge() يُمرَّر إليها أكثر من مصفوفة كوسيط مهمتها هي دمج تلك المصفوفات مع بعضها بعضًا وإعادة المصفوفة الناتجة array_rand() اختيار قيمة عشوائية واحدة أو أكثر تقبل وسيطًا إجباريًا هو المصفوفة التي ستؤخذ القيمة العشوائية منها ووسيطًا اختياريًا يُحدِّد عدد القيم التي ستُعاد تُعيد سلسلةً نصيةً تحوي مفتاح العنصر العشوائي المُختار إن اختارت قيمةً عشوائيةً وحيدة تعيد مصفوفة بمفاتيح العناصر العشوائية إن كان الوسيط الثاني أكبر من 1 ()array_search تبحث في المصفوفة عن قيمة مُعيّنة وستُعيد المفتاح المقابل لها إن وُجِدَت تقبل وسيطين أولهما هو القيمة التي ستبحث عنها، وثانيهما هو المصفوفة التي سيُبحَث فيها array_sum() حساب مجموع القيم العددية في مصفوفة تقبل وسيطًا وحيدًا هو المصفوفة المُراد حساب مجموع الأعداد فيها ()array_unique تُزيل هذه الدالة جميع التكرارات في مصفوفة تُمرَّر إليها كوسيط ()count ربما هذه من أهم الدوال السابقة، إذ تُعيد عدد العناصر الموجودة في مصفوفة تُمرَّر إليها كوسيط. نستفيد كثيرًا من هذه الدالة في حلقة for إن لم نكن نعرف عدد عناصر المصفوفة نستطيع أن نكتب: for (int i=0; i<array.count(); i++) {} تمرين لديك المصفوفة الآتية باسم names: Array ( [16472] => Ayham [16483] => Ameen [15789] => Bashir ) والمصفوفة باسم grades: Array ( [16472] => Array ( [math] => 62 [arabic] => 76 [english] => 75 ) [16483] => Array ( [math] => 85 [arabic] => 71 [english] => 82 ) [15789] => Array ( [math] => 52 [arabic] => 86 [english] => 93 ) ) اكتب برنامجًا يأخذ اسم الطالب ثم يطبع جميع علاماته. المصادر مقال Array and foreach loop in PHP لصاحبه Harish Kumar. كتاب تعلم البرمجة بلغة PHP. صفحات Arrays و array وما هو موجود في Array Functions.
-
بعد أن تعلمنا التعامل مع المعاملات الموضعية (Positional parameters) في الدرس السابق، حان الوقت الآن لشرح آخر بُنية من بُنى التحكم: for. وكما في البنيتين while و until، تُستعمَل for لإنشاء حلقات تكرار. الشكل العام لحلقة for: for variable in words; do commands done الخلاصة هي أنَّ for تُسنِد كلمةً من قائمة الكلمات إلى متغيّرٍ معيّن، ثم تُنفِّذ الأوامر الموجودة داخل الحلقة، ثم تكرر ذلك إلى أن تُستعمَل جميع الكلمات الموجودة في القائمة. هذا مثالٌ عنها: #!/bin/bash for i in word1 word2 word3; do echo $i done أُسنِدَت -في بادئ الأمر- القيمة word1 إلى المتغير i، ثم نُفِّذ الأمر echo $i، ثم أُسنِدَت القيمة word2 إلى المتغير i، ثم نُفِّذ الأمر echo $i، وهكذا، إلى أن تُسنَد جميع الكلمات إلى المتغير i. الشيء المثير للاهتمام في for هو تنوع الطرائق التي تستطيع فيها بناء قائمة الكلمات، حيث يمكن استخدام جميع أنواع التوسعات (expansions). سنولِّد قائمة الكلمات في المثال الآتي باستخدام تعويض الأوامر (command substitution): #!/bin/bash count=0 for i in $(cat ~/.bash_profile); do count=$((count + 1)) echo "Word $count ($i) contains $(echo -n $i | wc -c) characters" done قمنا في المثال السابق بإحصاء عدد الكلمات في ملف .bash_profile، ثم أظهرنا عدد الحروف في كل كلمة. حسنًا، ما علاقة ذلك بالمعاملات الموضعية؟ حسنًا، يمكن استخدام المعاملات الموضعية كقائمة بالكلمات التي ستمرّ عليها الحلقة for: #!/bin/bash for i in "$@"; do echo $i done المتغير "@" هو متغيرٌ خاصٌ بالصَدَفة ويحتوي على قائمة بوسائط سطر الأوامر. تُستعمَل هذه التقنية عادةً لمعالجة قائمة ملفات عبر سطر الأوامر. هذا مثالٌ آخر: #!/bin/bash for filename in "$@"; do result= if [ -f "$filename" ]; then result="$filename is a regular file" else if [ -d "$filename" ]; then result="$filename is a directory" fi fi if [ -w "$filename" ]; then result="$result and it is writable" else result="$result and it is not writable" fi echo "$result" done جرِّب السكربت السابق، ومرِّر إليه قائمةً بعدِّة ملفات، أو استعمل محرفًا بديلًا مثل * وانظر إلى مخرجاته. هذا سكربتٌ آخر يُقارِن الملفات الموجودة في مجلدين، ويظهِر قائمة بالملفات الموجودة في المجلد الأول وغير الموجودة في المجلد الثاني: #!/bin/bash # cmp_dir - program to compare two directories # Check for required arguments if [ $# -ne 2 ]; then echo "usage: $0 directory_1 directory_2" 1>&2 exit 1 fi # Make sure both arguments are directories if [ ! -d $1 ]; then echo "$1 is not a directory!" 1>&2 exit 1 fi if [ ! -d $2 ]; then echo "$2 is not a directory!" 1>&2 exit 1 fi # Process each file in directory_1, comparing it to directory_2 missing=0 for filename in $1/*; do fn=$(basename "$filename") if [ -f "$filename" ]; then if [ ! -f "$2/$fn" ]; then echo "$fn is missing from $2" missing=$((missing + 1)) fi fi done echo "$missing files missing" لنوظِّف ما سبق في مثالٌ عملي. لنحاول تحسين الدالة home_space في السكربت الذي نبنيه لكي تُخرِج المزيد من المعلومات. كانت النسخة القديمة من الدالة تبدو كما يلي: home_space() { # Only the superuser can get this information if [ "$(id -u)" = "0" ]; then echo "<h2>Home directory space by user</h2>" echo "<pre>" echo "Bytes Directory" du -s /home/* | sort -nr echo "</pre>" fi } # end of home_space هذه هي النسخة الحديثة منها: home_space() { echo "<h2>Home directory space by user</h2>" echo "<pre>" format="%8s%10s%10s %-s\n" printf "$format" "Dirs" "Files" "Blocks" "Directory" printf "$format" "----" "-----" "------" "---------" if [ $(id -u) = "0" ]; then dir_list="/home/*" else dir_list=$HOME fi for home_dir in $dir_list; do total_dirs=$(find $home_dir -type d | wc -l) total_files=$(find $home_dir -type f | wc -l) total_blocks=$(du -s $home_dir) printf "$format" $total_dirs $total_files $total_blocks done echo "</pre>" } # end of home_space تتضمن هذه النسخة المُحسَّنة أمرًا جديدًا هو printf، الذي يُستخدم لتنسيق المُخرجات بناءً على محتويات "عبارة التنسيق" (format string). تنحدر أصول الأمر printf من لغة البرمجة C وهو موجودٌ أيضًا في لغاتٍ برمجيةٍ أخرى مثل C++ و Perl و awk و java و PHP وبالطبع bash. هنالك أمرٌ آخر جديد هو الأمر find، الذي يُستخدم للبحث عن ملفات أو مجلدات تُطابِق معيارًا أو مقياسًا محدَّدًا (criteria). استخدمنا الأمر find في الدالة home_space لعرض قائمة بالمجلدات والملفات العادية الموجودة في مجلد المنزل، ثم أحصينا عدد الملفات والمجلدات باستخدام الأمر wc (تذكَّر أنَّه اختصار للعبارة "Word Count"). النقطة المثيرة للاهتمام في دالة home_space المُعدَّلة هي كيفية تعاملنا مع مشكلة عدم السماح بالوصول إلى مجلدات المنزل من المستخدمين العاديين، يمكنك ملاحظة أنَّنا اختبرنا إن كان المستخدم هو الجذر بوساطة id ثم -اعتمادًا على ناتج الاختبار- أسندنا سلاسل نصية مختلفة إلى المتغير dir_list، الذي سيُمثِّل قائمة الكلمات لحلقة for التي تليه. وبهذه الطريقة، إذا شغَّل مستخدمُ عاديٌ السكربت، فستُعرض معلومات عن مجلد المنزل الخاص به فقط. موضوعٌ آخر يمكننا توظيف حلقة for فيه هو الدالة system_info التي لم نكملها بعد. يمكننا كتابتها كالآتي: system_info() { # Find any release files in /etc if ls /etc/*release 1>/dev/null 2>&1; then echo "<h2>System release info</h2>" echo "<pre>" for i in /etc/*release; do # Since we can't be sure of the # length of the file, only # display the first line. head -n 1 $i done uname -orp echo "</pre>" fi } # end of system_info حدَّدنا في بادئ الأمر إن كانت هنالك ملفات release لكي نعالجها. تحتوي ملفات release على اسم التوزيعة وإصدارها. وهي موجودة في مجلد /etc. ولكي نكتشف وجودها، سنستعمل الأمر ls لكننا لسنا مهتمين بمخرجات الأمر، وإنما بحالة الخروج، التي ستكون مساوية للصفر (true) إن وُجِدَت أيّة ملفات. الخطوة الآتية هي طباعة شيفرة HTML لهذا القسم من الصفحة؛ ولمّا كنا نعلم أنَّ هنالك عدِّة ملفات release لكي نعالجها، فسنستخدم حلقة for للمرور على كلٍ واحدٍ منها. ثم سنستعمل الأمر head في داخل الحلقة للحصول على السطر الأول من كل ملف. في النهاية، استعملنا الأمر uname مع الخيارات o و r و p للحصول على بعض المعلومات الإضافية حول النظام. ترجمة -وبتصرّف- للمقال Flow Control - Part 3 لصاحبه William Shotts.
-
سنتعلم في هذا الدرس طريقة رسم أسهم في حلقة دائرية الشكل عن طريق برنامج إنكسكيب. في البداية سنزيل الإطار الموجود في الصفحة عن طريق: File > Document properties ثم من النافذة المنبثقة نزيل علامة الصح بجانب عبارة: Show page border. نرسم دائرة مع الضغط على Ctrl أثناء الرسم لتكون متساوية الأبعاد. نقلل شفافية الشكل Opacity إلى المنتصف أو قريبا منها. نزيل التعبئة من لوحة التعبئة والحدود Fill and stoke من تبويب التعبئة Fill. نضيف حدودا للدائرة من تبويب رسم الحدود Stroke paint من نفس اللوحة، ونتأكد من عدم تفعيل خيار تكبير الحدود عند تكبير الشكل من أعلى الشريط؛ حتى لا يتغير حجم الحد عند تكبير الدائرة. نتجه لتبوب سمة الحد Stroke style ونجعل قيمة العرض Width = 70. سنعدل في عرض وطول الدائرة من الشريط العلوي بعد التأكد من اختيار علامة القفل الموجودة بين الطول والعرض، للمحافظة على النسبة بين الطول والعرض، ونضع قيمته 400. لتحويل الحد إلى مسار سنختار من قائمة المسار Path الخيار Stroke to path. سنرسم مربعا مع الضغط على Ctrl أثناء الرسم للمحافظة على تساوي الطول والعرض للمربع. سنزيل حدود المربع ونلونه باللون الأحمر، ونقلل شفافية للمنتصف. من الشريط العلوي سنجعل عرضه 200 مع التأكد من تفعيل رمز القفل. نحدد المربع ثم نحدد الحلقة بالضغط على Shift لتحديدهما معا، ثم من لوحة المحاذاة والاصطفاف Align and distribute نحاذي الشكلين من جهة الحافة اليسرى، ثم من نفس اللوحة نحاذيهما من جهة الحافة العلوية. سنختار المربع ثم من قائمة Path نختار Object to path لتحويل الشكل لمسار. نكرر المربع عن طريق تحديده ومن لوحة المفاتيح نضغط Ctrl+D، ثم بعد ذلك نحدد المربع المكرر ثم نحدد الحلقة، ومن نافذة المحاذاة نحاذيهما من جهة الحافة السفلى. نختار الحلقة مع المربع الأول الذي الأصل، ثم من قائمة Path نختار Intersection. نكرر المربع المتبقي مجددا، ونزيحه جانبا ثم نقوم بتدويره بالضغط عليه مجددا لتظهر أسهم التدوير مع الضغط على Ctrl أثناء التدوير إلى أن يعطينا شكل المعين. نختار أداة التحرير Node tool ونحذف العقدة السفلية باختيارها ثم من لوحة المفاتيح نضغط زر الحذف. نفعل خيار الالتقاط والكبس Enable snapping من الشريط العلوي، ومنه نفعل Snap cusp nodes ثم نسحب أذرع العقدة Node handles إلى العقدة نفسها وسنلاحظ أن العقدة ستلتقط الذراع، نفعل ذلك مع العقدتين السفليتين ثم نلغي خيار الالتقاط حتى نكمل الرسم بحرية. وبهذا نحصل على شكل المثلث. ملاحظة: يمكن اختصار هذه الخطوات عن طريق أداة النجمة والمضلع Stars and polygons باختيار المضلع وتحديد عدد الزوايا 3 . قم بتدوير المثلث باتجاه عقارب الساعة مع الضغط على Ctrl أثناء التدوير أو اضغط على خيار التدوير من الشريط العلوي. كرر الجزء الأسود المتبقي من الحلقة ثم ارسم في حافته العلوية مستطيلا يتقاطع مع جزء من الحافة. ثم اختر الجزء المكرر من الحلقة مع المستطيل، واختر الأمر Intersection من قائمة Path. اختر المثلث مع المستطيل الصغير في حافة الحلقة الذي حصلنا عليه توًا، ومن لوحة المحاذاة والاصطفاف نحاذيهما أفقيا ثم نختار من نفس اللوحة خيار محاذاة الحافة اليسرى بالحافة اليمنى لتلتصق حافة المثلث اليسرى بحافة المستطيل الصغير اليمنى. نصغر المثلث من السهم الأيمن المتوسط مع الضغط على Ctrl أثناء التصغير. نحذف المستطيل الصغير في حافة السهم لعدم حاجتنا له بعد الآن، ثم نختار المثلث مع جزء الحلقة المتبقي ومن قائمة Path نختار Union لنجعلهما شكلا واحدا. نعيد تفعيل خيارات الالتقاط والكبس التي فعلناها سابقا ونفعل معها خيار Snap other point، ثم نضغط على السهم المستدير مرة أخرى لتظهر أسهم التدوير وليظهر لنا مركز الشكل على شكل علامة +. نسحب المركز من وسط الشكل إلى زاوية المربع العلوية اليمنى، ولكوننا فعلنا خيار الالتقاط والكبس فستلتقطه زاوية المربع. نكرر السهم ثم نقوم بتدويره مع الضغط على Ctrl أثناء التدوير، بحيث يكون طرف السهم المكرر عند قاعدة المثلث. نكرر السهم مجددا لمرتين وندورها بحيث تتكون عندنا حلقة الأسهم، ونحذف المربع لعدم حاجتنا له. نكرر السهم الأول ونحدد السهم الذي يليه باتجاه عقارب الساعة ثم من قائمة Path نختار Difference. نفعل الشيء نفسه مع بقية الأسهم بحيث نكرر كل سهم في الحلقة ونحدده مع الذي يليه ونختار الأمر Difference. نحدد جميع الأسهم ثم نعيد الشفافية 100 كما كانت، ثم من الشريط السفلي نلون الأسهم باللون المناسب، وبهذا نكون أنهينا حلقة الأسهم الدائرية، ولمزيد من التفاصيل نتابع بقية الخطوات. من تبويب التعبئة سنختار التدرج الخطي Linear gradient ثم من نفس التبويب نختار زر التحرير Edit في أسفل اللوحة. سيظهر لنا خطا مستقيما في طرفيه عقدتين عبارة عن لوني التدرج، نختار العقدة اليمنى ومن لوحة التعبئة عند الحرف A نحرك المؤشر إلى أقصى اليمين لنزيل شفافية اللون. نعطيه لونا قاتما ونحرك عقدة اللون القاتم عند طرف انطلاق السهم ونحرك عقدة اللون الفاتح عند رأس السهم، ونكرر الخطوات نفسها لبقية الأسهم. لإضافة فكرة أخرى للأسهم، سنقوم بتكرارها وتلوينها جميعها باللون الأسود وجعل شفافيتها بنصف القيمة أو مقاربة لها. نكرر السهم الأول، ثم من لوحة المفاتيح نضغط على Ctrl+0 ثم نضغط على Shift ونحدد السهم الذي يليه باتجاه عقارب الساعة، ثم من قائمة Path نختار Difference. نتجه للسهم الثاني ثم نكرره ونفعل به ما فعلناه بالأول، ثم بتحديده هو والسهم الذي يليه نتبع الأمر Difference ونفعل ذلك مع السهم الثالث والرابع. نعيد شفافية الأسهم 100 وبذلك نصل للنتيجة النهائية: الدرس مستفاد من مقطع فيديو بعنوان: Inkscape Tutorial: Arrow Circle لصاحبه Nick Saporito.