غالبًا ما نحتاج إلى تكرار الإجراءات.
على سبيل المثال، إخراج البضائع من القائمة واحدة تلو الأخرى أو مجرد تشغيل نفس الكود لكل رقم من 1 إلى 10.
الحلقات هي وسيلة لتكرار نفس الكود عدة مرات.
ل...من و...في الحلقات
إعلان صغير للقراء المتقدمين.
تتناول هذه المقالة الحلقات الأساسية فقط: while
و do..while
و for(..;..;..)
.
إذا أتيت إلى هذه المقالة بحثًا عن أنواع أخرى من الحلقات، فإليك المؤشرات:
راجع... للتكرار فوق خصائص الكائن.
انظر إلى…of والتكرارات للتكرار فوق المصفوفات والكائنات القابلة للتكرار.
خلاف ذلك، يرجى قراءة.
تحتوي الحلقة while
على بناء الجملة التالي:
بينما (الشرط) { // شفرة // ما يسمى بـ "جسم الحلقة" }
في حين أن condition
صحيح، يتم تنفيذ code
من نص الحلقة.
على سبيل المثال، الحلقة أدناه تخرج i
while i < 3
:
دعني = 0؛ بينما (i < 3) {// يظهر 0، ثم 1، ثم 2 تنبيه (ط)؛ أنا++; }
يسمى التنفيذ الفردي لجسم الحلقة بالتكرار . تقوم الحلقة في المثال أعلاه بإجراء ثلاث تكرارات.
إذا كان i++
مفقودًا من المثال أعلاه، فستتكرر الحلقة (نظريًا) إلى الأبد. من الناحية العملية، يوفر المتصفح طرقًا لإيقاف مثل هذه الحلقات، وفي JavaScript من جانب الخادم، يمكننا إيقاف العملية.
يمكن لأي تعبير أو متغير أن يكون شرط حلقة، وليس مجرد مقارنات: يتم تقييم الشرط وتحويله إلى قيمة منطقية بواسطة while
.
على سبيل المثال، الطريقة الأقصر للكتابة while (i != 0)
هي while (i)
:
دعني = 3؛ while (i) { // عندما تصبح i 0، يصبح الشرط خاطئًا، وتتوقف الحلقة تنبيه (ط)؛ أنا--؛ }
الأقواس المتعرجة ليست مطلوبة لجسم ذو سطر واحد
إذا كان جسم الحلقة يحتوي على عبارة واحدة، يمكننا حذف الأقواس المتعرجة {…}
:
دعني = 3؛ بينما (i) تنبيه(i--);
يمكن نقل التحقق من الحالة أسفل نص الحلقة باستخدام بناء الجملة do..while
:
يفعل { // جسم الحلقة } بينما (الحالة)؛
ستقوم الحلقة أولاً بتنفيذ النص، ثم التحقق من الحالة، وبينما تكون صحيحة، قم بتنفيذها مرارًا وتكرارًا.
على سبيل المثال:
دعني = 0؛ يفعل { تنبيه (ط)؛ أنا++; } بينما (i < 3);
يجب استخدام هذا النوع من بناء الجملة فقط عندما تريد تنفيذ نص الحلقة مرة واحدة على الأقل بغض النظر عن كون الشرط صحيحًا. عادةً ما يُفضل النموذج الآخر: while(…) {…}
.
تعتبر حلقة for
أكثر تعقيدًا، ولكنها أيضًا الحلقة الأكثر استخدامًا.
يبدو مثل هذا:
لـ (البدء؛ الشرط؛ الخطوة) { // ... جسم الحلقة ... }
دعونا نتعلم معنى هذه الأجزاء بالمثال. تعمل الحلقة أدناه على تشغيل alert(i)
لـ i
من 0
إلى (ولكن ليس بما في ذلك) 3
:
لـ (دع i = 0؛ i < 3؛ i++) {// يظهر 0، ثم 1، ثم 2 تنبيه (ط)؛ }
دعونا نتفحص العبارة for
جزءًا بجزء:
جزء | ||
---|---|---|
يبدأ | let i = 0 | ينفذ مرة واحدة عند الدخول إلى الحلقة. |
حالة | i < 3 | تم التحقق منه قبل كل تكرار للحلقة. إذا كاذبة، تتوقف الحلقة. |
جسم | alert(i) | يتم تشغيله مرارًا وتكرارًا بينما يكون الشرط صحيحًا. |
خطوة | i++ | يتم تنفيذه بعد النص في كل تكرار. |
تعمل خوارزمية الحلقة العامة على النحو التالي:
تشغيل تبدأ → (إذا كان الشرط → تشغيل الجسم وتشغيل الخطوة) → (إذا كان الشرط → تشغيل الجسم وتشغيل الخطوة) → (إذا كان الشرط → تشغيل الجسم وتشغيل الخطوة) → ...
وهذا يعني أن begin
يتم تنفيذه مرة واحدة، ثم يتكرر: بعد كل اختبار condition
، يتم تنفيذ body
step
.
إذا كنت جديدًا في استخدام الحلقات، فقد يكون من المفيد العودة إلى المثال وإعادة إنتاج كيفية تشغيله خطوة بخطوة على قطعة من الورق.
إليك بالضبط ما يحدث في حالتنا:
// for (let i = 0; i < 3; i++) تنبيه (i) // يبدأ التشغيل دعني = 0 // إذا كان الشرط → تشغيل الجسم وتشغيل الخطوة إذا (i < 3) { تنبيه (i)؛ ط++ } // إذا كان الشرط → تشغيل الجسم وتشغيل الخطوة إذا (i < 3) { تنبيه (i)؛ ط++ } // إذا كان الشرط → تشغيل الجسم وتشغيل الخطوة إذا (i < 3) { تنبيه (i)؛ ط++ } // ... انتهى، لأنني الآن == 3
إعلان متغير مضمن
هنا، يتم الإعلان i
المتغير "counter" في الحلقة مباشرةً. وهذا ما يسمى إعلان متغير "مضمن". هذه المتغيرات تكون مرئية فقط داخل الحلقة.
لـ (دع i = 0; i < 3; i++) { تنبيه (ط)؛ // 0، 1، 2 } تنبيه (ط)؛ // خطأ، لا يوجد مثل هذا المتغير
بدلًا من تعريف متغير، يمكننا استخدام متغير موجود:
دعني = 0؛ for (i = 0; i < 3; i++) {// استخدم متغيرًا موجودًا تنبيه (ط)؛ // 0، 1، 2 } تنبيه (ط)؛ // 3، مرئي، لأنه تم الإعلان عنه خارج الحلقة
يمكن تخطي أي جزء من for
.
على سبيل المثال، يمكننا حذف begin
إذا لم نكن بحاجة إلى القيام بأي شيء عند بداية الحلقة.
مثل هنا:
دعني = 0؛ // لقد أعلنا وتعييننا بالفعل for (; i < 3; i++) {// لا حاجة لـ "البدء" تنبيه (ط)؛ // 0، 1، 2 }
يمكننا أيضًا إزالة جزء step
:
دعني = 0؛ من أجل (؛ أنا <3؛) { تنبيه (i++)؛ }
وهذا يجعل الحلقة مماثلة لـ while (i < 3)
.
يمكننا في الواقع إزالة كل شيء، وإنشاء حلقة لا نهائية:
ل (؛؛) { // يتكرر بلا حدود }
يرجى ملاحظة أن اثنين for
الفواصل المنقوطة ;
يجب أن يكون حاضرا. وإلا فسيكون هناك خطأ في بناء الجملة.
عادة، تخرج الحلقة عندما تصبح حالتها خاطئة.
ولكن يمكننا فرض الخروج في أي وقت باستخدام توجيه break
الخاص.
على سبيل المثال، الحلقة أدناه تطلب من المستخدم سلسلة من الأرقام، "متقطعة" عندما لا يتم إدخال أي رقم:
دع المبلغ = 0؛ بينما (صحيح) { Let value = +prompt("أدخل رقمًا"، ''); إذا (! القيمة) استراحة؛ // (*) مجموع += القيمة؛ } تنبيه('المجموع:' + المجموع);
يتم تنشيط توجيه break
عند السطر (*)
إذا قام المستخدم بإدخال سطر فارغ أو قام بإلغاء الإدخال. يقوم بإيقاف الحلقة على الفور، ويمرر التحكم إلى السطر الأول بعد الحلقة. وهي alert
.
يعد الجمع بين "الحلقة اللانهائية + break
حسب الحاجة" أمرًا رائعًا في المواقف التي يجب فيها التحقق من حالة الحلقة ليس في بداية الحلقة أو نهايتها، ولكن في المنتصف أو حتى في عدة أماكن من جسمها.
التوجيه continue
هو "إصدار أخف" من break
. لا يوقف الحلقة بأكملها. بدلاً من ذلك، يقوم بإيقاف التكرار الحالي ويجبر الحلقة على بدء حلقة جديدة (إذا كانت الحالة تسمح بذلك).
يمكننا استخدامه إذا انتهينا من التكرار الحالي وأردنا الانتقال إلى الإصدار التالي.
تستخدم الحلقة أدناه continue
في إخراج القيم الفردية فقط:
لـ (دع i = 0; i < 10; i++) { // إذا كان هذا صحيحا، تخطي الجزء المتبقي من الجسم إذا (i % 2 == 0) تابع؛ تنبيه (ط)؛ // 1، ثم 3، 5، 7، 9 }
بالنسبة للقيم الزوجية لـ i
، يتوقف توجيه continue
عن تنفيذ النص ويمرر التحكم إلى التكرار التالي لـ for
(مع الرقم التالي). لذلك يتم استدعاء alert
فقط للقيم الفردية.
يساعد التوجيه continue
على تقليل التداخل
يمكن أن تبدو الحلقة التي تعرض القيم الفردية كما يلي:
لـ (دع i = 0; i < 10; i++) { إذا (ط % 2) { تنبيه (ط)؛ } }
من الناحية الفنية، هذا مطابق للمثال أعلاه. بالتأكيد، يمكننا فقط لف الكود في كتلة if
بدلاً من استخدام continue
.
ولكن كأثر جانبي، أدى ذلك إلى إنشاء مستوى آخر من التداخل (استدعاء alert
داخل الأقواس المتعرجة). إذا كان الكود الموجود داخل if
أطول من بضعة أسطر، فقد يؤدي ذلك إلى تقليل إمكانية القراءة بشكل عام.
لا يوجد break/continue
إلى الجانب الأيمن من '؟"
يرجى ملاحظة أنه لا يمكن استخدام بنيات بناء الجملة التي ليست تعبيرات مع العامل الثلاثي ?
. على وجه الخصوص، لا يُسمح بالتوجيهات مثل break/continue
هناك.
على سبيل المثال، إذا أخذنا هذا الكود:
إذا (ط > 5) { تنبيه (ط)؛ } آخر { يكمل؛ }
… وأعد كتابتها باستخدام علامة الاستفهام:
(ط > 5) ؟ تنبيه (ط): متابعة؛ // الاستمرار غير مسموح به هنا
…يتوقف عن العمل: هناك خطأ في بناء الجملة.
هذا مجرد سبب آخر لعدم استخدام عامل علامة الاستفهام ?
بدلاً من if
.
في بعض الأحيان نحتاج إلى الخروج من حلقات متداخلة متعددة في وقت واحد.
على سبيل المثال، في الكود أدناه نقوم بالتكرار عبر i
و j
، ونطلب الإحداثيات (i, j)
من (0,0)
إلى (2,2)
:
لـ (دع i = 0; i < 3; i++) { لـ (دع j = 0; j < 3; j++) { Let input = موجه(`القيمة في الإحداثيات (${i},${j})`, ''); // ماذا لو أردنا الخروج من هنا إلى "تم" (أدناه)؟ } } تنبيه ("تم!")؛
نحتاج إلى طريقة لإيقاف العملية إذا قام المستخدم بإلغاء الإدخال.
break
العادي بعد input
لن يؤدي إلا إلى كسر الحلقة الداخلية. هذا ليس كافيًا - العلامات، هيا للإنقاذ!
التسمية عبارة عن معرف بنقطتين قبل الحلقة:
اسم التسمية: لـ (...) { ... }
تنقسم عبارة break <labelName>
في الحلقة أدناه إلى التسمية:
الخارجي: for (let i = 0; i < 3; i++) { لـ (دع j = 0; j < 3; j++) { Let input = موجه(`القيمة في الإحداثيات (${i},${j})`, ''); // إذا كانت السلسلة فارغة أو تم إلغاؤها، فقم بالخروج من كلتا الحلقتين إذا (! الإدخال) كسر الخارجي؛ // (*) // افعل شيئًا بقيمة ... } } تنبيه ("تم!")؛
في الكود أعلاه، يتطلع break outer
إلى الأعلى بحثًا عن الملصق المسمى outer
ويخرج من تلك الحلقة.
لذلك ينتقل عنصر التحكم مباشرة من (*)
إلى alert('Done!')
.
يمكننا أيضًا نقل الملصق إلى سطر منفصل:
الخارجي: لـ (دع i = 0; i < 3; i++) { ... }
يمكن أيضًا استخدام توجيه continue
مع الملصق. في هذه الحالة، ينتقل تنفيذ التعليمات البرمجية إلى التكرار التالي للحلقة المسماة.
التسميات لا تسمح "بالقفز" في أي مكان
لا تسمح لنا التصنيفات بالانتقال إلى مكان عشوائي في الكود.
على سبيل المثال، من المستحيل القيام بذلك:
كسر التسمية؛ // انتقل إلى الملصق أدناه (لا يعمل) الوسم: ل (...)
يجب أن يكون توجيه break
داخل كتلة التعليمات البرمجية. من الناحية الفنية، فإن أي كتلة تعليمات برمجية مُسمى ستفي بالغرض، على سبيل المثال:
ملصق: { // ... كسر التسمية؛ // يعمل // ... }
…على الرغم من أنه يتم استخدام 99.9% من break
الزمني داخل الحلقات، كما رأينا في الأمثلة أعلاه.
continue
ممكنة فقط من داخل الحلقة.
قمنا بتغطية 3 أنواع من الحلقات:
while
- يتم التحقق من الحالة قبل كل تكرار.
do..while
– يتم التحقق من الحالة بعد كل تكرار.
for (;;)
- يتم التحقق من الحالة قبل كل تكرار، وتتوفر إعدادات إضافية.
لإنشاء حلقة "لا نهائية"، عادةً ما يتم استخدام البنية while(true)
. يمكن إيقاف مثل هذه الحلقة، تمامًا مثل أي حلقة أخرى، باستخدام أمر break
.
إذا لم نرغب في القيام بأي شيء في التكرار الحالي ونرغب في الانتقال إلى التكرار التالي، فيمكننا استخدام التوجيه continue
.
break/continue
تسميات الدعم قبل الحلقة. التسمية هي الطريقة الوحيدة break/continue
في الهروب من حلقة متداخلة للانتقال إلى حلقة خارجية.
الأهمية: 3
ما هي القيمة الأخيرة التي تم التنبيه عليها بواسطة هذا الكود؟ لماذا؟
دعني = 3؛ بينما (ط) { تنبيه(i--); }
الجواب : 1
.
دعني = 3؛ بينما (ط) { تنبيه(i--); }
كل تكرار للحلقة يقلل i
بمقدار 1
. يقوم التحقق while(i)
بإيقاف الحلقة عندما i = 0
.
وبالتالي، تشكل خطوات الحلقة التسلسل التالي ("الحلقة غير الملفوفة"):
دعني = 3؛ تنبيه(ط--); // يظهر 3، ويقلل i إلى 2 تنبيه (i--) // يظهر 2، ويقلل i إلى 1 تنبيه (i--) // يظهر 1، ويقلل i إلى 0 // تم، أثناء التحقق (i) يوقف الحلقة
الأهمية: 4
في كل تكرار للحلقة، اكتب القيمة التي تنتجها ثم قارنها بالحل.
كلا الحلقتين alert
نفس القيم، أم لا؟
شكل البادئة ++i
:
دعني = 0؛ بينما (++i < 5) تنبيه(i);
نموذج postfix i++
دعني = 0؛ بينما (i++ < 5) تنبيه(i);
توضح المهمة كيف يمكن أن تؤدي نماذج postfix/prefix إلى نتائج مختلفة عند استخدامها في المقارنات.
من 1 إلى 4
دعني = 0؛ بينما (++i < 5) تنبيه(i);
القيمة الأولى هي i = 1
، لأن ++i
يزيد أولاً i
ثم يُرجع القيمة الجديدة. فالمقارنة الأولى هي 1 < 5
alert
يظهر 1
.
ثم اتبع 2, 3, 4…
- تظهر القيم واحدة تلو الأخرى. تستخدم المقارنة دائمًا القيمة المتزايدة، لأن ++
موجود قبل المتغير.
أخيرًا، تتم زيادة i = 4
إلى 5
، وتفشل المقارنة while(5 < 5)
، وتتوقف الحلقة. لذلك لم يتم عرض 5
.
من 1 إلى 5
دعني = 0؛ بينما (i++ < 5) تنبيه(i);
القيمة الأولى هي مرة أخرى i = 1
. صيغة postfix الخاصة بـ i++
تزيد i
ثم تُرجع القيمة القديمة ، وبالتالي فإن المقارنة i++ < 5
ستستخدم i = 0
(على عكس ++i < 5
).
لكن نداء alert
منفصل. إنها عبارة أخرى يتم تنفيذها بعد الزيادة والمقارنة. وبذلك تحصل على التيار i = 1
.
ثم اتبع 2, 3, 4…
دعونا نتوقف عند i = 4
. نموذج البادئة ++i
سيزيده ويستخدم 5
في المقارنة. ولكن هنا لدينا نموذج postfix i++
. لذا فهو يزيد i
إلى 5
، لكنه يُرجع القيمة القديمة. ومن ثم فإن المقارنة في الواقع while(4 < 5)
– صحيحة، ويستمر التحكم في alert
.
القيمة i = 5
هي القيمة الأخيرة، لأنه في الخطوة التالية تكون القيمة while(5 < 5)
خاطئة.
الأهمية: 4
لكل حلقة، اكتب القيم التي ستعرضها. ثم قارن مع الجواب.
كلتا الحلقتين alert
نفس القيم أم لا؟
نموذج ما بعد الإصلاح:
for (let i = 0; i < 5; i++) تنبيه(i);
نموذج البادئة:
for (let i = 0; i < 5; ++i) تنبيه( i );
الجواب: من 0
إلى 4
في كلتا الحالتين.
for (let i = 0; i < 5; ++i) تنبيه( i ); for (let i = 0; i < 5; i++) تنبيه(i);
يمكن خصم ذلك بسهولة من خوارزمية for
:
نفذ مرة واحدة i = 0
قبل كل شيء (البدء).
تحقق من الشرط i < 5
إذا كان true
- قم بتنفيذ alert(i)
، ثم i++
يتم فصل الزيادة i++
عن فحص الحالة (2). هذا مجرد بيان آخر.
القيمة التي تُرجعها الزيادة غير مستخدمة هنا، لذا لا يوجد فرق بين i++
و ++i
.
الأهمية: 5
استخدم حلقة for
لإخراج أرقام زوجية من 2
إلى 10
.
قم بتشغيل العرض التوضيحي
من أجل (دع i = 2؛ i <= 10؛ i++) { إذا (ط % 2 == 0) { تنبيه (ط)؛ } }
نستخدم عامل التشغيل "modulo" %
للحصول على الباقي والتحقق من التساوي هنا.
الأهمية: 5
أعد كتابة الكود مع تغيير الحلقة for
إلى while
دون تغيير سلوكها (يجب أن يظل الإخراج كما هو).
لـ (دع i = 0; i < 3; i++) { تنبيه( `الرقم ${i}!` ); }
دعني = 0؛ بينما (ط < 3) { تنبيه( `الرقم ${i}!` ); أنا++; }
الأهمية: 5
اكتب حلقة تطلب رقمًا أكبر من 100
. إذا قام الزائر بإدخال رقم آخر – اطلب منه الإدخال مرة أخرى.
يجب أن تطلب الحلقة رقمًا حتى يُدخل الزائر رقمًا أكبر من 100
أو يلغي الإدخال/يدخل سطرًا فارغًا.
هنا يمكننا أن نفترض أن الزائر يقوم بإدخال الأرقام فقط. ليست هناك حاجة لتنفيذ معالجة خاصة للمدخلات غير الرقمية في هذه المهمة.
قم بتشغيل العرض التوضيحي
اسمحوا الأسطوانات؛ يفعل { num = موجه("أدخل رقمًا أكبر من 100؟", 0); } while (num <= 100 && num);
الحلقة do..while
.. while تتكرر بينما يكون كلا التحققين صادقين:
التحقق من num <= 100
– أي أن القيمة المدخلة لا تزال ليست أكبر من 100
.
يكون الاختيار && num
خطأ عندما يكون num
null
أو سلسلة فارغة. ثم تتوقف حلقة while
أيضًا.
ملاحظة: إذا كانت num
null
، فإن num <= 100
تكون true
، لذلك بدون التحقق الثاني لن تتوقف الحلقة إذا قام المستخدم بالنقر فوق "إلغاء الأمر". كلا الشيكات مطلوبة.
الأهمية: 3
يسمى العدد الصحيح الأكبر من 1
عددًا أوليًا إذا كان لا يمكن قسمته بدون باقي على أي شيء باستثناء 1
ونفسه.
بمعنى آخر، n > 1
هو عدد أولي إذا كان لا يمكن تقسيمه بالتساوي على أي شيء باستثناء 1
و n
.
على سبيل المثال، 5
هو عدد أولي، لأنه لا يمكن قسمته بدون باقي على 2
و 3
و 4
.
اكتب الكود الذي يخرج الأعداد الأولية في الفترة من 2
إلى n
.
بالنسبة لـ n = 10
ستكون النتيجة 2,3,5,7
.
ملاحظة: يجب أن يعمل الرمز مع أي n
، ولا يتم ضبطه بشدة لأي قيمة ثابتة.
هناك العديد من الخوارزميات لهذه المهمة.
دعونا نستخدم حلقة متداخلة:
لكل i في الفترة { تحقق مما إذا كان لدي مقسوم على 1..i إذا كانت الإجابة بنعم => فإن القيمة ليست أولية إذا كانت الإجابة لا => القيمة أولية، قم بإظهارها }
الكود باستخدام التسمية:
دع ن = 10؛ نكست برايم: for (let i = 2; i <= n; i++) { // لكل i... for (let j = 2; j < i; j++) { // ابحث عن المقسوم عليه.. إذا (i % j == 0) تابع nextPrime؛ // ليس عددًا أوليًا، انتقل بعد ذلك i } تنبيه (ط)؛ // رئيس الوزراء }
هناك مساحة كبيرة لتحسينها. على سبيل المثال، يمكننا البحث عن المقسومات من 2
إلى الجذر التربيعي لـ i
. ولكن على أي حال، إذا أردنا أن نكون فعالين حقًا لفترات طويلة، فنحن بحاجة إلى تغيير النهج والاعتماد على الرياضيات المتقدمة والخوارزميات المعقدة مثل المنخل التربيعي، ومنخل حقل الأرقام العامة وما إلى ذلك.