لوحة المتصدرين
المحتوى الأكثر حصولًا على سمعة جيدة
المحتوى الأعلى تقييمًا في 07/13/19 في كل الموقع
-
تعتبر السيرة الذاتية من أهم الأدوات عند التقدم للحصول على وظيفة ولذلك يجب أن تكون جيدة الصياغة وتعرض مؤهلاتك بصورة ذكية. يتيح مايكروسوفت وورد إمكانية إنشاء سيرة ذاتية باستخدام القوالب الجاهزة أو إنشائها يدويا من الصفر. ولمعرفة كيفية إنشاء سيرتك الذاتية بإحدى الطريقتين، بالإضافة إلى بعض الأدوات والنصائح لتحسينها تابع معنا هذا الدرس. إنشاء السيرة الذاتية باستخدام القوالب بالإمكان الوصول إلى قوالب السيرة الذاتية مباشرة من نافذة بداية وورد: في حقل البحث سنكتب Resume ونضغط Enter لعرض القوالب الخاصة بالسير الذاتية. وهذه القوالب مصنفة بفئات مختلفة. أن كنت تستخدم إصدارات أقدم من وورد 2013 يمكنك الوصول إلى هذه القوالب من: ملف File > جديد New ثم البحث في حقل البحث الخاص بالقوالب. حقوق الصورة ننقر على أحد المصغرات لعرض معاينة للقالب، ونستخدم الأسهم للتنقل بين القوالب. عندما نختار المناسب ننقر إنشاء Create: بعد تنزيل القالب وفتحه في وورد سنبدأ بتحرير القالب وكتابة المعلومات الشخصية، الهدف المهني، المهارات، الخبرات، إلخ. يحتوي القالب على إرشادات بسيطة تخبرنا ما الذي يجب كتابته في كل حقل: بإمكاننا نسخ ولصق النصوص من نسخة قديمة من السيرة الذاتية إن وجدت. عند لصق النصوص يمكننا تنسيقها من خانة الأنماط لتتلاءم مع تنسيقات القالب. أو استخدام خيارات اللصق عن طريق زر Ctrl. نستخدم الأمر Keep Text Only إذا كنا نرغب في اللصق دون تنسيق: أو نستخدم خيار Merge Formatting لدمج التنسيقات، أي جعل حجم ونوع الخط متناسقا مع القالب، والاحتفاظ بالتنسيقات الأخرى للنص الأصلي كالنصوص العريضة والمائلة، إلخ: يجب أن نأخذ في الاعتبار عند إنشاء السيرة الذاتية أن نكون محددين جدا في فقرة الهدف المهني Objective. إذ يجب أن يكون الهدف مختلفا باختلاف الشركة التي نقدم للحصول على وظيفة فيها. لا تستخدم نفس الهدف المهني لجميع الشركات. تحتوي القوالب، وخصوصا قوالب السير الذاتية، على حقول بتنسيقات معينة، وتحتوي على نصوص إرشادية، تسمى هذه الحقول عناصر التحكم بالمحتوى Content Controls. كمثال على هذه العناصر الحقل المؤشر في الصورة أدناه: لدينا خيار استخدام هذه العناصر أو إزالتها بالنقر بزر الفأرة الأيمن عليها واختيار Remove Content Control: يمكننا التعديل على تنسيق وتصميم القالب من تبويب تصميم Design. يمكننا اختيار التنسيق الذي يناسبنا من أحجام النصوص، الألوان أو المسافات بين الأسطر والفقرات، وغيرها، وذلك معرض تنسيقات المستند Document Formatting: أو من معرض نسق Themes: استخدام الجداول في إنشاء السيرة الذاتية يفضل البعض عدم استخدام القوالب لإنشاء السيرة الذاتية، ويعتمدون على مفتاح tab أو المسطرة في تنسيق المسافات بين عناوين الأقسام وما يقابلها. لكن هناك طريقة أسهل وأسرع وهي استخدام الجداول. ولفعل ذلك سنقوم بالخطوات التالية: سنقوم بفتح مستندًا جديدًا وكتابة المعلومات الشخصية في بداية الصفحة، ثم سنقوم بإدراج جدول Table من تبويب إدراج Insert: سنقوم بإدراج عمودين، أحدهما لعنوان القسم، والآخر لما يقابل تلك الأقسام من معلومات. أما عدد الصفوف فيعتمد على عدد الأقسام التي تريد إضافاتها، أي صف للهدف المهني، صف للخبرات، صف للمهارات، إلخ. بعدها سنقوم بكتابة المحتويات، وسنستخدم خلية واحدة لكل قسم، أي سنكتب جميع المهارات أو الخبرات السابقة في خلية واحدة من الجدول مهما كان عددها: الخطوة التالية بعد الانتهاء من الكتابة هي تنسيق الجدول. سنقوم بتقليل عرض العمود الأول لإزالة المساحة الزائدة والاستفادة منها في زيادة عرض العمود الثاني. سنضع المؤشر على الحد بين العمود إلى أن يتغير شكله إلى متجهين، ثم سنقوم بالسحب إلى جهة اليسار: ثم سنقوم بإضافة عمود وسطي كحاجز رفيع يفصل بين العمودين، بالنقر على علامة (+) التي تظهر عن وضع المؤشر على الحد بين العمودين وهي طريقة سريعة لإدراج أعمدة (أو صفوف) في إصدار 2013 من البرنامج: أو بالنقر داخل أحد خلايا العمود الأول، والتأشير فوق إدراج Insert واختيار إدراج عمود إلى اليمين Insert Column to Right: وسنقوم بتقليل عرض هذا العمود لأننا لا نريد من الحاجز أن يكون كبيرًا جدًا: بالتأكيد لا نريد إظهار حدود الجدول في السيرة الذاتية لذلك سنقوم بإزالتها. سنحدد جميع خلايا الجدول أولا من أيقونة (+) في حافة الجدول: ثم سنذهب إلى تبويب تصميم Design السياقي الخاص بالجدول، ونختار بلا حدود No Borders من أمر حدود Borders: ما زال بإمكاننا تعديل أبعاد الجدول حتى بعد إزالة الحدود. يمكننا استخدام خطوط الشبكة Gridlines. هذه الخطوط لا تظهر عند الطباعة لكنها تساعدنا على معرفة أماكن حدود أعمدة وصفوف الجدول. سنقوم بتحديد الجدول أولا من أيقونة (+) كما في الخطوة السابقة، ومن أمر حدود سنختار عرض خطوط الشبكة View Gridlines: انتهينا تقريبا من إنشاء السيرة الذاتية. ما تبقى هو تنسيق النصوص وأحجامها، أو إضافة عناصر تصميمية في رأس Header الورقة. نصائح وأدوات لتحسين السيرة الذاتية اليوم أصبح أغلب الباحثين عن عمل يقدمون سيرهم الذاتية عبر الإنترنت. وتخضع تلك السير الذاتية للبحث عن كلمات معينة بواسطة أنظمة تتبع تستخدم من قبل أصحاب العمل. الأمر أشبه بتهيئة محركات البحث SEO، إذ يجب أن تتضمن سيرتك الذاتية الكلمات التي تجعل العثور عليها سهلا عند البحث. تسمى هذه الكلمات بالكلمات المفتاحية Keywords أو الكلمات الفعالة Power Words. تحتاج إلى سيرة ذاتية احترافية؟ استعن بأفضل خبراء التوظيف في الوطن العربي لمساعدتك على إنشاء سيرة ذاتية احترافية بتصميم عصري وجذاب أنشئ سيرتك الذاتية الآن تختلف هذه الكلمات باختلاف الوظيفة التي تُقدم إليها ويجب عليك أن تذكر الكلمات المناسبة ذات الصلة بالعمل الذي تبحث عنه والتي تجذب صاحب العمل. ابحث عن الكلمات المستخدمة في الوصف الوظيفي وضمنها في سيرتك الذاتية. فإذا كانت الوظيفة تتطلب خبرة في برامج التصميم الجرافيكي من ضمنها Photoshop ،Illustrator ،After Effects ...إلخ، تحدث عن هذه البرامج ضمن خبراتك أو اذكرها ضمن مهاراتك، لكن بالطبع إذا كنت مؤهلا لذلك. وإذا كنت تقدم طلبا للعمل في شركة هواتف، تحدث عن الهواتف بالتحديد وليس الأجهزة الذكية بصورة عامة. كن محددا. من الأمور الأخرى التي يجب مراعاتها في السيرة الذاتية هو تجنب تكرار الكلمات. في بعض الأحيان تكرر الكثير من الكلمات دون أن تدرك ذلك. في المثال أدناه هناك العديد من الكلمات المكررة، "expert specialist"، "high experience"... ما سنفعله هو أننا سنقوم بالبحث عن مرادفات لتلك الكلمات واستبدالها. أولا سنقوم بوضع المؤشر فوق إحدى الكلمات، ثم ننقر بزر الفأرة الأيمن ونؤشر فوق مرادفات Synonyms، وسنختار واحدة من المرادفات المقترحة: وإذا لم نجد الكلمة التي تناسبنا سنبحث في Thesaurus الذي يوفر المزيد من المرادفات: وكذلك هناك ما يسمى بصفحة المقدمة أو الخطاب المرفق مع السيرة الذاتية Cover Letter. وهي عبارة عن صفحة تُرفق في بداية السيرة الذاتية توضح بشكل مختصر وسريع الهدف من الوظيفة والمؤهلات التي تجعلك مناسبا للوظيفة التي تقدم إليها. يجب أن تحرص على صياغة صفحة المقدمة بطريقة ذكية وجذابة، فقد تكون هي فرصتك للحصول على الوظيفة. وإذا لم تكتب صفحة مقدمة من قبل، أو لم تكن معتادا على كتابتها يمكنك استخدام قوالب وورد. إذ تتوفر عدة قوالب بالتنسيق الرسمي، يمكنك تنزيلها وتحريرها. من صفحة البداية وفي حقل البحث، اكتب resume cover letter واختر أحد القوالب. قم بتنزيل القالب وابدأ بتحريره حسب الإرشادات في حقول التحكم بالمحتوى. بهذا تكون قد تعرفت على كيفية إنشاء سيرتك الذاتية، والتي إن كنت منشغلًا عن إتمامها كما يجب، أو كنت بحاجة لواحدة ذات احترافية عالية، فيمكنك استعمال خدمة كتابة السير الذاتية المقدمة من منصة بعيد، التي تمنحك سيرةً ذاتيةً احترافيةً مقدمةً من طرف خبراء في التوظيف، وهذا بتصاميم مميزة تستهدف مجالات العمل المختلفة؛ إلى جانب بعض المزايا الإضافية التي ستفيدك أكثر لرفع حظوظك بالتوظيف، وسنوضح بعضها في الآتي: الحصول على استشارة فردية مع أحد خبراء التوظيف. الحصول على السيرة الذاتية بنسختين عربية وإنجليزية حسب الباقة التي ستختارها، وبالنموذج الذي تراه مناسبًا لك. إجراء مقابلة توظيف تجريبية يقودها أحد اخصائيي التوظيف، لتكون بمثابة تدريب لك ليوم مقابلة العمل الفعلية مع التطرق إلى أهم الأسئلة التي قد تُطرح عليك فعليًا، وبطبيعة الحال ستحصل على بعض التعقيبات والتوجيهات التي ستساهم في رفع جاهزيتك والتحضير لمقابلات العمل. الحصول على مساعدة لإعداد ملف مهني احترافي على بعيد وإعداد ملفاتك الشخصية على مستقل وخمسات للتعريف بخبراتك ومهاراتك الشخصية. وإن كنت بحاجة لفهم أوسع حول الأمور قبل حصولك على باقة من الباقات المتاحة، فيمكنك التواصل مع الدعم الفني للمنصة لإجابتك عن استفساراتك.1 نقطة
-
يسمح لك استخدام حلقات for أو while في بايثون بأتمتة وتكرار المهام بطريقة فعّالة. لكن في بعض الأحيان، قد يتدخل عامل خارجي في طريقة تشغيل برنامجك، وعندما يحدث ذلك، فربما تريد من برنامجك الخروج تمامًا من حلقة التكرار، أو تجاوز جزء من الحلقة قبل إكمال تنفيذها، أو تجاهل هذا العامل الخارجي تمامًا. لذا يمكنك فعل ما سبق باستخدام تعابير break و continue و pass. التعبير break يوفِّر لك التعبير break القدرة على الخروج من حلقة التكرار عند حدوث عامل خارجي. حيث عليك وضع التعبير break في الشيفرة التي ستُنفَّذ في كل تكرار للحلقة، ويوضع عادةً ضمن تعبير if. ألقِ نظرةً إلى أحد الأمثلة الذي يستعمل التعبير break داخل حلقة for: number = 0 for number in range(10): number = number + 1 if number == 5: break # break here print('Number is ' + str(number)) print('Out of loop') هذا برنامجٌ صغيرٌ، هيّأنا في بدايته المتغير number بجعله يساوي الصفر، ثم بنينا حلقة تكرار for التي تعمل لطالما كانت قيمة المتغير number أصغر من 10. ثم قمنا بزيادة قيمة المتغير number داخل حلقة for بمقدار 1 في كل تكرار، وذلك في السطر number = number + 1. ثم كانت هنالك عبارة if التي تختبر إن كان المتغير number مساوٍ للرقم 5، وعند حدوث ذلك فسيُنفَّذ التعبير break للخروج من الحلقة. وتوجد داخل حلقة التكرار الدالة print() التي تُنفَّذ في كل تكرار إلى أن نخرج من الحلقة عبر التعبير break، وذلك لأنَّها موجودة بعد التعبير break. لكي نتأكد أننا خرجنا من الحلقة، فوضعنا عبارة print() أخيرة موجودة خارج حلقة for. سنرى الناتج الآتي عند تنفيذ البرنامج: Number is 1 Number is 2 Number is 3 Number is 4 Out of loop الناتج السابق يُظهِر أنَّه بمجرد أن أصبح العدد الصحيح number مساويًا للرقم 5، فسينتهي تنفيذ حلقة التكرار عبر التعبير break. الخلاصة: التعبير break يؤدي إلى الخروج من حلقة التكرار. دورة تطوير التطبيقات باستخدام لغة Python احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة اشترك الآن التعبير continue التعبير continue يسمح لنا بتخطي جزء من حلقة التكرار عند حدوث عامل خارجي، لكن إكمال بقية الحلقة إلى نهايتها. بعبارةٍ أخرى: سينتقل تنفيذ البرنامج إلى أوّل حلقة التكرار عند تنفيذ التعبير continue. يجب وضع التعبير continue في الشيفرة التي ستُنفَّذ في كل تكرار للحلقة، ويوضع عادةً ضمن تعبير if. سنستخدم نفس البرنامج الذي استعملناها لشرح التعبير break أعلاه، لكننا سنستخدم التعبير continue بدلًا من break: number = 0 for number in range(10): number = number + 1 if number == 5: continue # continue here print('Number is ' + str(number)) print('Out of loop') الفرق بين استخدام التعبير continue بدلًا من break هو إكمال تنفيذ الشيفرة بغض النظر عن التوقف الذي حدث عندما كانت قيمة المتغير number مساويةً إلى الرقم 5. لننظر إلى الناتج: Number is 1 Number is 2 Number is 3 Number is 4 Number is 6 Number is 7 Number is 8 Number is 9 Number is 10 Out of loop نلاحظ أنَّ السطر الذي يجب أن يحتوي على Number is 5 ليس موجودًا في المخرجات، لكن سيُكمَل تنفيذ حلقة التكرار بعد هذه المرحلة مما يطبع الأرقام من 6 إلى 10 قبل إنهاء تنفيذ الحلقة. يمكنك استخدام التعبير continue لتفادي استخدام تعابير شرطية معقدة ومتشعّبة، أو لتحسين أداء البرنامج عن طريق تجاهل الحالات التي ستُرفَض نتائجها. الخلاصة: التعبير continue سيؤدي إلى جعل البرنامج يتجاهل تنفيذ حلقة التكرار عند تحقيق شرط معين، لكن بعدئذٍ سيُكمِل تنفيذ الحلقة كالمعتاد. التعبير pass التعبير pass يسمح لنا بالتعامل مع أحد الشروط دون إيقاف عمل حلقة التكرار بأي شكل، أي ستُنفَّذ جميع التعابير البرمجية الموجودة في حلقة التكرار ما لم تستعمل تعابير مثل break أو continue فيها. وكما هو الحال مع التعابير السابقة، يجب وضع التعبير pass في الشيفرة التي ستُنفَّذ في كل تكرار للحلقة، ويوضع عادةً ضمن تعبير if. سنستخدم نفس البرنامج الذي استعملناها لشرح التعبير break أو continue أعلاه، لكننا سنستخدم التعبير pass هذه المرة: number = 0 for number in range(10): number = number + 1 if number == 5: pass # pass here print('Number is ' + str(number)) print('Out of loop') التعبير pass الذي يقع بعد العبارة الشرطية if يخبر البرنامج أنَّ عليه إكمال تنفيذ الحلقة وتجاهل مساواة المتغير number للرقم 5. لنشغِّل البرنامج ولننظر إلى الناتج: Number is 1 Number is 2 Number is 3 Number is 4 Number is 5 Number is 6 Number is 7 Number is 8 Number is 9 Number is 10 Out of loop لاحظنا عند استخدامنا للتعبير pass في هذا البرنامج أنَّ البرنامج يعمل كما لو أننا لم نضع عبارة شرطية داخل حلقة التكرار؛ حيث يخبر التعبير pass البرنامج أن يكمل التنفيذ كما لو أنَّ الشرط لم يتحقق. يمكن أن تستفيد من التعبير pass عندما تكتب برنامجك لأوّل مرة أثناء تفكيرك بحلّ مشكلة ما عبر خوارزمية، لكن قبل أن تضع التفاصيل التقنية له. الخلاصة تسمح لك التعابير break و continue و pass باستعمال حلقات for و while بطريقةٍ أكثر كفاءة. ترجمة –وبتصرّف– للمقال How To Use Break, Continue, and Pass Statements when Working with Loops in Python 3 لصاحبته Lisa Tagliaferri اقرأ أيضًا الدرس التالي: كيفية تعريف الدوال في بايثون 3 الدرس السابق: كيفية إنشاء حلقات تكرار for في بايثون 3 المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون1 نقطة
-
بايثون هي لغةٌ سهلة القراءة ومتنوعة ومتعددة الاستخدامات، وغالبًا ما تُستخدم في كتابة السكربتات (scripting) والأتمتة وتحليل البيانات وتعلم الآلة (machine learning) والتطوير الخلفي (back-end development). ظهرت بايثون سنة 1991، واستوحي اسمها من مجموعة كوميدية بريطانية باسم «Monty Python»، وكان أحد الأهداف الأساسية لفريق تطوير بايثون هو جعل اللغة مرحةً وسهلة الاستخدام، وبسيطة الإعداد، وجعل طريقة كتابتها مباشرة، مع توفير تقرير مباشر عند حدوث أخطاء. وهي خيارٌ ممتازٌ للمبتدئين والوافدين الجدد على البرمجة. إصدار بايثون 3 هو الإصدار الحالي من اللغة ويُعدُّ مستقبل بايثون. سيُرشِدُك هذا الدرس خطوةً بخطوة إلى كيفية تثبيت بايثون 3 على خادم أوبنتو 18.04، البرمجة على الخوادم لها العديد من الميزات، كما تدعم المشاريع التعاونية. صحيحٌ أنَّ هذا الدرس يشرح عملية التثبيت على خادم أوبنتو 18.04، إلا أنَّ المفاهيم الأساسية فيه تنطبق على جميع توزيعات دبيان لينيكس (Debian Linux). المتطلبات المسبقة لمتابعة هذه المقالة، يجب أن تملك صلاحيات مستخدم غير جذري (non-root user ) مع امتيازات sudo على خادم أوبنتو 18.04. إذا لم تكن لك خبرة في التعامل مع بيئة النافذة الطرفية، فيمكنك مطالعة المقالة "مدخل إلى طرفيّة لينكس Linux Terminal". الخطوة الأولى: إعداد بايثون 3 في أوبنتو 18.04 والإصدارات الأخرى من دبيان لينكس، ستجد كلًا من بايثون 3 وبايثون 2 مثبتين مسبقًا. للتأكد من أنّ إصدارات بايثون حديثة، سنحدّث النظام ونرقّيه باستخدام الأمر apt للعمل مع أداة التحزيم المتقدمة من أوبنتو (Ubuntu’s Advanced Packaging Tool): sudo apt update sudo apt -y upgrade الخيار -y يعني أنَّك توافق على تثبيت جميع الحزم القابلة للتحديث، لكن قد تحتاج إلى تأكيد ذلك عند تحديث النظام وذلك اعتمادًا على الحزم التي ستُحدَّث، ونسخة نظامك. بعد إكمال العملية، يمكننا التحقق من إصدار بايثون 3 المُثبّت في النظام بكتابة: python3 -V ستحصل على مخرجات في نافذة الطرفية والتي ستريك إصدار بايثون المثبّت. قد يختلف الرقم بناءً على النسخة المثبتة في توزيعتك، لكن يجب أن يكون شبيهًا بما يلي: Python 3.5.2 لإدارة الحزم البرمجية الخاصة ببايثون، سنثبّت pip، وهي أداةٌ تعمل مع لغة بايثون تُثَبِّت وتدير الحزم البرمجية التي قد نحتاج إلى استخدامها في تطوير مشاريعنا. يمكنك تعلم المزيد عن الوحدات والحزم التي يمكنك تثبيتها بالأداة pip بقراءة المقالة كيفية استيراد الوحدات في بايثون 3 من هذه السلسلة: sudo apt install -y python3-pip يمكن تثبيت حزم بايثون بكتابة ما يلي: pip3 install package_name حيث عليك وضع اسم الحزمة أو المكتبة التابعة لبايثون مكان package_name مثل Django لتطوير الويب، أو NumPy لإجراء الحسابات العلمية. لذا، إن شئتَ تنزيل NumPy فيمكنك تنفيذ الأمر pip3 install numpy. هناك عدة حزم وأدوات تطوير أخرى يجب تثبيتها للتأكد من أنّ بيئة البرمجة جاهزة: sudo apt install build-essential libssl-dev libffi-dev python3-dev بعد أن انتهينا من ضبط بايثون وتثبيت pip، يمكننا الآن إنشاء «بيئة افتراضية» (virtual environment) لمشاريعنا. الخطوة الثانية: إعداد بيئة افتراضية تُمكِّنك البيئات الافتراضية من إنشاء مساحة معزولة في خادمك مخصصة لمشاريع بايثون، مما يعني أنَّ كل مشروع تعمل عليه ستكون له اعتماديّاته (dependencies) الخاصة به، والتي لن تؤثِّر على غيره من المشاريع. يوفِّر لنا ضبط بيئةٍ برمجيةٍ تحكمًا أكبر بمشاريع بايثون، وإمكانية التعامل مع إصداراتٍ مختلفةٍ من حزم بايثون. وهذا مهمٌ كثيرًا عندما تتعامل مع الحزم الخارجية. يمكنك ضبط أيُّ عددٍ تشاء من البيئات الافتراضية، وكل بيئة ستكون ممثلة بمجلد في خادمك يحتوي على عدد من السكربتات. هناك عدة طرق لإعداد بيئة برمجية في بايثون، لكننا سنستخدم وحدة (module) برمجية باسم venv، وهي جزءٌ من مكتبة بايثون 3 القياسية. سنثبّت venv على نظامنا بكتابة: sudo apt-get install -y python3-venv بعد إتمام التثبيت، فنحن جاهزون لإنشاء البيئات الافتراضية، يمكننا الآن إما اختيار مجلد نضع فيه بيئات بايثون، أو إنشاء مجلد جديد باستخدام الأمر mkdir كما يلي: mkdir environments cd environments بعد أن انتقلتَ إلى المجلد الذي تريد احتواء البيئات فيه، تستطيع الآن إنشاء بيئة جديدة بتنفيذ الأمر الآتي: python3.6 -m venv my_env سيُنشِئ الأمر pyvenv مجلدًا جديدًا فيه بعض الملفات التي يمكن عرضها باستخدام الأمر ls: ls my_env ستظهر لك مخرجات شبيهة بالمخرجات التالية: bin include lib lib64 pyvenv.cfg share تعمل هذه الملفات مع بعضها لضمان أنَ تكون مشاريعك معزولةٌ عن سياق الآلة المحلية، لكي لا تختلط ملفات النظام مع ملفات المشاريع. وهذا أمرٌ حسنٌ لإدارة الإصدارات ولضمان أنَّ كل مشروع يملك وصولًا إلى الحزمٍ التي يحتاجها. تتوافر أيضًا Python Wheels، والتي هي صيغة حزمٍ مبنية (built-package format) لبايثون، والتي يمكن أن تُسرِّع من تطوير البرامج بتقليل عدد المرات التي تحتاج فيها إلى تصريف (compile) المشروع، وهي موجودةٌ في المجلد share في توزيعة أوبنتو 18.04. عليك تفعيل البيئة لاستخدامها، وذلك بكتابة الأمر التالي الذي سيُنفِّذ سكربت التفعيل: source my_env/bin/activate يجب أن تظهر الآن سابقةٌ (prefix) في المِحث (prompt) والتي هي اسم البيئة المستخدمة، وفي حالتنا هذه يكون اسمها my_env، وقد يكون مظهر المِحَث مختلفًا في توزيعة دبيان، وذلك اعتمادًا على الإصدار المستخدم؛ لكن يجب أن تشاهد اسم البيئة بين قوسين في بداية السطر: (my_env) sammy@ubuntu:~/environments$ ستسمح لك السابقة بمعرفة أنَّ البيئة my_env مفعلة حاليًا، وهذا يعني أننا سنستخدم إعدادات وحزم هذه البيئة عند إنشاء مشاريع جديدة. ملاحظة: يمكنك داخل البيئة الافتراضية أن تستخدم الأمر python بدلًا من python3 والأمر pip بدلًا من pip3 إن شئتَ. أما إذا كنتَ تستخدم بايثون 3 خارج البيئة الافتراضية، فيجب عليك حينها استخدام python3 و pip3 حصرًا. يجب أن تكون بيئتك الافتراضية جاهزةً للاستخدام بعد اتباعك للخطوات السابقة. الخطوة الثالثة: إنشاء برنامج بسيط بعد أن أكملنا ضبط بيئتنا الافتراضية، لننشِئ برنامجًا بسيطًا يعرض العبارة «Hello World!»، وبهذا سنتحقق من أنَّ البيئة تعمل بالشكل الصحيح، ولكي تتعوّد على إنشاء برامج بايثون إن كنتَ وافدًا جديدًا على اللغة. علينا أولًا تشغيل محرر ملفات نصية لإنشاء ملف جديد، وليكن المحرر nano الذي يعمل من سطر الأوامر: (my_env) sammy@ubuntu:~/environments$ nano hello.py بعد فتح الملف في نافذة الطرفية، سنكتب البرنامج الخاص بنا: print("Hello, World!") أغلق محرر nano بالضغط على Ctrl+x ثم اضغط على y عندما يسألك عن حفظ الملف. بعد أن يُغلَق المحرر nano وتعود إلى سطر الأوامر، حاول تنفيذ البرنامج: (my_env) sammy@ubuntu:~/environments$ python hello.py سيؤدي برنامج hello.py الذي أنشأتَه إلى طباعة الناتج الآتي في الطرفية: Hello, World! للخروج من البيئة، اكتب الأمر deactivate وستعود إلى مجلدك الأصلي. الخلاصة تهانينا! لقد ضبطتَ الآن بيئة تطويرٍ للغة بايثون 3 في خادم لينيكس ديبيان، حان الآن الوقت للتعمق بلغة بايثون وإنشاء برامج رائعة! بالتوفيق. ترجمة -وبتصرّف- للمقال How To Install Python 3 and Set Up a Programming Environment on an Ubuntu 18.04 Server لصاحبته Lisa Tagliaferri اقرأ أيضًا المقالة التالية: كيف تكتب أول برنامج لك المقالة السابقة: اعتبارات عملية للاختيار ما بين بايثون 2 و بايثون 3 المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون1 نقطة
-
يسمح لنا استخدام حلقات التكرار في برمجة الحاسوب بأتمتة وتكرار المهام المتشابهة مرّاتٍ عدِّة. وسنشرح في هذا الدرس كيفية استخدام حلقة for في بايثون. حلقة for تؤدي إلى تكرار تنفيذ جزء من الشيفرات بناءً على عدّاد أو على متغير، وهذا يعني أنَّ حلقات for تستعمل عندما يكون عدد مرات تنفيذ حلقة التكرار معلومًا قبل الدخول في الحلقة، وذلك على النقيض من حلقات while المبنية على شرط. حلقات for تُبنى حلقات for في بايثون كما يلي: for [iterating variable] in [sequence]: [do something] ستُنفَّذ الشيفرات الموجودة داخل حلقة التكرار عدِّة مرات إلى أن تنتهي الحلقة. لننظر إلى كيفية مرور الحلقة for على مجالٍ من القيم: for i in range(0,5): print(i) سيُخرِج البرنامج السابق عند تشغيله الناتج الآتي: 0 1 2 3 4 ضبطنا المتغير i في حلقة for ليحتوي على القيمة التي ستُنفَّذ عليها حلقة التكرار، وكان مجال القيم التي ستُسنَد إلى هذا المتغير من 0 إلى 5. ثم طبعًا قيمة المتغير في كل دوران لحلقة التكرار، لكن أبقِ في ذهنك أنَّنا نميل إلى بدء العد من الرقم 0 في البرمجة، وعلى الرغم من عرض خمسة أرقام، لكنها تبدأ بالرقم 0 وتنتهي بالرقم 4. من الشائع أن ترى استخدامًا لحلقة for عندما تحتاج إلى تكرار كتلة معيّنة من الشيفرات لعددٍ من المرات. استخدام حلقات التكرار مع الدالة range() إحدى أنواع السلاسل غير القابلة للتعديل في بايثون هي تلك الناتجة من الدالة range()، وتستخدم الدالة range() في حلقات التكرار للتحكم بعدد مرات تكرار الحلقة. عند التعامل مع الدالة range() عليك أن تمرر معاملًا رقميًا أو معاملين أو ثلاثة معاملات: start يشير إلى القيم العددية الصيحية التي ستبدأ بها السلسلة، وإذا لم تُمرَّر قيمة لهذا المعامل فستبدأ السلسلة من 0 stop هذا المعامل مطلوب دومًا وهو القيمة العددية الصحيحة التي تمثل نهاية السلسلة العددية لكن دون تضمينها step هي مقدار الخطوة، أي عدد الأرقام التي يجب زيادتها (أو إنقاصها إن كنّا نتعامل مع أرقام سالبة) في الدورة القادمة، وقيمة المعامل step تساوي 1 في حال لم تُحدَّد له قيمة لننظر إلى بعض الأمثلة التي نُمرِّر فيها مختلف المعاملات إلى الدالة range(). لنبدأ بتمرير المعامل stop فقط، أي أنَّ السلسلة الآتية من الشكل range(stop): for i in range(6): print(i) كانت قيمة المعامل stop في المثال السابق مساويةً للرقم 6، لذا ستمر حلقة التكرار من بداية المجال 0 إلى نهايته 6 (باستثناء الرقم 6 كما ذكرنا أعلاه): 0 1 2 3 4 5 المثال الآتي من الشكل range(start ,stop) الذي تُمرَّر قيم بدء السلسلة ونهايتها: for i in range(20,25): print(i) المجال –في المثال السابق– يتراوح بين 20 (بما فيها الرقم 20) إلى 25 (باستثناء الرقم 25)، لذا سيبدو الناتج كما يلي: 20 21 22 23 24 الوسيط step الخاص بالدالة range() شبيه بمعامل الخطوة الذي نستعمله عند تقسيم [السلاسل النصية](آلية فهرسة السلاسل النصية وطريقة تقسيمها في بايثون 3) لأنه يستعمل لتجاوز بعض القيم ضمن السلسلة. يأتي المعامل step في آخر قائمة المعاملات التي تقبلها الدالة range() وذلك بالشكل الآتي range(start, stop, step). لنستعمل المعامل step مع قيمة موجبة: for i in range(0,15,3): print(i) سيؤدي المثال السابق إلى إنشاء سلسلة من الأرقام التي تبدأ من 0 وتنتهي عند 15 لكن قيمة المعامل step هي 3، لذا سيتم تخطي رقمين في كل دورة، أي سيكون الناتج كالآتي: 0 3 6 9 12 يمكننا أيضًا استخدام قيمة سالبة للمعامل step للدوران إلى الخلف، لكن علينا تعديل قيم start و stop بما يتوافق مع ذلك: for i in range(100,0,-10): print(i) قيمة المعامل start في المثال السابق هي 100، وكانت قيمة المعامل stop هي 0، والخطوة هي -10، لذا ستبدأ السلسلة من الرقم 100 وستنتهي عند الرقم 0، وسيكون التناقص بمقدار 10 في كل دورة، ويمكننا ملاحظة ذلك في الناتج الآتي: 100 90 80 70 60 50 40 30 20 10 الخلاصة: عندما نبرمج باستخدام لغة بايثون، فسنجد أننا نستفيد كثيرًا من السلاسل الرقمية التي تنتجها الدالة range(). دورة تطوير التطبيقات باستخدام لغة Python احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة اشترك الآن استخدام حلقة for مع أنواع البيانات المتسلسلة يمكن الاستفادة من القوائم (من النوع list) وغيرها من أنواع البيانات المتسلسلة واستعمالها كمعاملات لحلقات for، فبدلًا من الدوران باستخدام الدالة range() فيمكننا تعريف قائمة ثم الدوران على عناصرها. سنُسنِد في المثال الآتي قائمةً إلى متغير، ثم سنستخدم حلقة for للدوران على عناصر القائمة: sharks = ['hammerhead', 'great white', 'dogfish', 'frilled', 'bullhead', 'requiem'] for shark in sharks: print(shark) في هذه الحالة، قمنا بطباعة كل عنصر موجود في القائمة؛ وصحيحٌ أننا استعملنا الكلمة shark كاسم للمتغير، لكن يمكنك استعمال أي اسم صحيح آخر ترغب به، وستحصل على نفس النتيجة: hammerhead great white dogfish frilled bullhead requiem الناتج السابق يُظهِر دوران الحلقة for على جميع عناصر القائمة مع طباعة كل عنصر في سطرٍ منفصل. يشيع استخدام القوائم والأنواع الأخرى من البيانات المتسلسلة مثل السلاسل النصية وبنى tuple مع حلقات التكرار لسهولة الدوران على عناصرها. يمكنك دمج هذه الأنواع من البيانات مع الدالة range() لإضافة عناصر إلى قائمة، مثلًا: sharks = ['hammerhead', 'great white', 'dogfish', 'frilled', 'bullhead', 'requiem'] for item in range(len(sharks)): sharks.append('shark') print(sharks) الناتج: ['hammerhead', 'great white', 'dogfish', 'frilled', 'bullhead', 'requiem', 'shark', 'shark', 'shark', 'shark', 'shark', 'shark'] أضفنا هنا السلسلة النصية 'shark' خمس مرات (وهو نفس طول القائمة sharks الأصلي) إلى القائمة sharks. يمكننا استخدام حلقة for لبناء قائمة جديدة: integers = [] for i in range(10): integers.append(i) print(integers) هيّئنا في المثال السابق قائمةً فارغةً باسم integers لكن حلقة التكرار for ملأت القائمة لتصبح كما يلي: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] وبشكلٍ شبيهٍ بما سبق، يمكننا الدوران على السلاسل النصية: sammy = 'Sammy' for letter in sammy: print(letter) الناتج: S a m m y يمكن الدوران على بنى tuple كما هو الحال في القوائم والسلاسل النصية. عند المرور على عناصر نوع البيانات dictionary، فمن المهم أن تبقي بذهنك البنية الخاصة به (key:value) لكي تضمن أنَّك تستدعي العنصر الصحيح من المتغير. هذا مثالٌ بسيطٌ نعرض فيه المفتاح (key) والقيمة (value): sammy_shark = {'name': 'Sammy', 'animal': 'shark', 'color': 'blue', 'location': 'ocean'} for key in sammy_shark: print(key + ': ' + sammy_shark[key]) الناتج: name: Sammy animal: shark location: ocean color: blue عند استخدام متغيرات من النوع dictionary مع حلقات for فيكون المتغير المرتبط بحلقة التكرار متعلقًا بمفتاح القيم، وعلينا استخدام التعبير dictionary_variable[iterating_variable] للوصول إلى القيمة الموافقة للمفتاح. ففي المثال السابق كان المتغير المرتبط بحلقة التكرار باسم key وهو يُمثِّل المفاتيح، واستعملنا التعبير sammy_shark[key] للوصول إلى القيمة المرتبطة بذاك المفتاح. الخلاصة: تُستعمَل حلقات التكرار عادةً للدوران على عناصر البيانات المتسلسلة وتعديلها. حلقات for المتشعّبة يمكن تشعّب حلقات التكرار في بايثون، كما هو الحال في بقية لغات البرمجة. حلقة التكرار المتشعبة هي الحلقة الموجودة ضمن حلقة تكرار أخرى، وهي شبيهة بعبارات if المتشعّبة. تُبنى حلقات التكرار المتشعبة كما يلي: for [first iterating variable] in [outer loop]: # Outer loop [do something] # Optional for [second iterating variable] in [nested loop]: # Nested loop [do something] يبدأ البرنامج بتنفيذ حلقة التكرار الخارجية، ويُنفَّذ أوّل دوران فيها، وأوّل دوران سيؤدي إلى الدخول إلى حلقة التكرار الداخلية، مما يؤدي إلى تنفيذها إلى أن تنتهي تمامًا. ثم سيعود تنفيذ البرنامج إلى بداية حلقة التكرار الخارجية، ويبدأ بتنفيذ الدوران الثاني، ثم سيصل التنفيذ إلى حلقة التكرار الداخلية، وستُنفَّذ حلقة التكرار الداخلية بالكامل، ثم سيعود التنفيذ إلى بداية حلقة التكرار الخارجية، وهلّم جرًا إلى أن ينتهي تنفيذ حلقة التكرار الخارجية أو إيقاف حلقة التكرار عبر استخدام [التعبير break](كيفية استخدام تعابير break و continue و pass عند التعامل مع حلقات التكرار في بايثون 3) أو غيره من التعابير. لنُنشِئ مثالًا يستعمل حلقة forمتشعبة لكي نفهم كيف تعمل بدقة. حيث ستمر حلقة التكرار الخارجية في المثال الآتي على قائمة من الأرقام اسمها num_list، أما حلقة التكرار الداخلية فستمر على قائمة من السلاسل النصية اسمها alpha_list: num_list = [1, 2, 3] alpha_list = ['a', 'b', 'c'] for number in num_list: print(number) for letter in alpha_list: print(letter) سيظهر الناتج الآتي عند تشغيل البرنامج: 1 a b c 2 a b c 3 a b c يُظهِر الناتج السابق أنَّ البرنامج أكمل أوّل دوران على عناصر حلقة التكرار الخارجية بطباعة الرقم 1، ومن ثم بدأ تنفيذ حلقة التكرار الدخلية مما يطبع الأحرف a و b و c على التوالي. وبعد انتهاء تنفيذ حلقة التكرار الداخلية، فعاد البرنامج إلى بداية حلقة التكرار الخارجية طابعًا الرقم 2، ثم بدأ تنفيذ حلقة التكرار الداخلية (مما يؤدي إلى إظهار a و b و c مجددًا). وهكذا. يمكن الاستفادة من حلقات for المتشعبة عند المرور على عناصر قوائم تتألف من قوائم. فلو استعملنا حلقة تكرار وحيدة لعرض عناصر قائمة تتألف من عناصر تحتوي على قوائم، فستُعرَض قيم القوائم الداخلية: list_of_lists = [['hammerhead', 'great white', 'dogfish'],[0, 1, 2],[9.9, 8.8, 7.7]] for list in list_of_lists: print(list) الناتج: ['hammerhead', 'great white', 'dogfish'] [0, 1, 2] [9.9, 8.8, 7.7] وفي حال أردنا الوصول إلى العناصر الموجودة في القوائم الداخلية، فيمكننا استعمال حلقة for متشعبة: list_of_lists = [['hammerhead', 'great white', 'dogfish'],[0, 1, 2],[9.9, 8.8, 7.7]] for list in list_of_lists: for item in list: print(item) الناتج: hammerhead great white dogfish 0 1 2 9.9 8.8 7.7 الخلاصة: نستطيع الاستفادة من حلقات for المتشعبة عندما نريد الدوران على عناصر محتوى في قوائم. الخلاصة رأينا في هذا الدرس كيف تعمل حلقة التكرار for في لغة بايثون، وكيف نستطيع إنشاءها واستعمالها. حيث تستمر حلقة for بتنفيذ مجموعة من الشيفرات لعددٍ مُحدِّدٍ من المرات. هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة –وبتصرّف– للمقال How To Construct For Loops in Python 3 لصاحبته Lisa Tagliaferri اقرأ أيضًا الدرس التالي: كيفية استخدام تعابير break و continue و pass عند التعامل مع حلقات التكرار في بايثون 3 الدرس السابق: كيفية إنشاء حلقات تكرار while في بايثون 3 المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون1 نقطة
-
أهلًا بك، ينقسم مجال تطوير الويب(سواء مواقع الويب أو تطبيقات الويب) إلى قسمين، تطوير واجهات المستخدم (frontend) وتطوير الخلفيات (backend)... تطوير واجهات المستخدم أي تصميم الصفحات من أشكال وألوان وحركات وانتقالات في عناصر الواجهه برمجيًا.. بالأساس تستخد HTML CSS JavaScript لهذا الغرض.. بينما تطوير الخلفيات (backend) هو برمجة الجانب الغير مرئي للمستخدم وله أهمية كبيرة في النظام .. مثلًا نظام الدخول والخروج وإنشاء الحساب والتحقق منه والدفع الإلكتروني والتواصل مع قاعدة البيانات ونظام العضويات والصلاحيات في البرنامج وكل هذه الأمور المشابهه.. ويُستخدم لهذا الغرض لغات برمجية مع إطارات عملها مثل لغة PHP ولغة JavaScript (تستخدم في frontend و backend معًا) و #C و Python وغيرها لكن بشكل عام تطوير تطبيقات الويب أي تطوير القسمين معًا.. frontend و backend ويُسمى مطور تطبيقات الويب المحترف في المجالين الإثنين Full Stack Developer أي لديه خبرة في الواجهات والبرمجة الخلفية أيضًا1 نقطة
-
التفكيك Destructuringذكرنا في الجزء السابق أن اهتمامًا كبيرًا أُوليَ لتسهيل كتابة الشفرة وقراءتها في ECMAScript 6، والإسناد بالتفكيك (Destructuring assignment) لا يخرج عن هذا السياق، وهو ليس بالمفهوم الجديد في عالم البرمجة، فهو معروف في Python وفي Ruby. بعيدًا عن تعقيدات المصطلحات، إليك هذا المثال: var [a, b, c] = [1, 2, 3]; a == 1 // true b == 2 // true c == 3 // trueما الذي يحدث هنا؟ بكل بساطة تسمح ECMAScript 6 بصياغة جديدة للتعريف عن المتغيرات أو إسناد قيم جديدة إليها جُملةً واحدة من خلال جمعها ضمن قوسي مصفوفة (Array) وسيقوم مُفسّر اللغة بإسناد قيمة مقابلة لكل متغيّر من المصفوفة الواقعة على يمين مُعامل الإسناد (=). الأمر لا يقتصر على إسناد المصفوفات، بل يمكن أيضًا إسناد خصائص العناصر: let person = { firstName: "John", lastName: "Smith", Age: 42, Country: "UK" }; let { firstName, lastName } = person; console.log(`Hello ${ firstName } ${ lastName }!`); // Hello John Smith!في هذا المثال لدينا متغيّرات تتبع للنطاق العامّ firstName وlastName، وقد أسندنا لها قيمًا من خصائص الكائن person، حيث يبحث مفسّر اللّغة عن خصائص في الكائن person يماثل اسمها اسم المتغيّر المفروض ويُسندها إلى المُتغيّرات. يمكن توضيح المقصود بصورة أفضل إذا أعدنا كتابة الشفرة لتتوافق مع الإصدار الحالي من JavaScript: var person = { firstName: "John", lastName: "Smith", Age: 42, Country: "UK" }; var firstName = person.firstName; var lastName = person.lastName; console.log("Hello " + firstName + " " + lastName + "!"); // Hello John Smith! يشيع استخدام التفكيك في CoffeeScript (وهي لغة أقر Brendan Eich مُخترع JavaScript بأنّ الإصدار الأخير من JavaScript استوحى الكثير منها)، وخصوصًا عندما تُنظّم البرامج في وحدات كما في Node.js ويكون اهتمامًا مُقتصرًا على استيراد جزء مُحدّد من الوحدة المعنيّة: { EventEmitter } = require 'events' { EditorView } = require 'atom' { compile } = require 'coffee-script' compile('# coffeescript code here');عند تحويل هذا النص إلى JavaScript الحالية، سنحصل على: var EventEmitter = require('events').EventEmitter; var EditorView = require('atom').EditorView; var compile = require('events').compile; compile('# coffeescript code here');من الاستخدامات المفيدة للإسناد بالتفكيك التبديل بين قيمتي متغيّرين بصورة سهلة، سنقتبس المثال من توثيق CoffeeScript ونُعيد كتابته بـJavaScript: var theBait = 1000; var theSwitch = 0; [theBait, theSwitch] = [theSwitch, theBait];قبل ES6 كنا لنحتاج لكتابة مُتغيّر مؤقّت نخزن فيه قيمة إحدى المتغيّرين للاحتفاظ بها قبل التبديل بين القيمتين، وهو ما يفعله محوّل CoffeeScript بالفعل ليعطينا شفرة JavaScript متوافقة مع الإصدار الحالي (مع أنه يقوم بتخزين كلا القيمتين في مصفوفة، إلا أنّ الفكرة تبقى ذاتها): var theBait, theSwitch, _ref; theBait = 1000; theSwitch = 0; _ref = [theSwitch, theBait], theBait = _ref[0], theSwitch = _ref[1];المُكرِّرات (Iterators) وحلقة for... ofما من لغة برمجة تخلو من وسيلة للمرور على عدد من القيم وتكرار تنفيذ عمليّة معيّنة على هذه القيم، من أبسط هذه الوسائل حلقة for التقليديّة الشّهيرة، وفي JavaScript يشيع استخدام حلقة for... in إلى جانبها للمرور على أسماء خصائص العناصر، إذ يمكننا معرفة كل خصائص العنصر document بسطرين فقط: for (var propertyName in document) { console.log(propertyName); } // "body" // "addEventListener" // "getElementById" // ...لاحظ أن حلقة for... in تُعيد أسماء خصائص العنصر (كسلسة نصيّة String)، والأمر لا يستثني المصفوفات، فهي ليست سوى كائنات بأسماء خصائص توافق رقم الفهرس (Index): for (var i in [1, 2, 3]) { console.log(i); } // "0" // "1" // "2" من عيوب حلقة for... in أن لا شيء في تعريف اللغة يُجبر مُفسّر اللّغة على إخراج العناصر بترتيب ثابت بالضّرورة، وهذا يعني أنها تصبح مباشرة غير صالحة للمرور على المصفوفات - التي تستخدم لحفظ عناصر مُرتّبة - بطريقة بديهيّة، ويحلّ محلّها حلقة for التقليديّة عندئذٍ، وأمّا عند استخدامها للمرور على الكائنات، فإنّها لا تُعيد إلّا الخصائص الّتي تُعرّف على أنها قابلة للتعداد (enumerable)، وهو شيء يُحدّد عند تعريف الخاصّة، كما أنّها تُعيد الخصائص القابلة للتعداد التي ورثها الكائن عن "آباءه" ضمن سلسلة الوراثة، وهو تصرّف قد لا يكون مرغوبًا دومًا، وغالبًا سترى المطوّرين يُجرون فحصًا للخاصّة قبل متابعة تنفيذ الشفرة لمعرفة ما إذا كانت تخصّ العنصر ذاته أمّ أنّه ورثها: var obj = { a: 1, b: 2, c: 3 }; // كائن جديد لا يرث سوى النموذج Object for (var prop in obj) { console.log("o." + prop + " = " + obj[prop]); } // "o.a = 1" // "o.b = 2" // "o.c = 3"في هذا المثال (المنقول عن شبكة مطوّري موزيلا) فرضنا كائنًا جديدًا بثلاث خصائص، وعند المرور عليه بحلقة for... in فإنّنا حصلنا على النتيجة المتوقّعة، ولم نحصل على خصائص إضافيّة لأنّ الكائن الذي فرضناه لا يرث أي كائن آخر سوى Object (الذي ترثه كل الكائنات افتراضًا). أما في المثال التالي، فقد احتجنا لإجراء اختبار hasOwnProperty على العنصر الوارث لكي لا تظهر سوى الخاصة color التي يملكها بذاته ولم يرثها: var triangle = {a: 1, b: 2, c: 3}; function ColoredTriangle() { this.color = "red"; } ColoredTriangle.prototype = triangle; var obj = new ColoredTriangle(); for (var prop in obj) { if (obj.hasOwnProperty(prop)) { console.log("o." + prop + " = " + obj[prop]); } } // Output: // "o.color = red"حسنًا، لقد أطلنا الحديث عن حلقة for... in وهي ليست بالجديدة؛ لكنّنا أصبحنا نرى الحاجة لشيء جديد أكثر بساطة ومرونة، فهذا ما تبتغيه ES6 في النهاية، ولهذا نشأت فكرة المُكرّرات؛ التي تسمح لأي عنصر بأن يختار لنفسه الطّريقة التي يتصرّف بها عند المرور به في حلقة، ومع المُكرّرات لا بدّ من نوع جديد من الحلقات لتلبية هذه الحاجة والمحافظة على حلقة for... in للتوافق مع الإصدارات القديمة. من هنا نشأت حلقة for... of الجديدة. for (var num of [1, 2, 3]) { console.log(num); } // 1 // 2 // 3 for (var node of document.querySelectorAll('a')) { console.log(node); } // <a class="title" href="/"> // <a class="contact" href="/contact/">حصلنا في المثالين السابقين على قيمة الخاصّة وليس اسم الخاصّة، لكنّ هذا لا يعني أنّ حلقة for... of تُعيد قيم الخصائص دومًا، بل إنّها تستدعي مُكرّر الكائن (Iterator) وتطلب منه في كلّ دورة للحلقة تزويدها بشيء ما، وتترك للمُكرّر الحُريّة بإعادة أي قيمة يرغب بها، ولكن ولأنّنا نستدعي في مثالنا مصفوفة أولاً، وعنصر من نوع NodeList ثانيًا، وكلا النّوعين يُعيد مُكرُّرهما قيمَ العناصر في المصفوفة، فإنّنا نحصل على تلك النتيجة البديهيّة. بإمكاننا إنشاء أصناف بمُكرّرات خاصّة نُنشئها بأنفسنا، ولنفترض أن لدينا نوعًا لصفّ ضمن مدرسة ابتدائية، ونريد أن نحصل على تفاصيل الطلّاب على هيئة نص مُنسّق عند المرور على الصّفّ في حلقة for... of: function SchoolClass(students) { this.students = students; } SchoolClass.prototype[Symbol.iterator] = function*() { for (let i = 0; i < this.students.length; i++) { let student = this.students[i]; yield `#${i+1} ${student.name} (${student.age} years old)`; } } var ourClass = new SchoolClass([ { name: "Ahmed", age: 10 }, { name: "Alaa", age: 9 }/*, ...*/ ]); for (student of ourClass) { console.log(student); } // "#1 Ahmed (10 years old)" // "#2 Alaa (9 years old)" // "#3 ..." ...استخدمنا الرمز الخاص Symbol.iterator لإسناد دالّة مُولّد (Generator function) التي تُعطينا عند استدعائها نسخة من مُكرّر الصنف المُخصّص الذي أنشأناه. سنتعرف بعد قليل على المُولّدات (Generators) وكذلك على الرموز (Symbols) في وقت لاحق. لاحظ أنّنا استخدمنا حلقة for... of للمرور على محتويات ourClass. تذكّر أنّنا استخدمنا هذه الحلقة في الجزء السابق مع Array Comprehension، كما في المثال: let people = ["Samer", "Ahmed", "Khalid"]; console.log([`Hello ${person}` for (person of people)]); إن كانت الفقرة الأخيرة غامضة بعض الشيء فلا تقلق، سنتوسّع بشرح المولّدات بعد قليل. لكن دعونا نتوقّف قليلاً ولننتقل إلى الجانب الفلسفي لهذه الإضافات في JavaScript، قد تبدو للوهلة الأولى تعقيدات بلا طائل، خصوصًا وأنّ كثيرًا منها لا يهدف سوى للتسهيل، ولا يقدّم شيئًا يستحيل إنجازه بالإصدارات السابقة من اللغة؛ هنا يمكن الرّدّ بأنّ تطوّر اللغة متعلّق بكيفيّة استخدامها والخبرة التي تُكتسب مع مرور السّنين، حيث تظهر للمطوّرين حاجات جديدة وأفكار تطبّق مرارًا لدرجة أنها ترتقي لتصبح ضمن أساسات اللغة. سهولة كتابة الشفرة لم تعد رفاهية، بل هي ضرورة لإنجاز المشاريع الكبيرة لأنّها تتيح اختصار الوقت الذي كان سيضيع في كتابة متكرّرة ومُملّة، كما أنّها تُلبّي ما يتوقّعه المطوّرون من لغة أصبحت تؤخذ على محمل الجدّ وتُستخدم في تطوير تطبيقات ضخمة ومُعقّدة بعد أن كان جُلّ استخدامها تنفيذ بعض المهام البسيطة. المُولِّدات (Generators)المولدات (Generators) ببساطة هي دوال يمكن إيقافها والعودة إليها في وقت لاحق مع الاحتفاظ بسياقها دون تغيير، صياغة دوال المولدات لا تختلف كثيرًا عن صياغة الدوال التقليدية، كل ما عليك هو إضافة إشارة * بعد function واستخدام yield بدل return، المثال التالي سيوضح فكرة المولدات أكثر: function* getName() { let names = ['Muhammad', 'Salem', 'Abdullah']; for (name of names) { yield name; } } let nameGenerator = getName(); nameGenerator.next().value; // 'Muhammad' nameGenerator.next().value; // 'Salem' nameGenerator.next().value; // 'Abdullah' nameGenerator.next().value; // undefined } ما الذي يحدث هنا؟ فرضنا دالّة مولّد (Generator function) (والتي تُميّز بإشارة النجمة *) سمّيناها getName، وفيها صرحنا عن مصفوفة فيها أسماء، وظيفة هذه الدالة أن تعطينا عند استدعائها نسخة من مُكرّر (Iterator) (الذي شرحناه لتوّنا)، يزوّدنا بالأسماء بالترتيب في كل مرة نستدعيه فيها ليعطينا النتيجة التالية (next())، أولاً يجب حفظ نسخة المُكرّر ضمن متغير لكي نسمح له بحفظ حالته، ودون ذلك سيعطينا استدعاء دالّة المولد مباشرةً getName().next() دوماً النتيجة الأولى لأننا عملياً نُنشئ نسخة جديدة عنه في كل مرة نستدعيه، أما استدعاء نسخة عنه وحفظها في متغير مثل myGenerator فيسمح لنا باستدعاء .next() عليها كما هو متوقع. لا ترجع الدالة .next() القيمة التي نرسلها عبر yield فقط، بل ترجع كائناً يحوي القيمة المطلوبة ضمن الخاصة value، وخاصة أخرى done تسمح لنا بمعرفة ما إذا كان المولد قد أعطانا كل شيء. لنُعِدْ ترتيب أفكارنا: المولّدات تسمح بتوقّف تنفيذها مع الاحتفاظ بحالة التنفيذ (يحدث توقّف التنفيذ عند كلّ كلمة yield). فلو أنّنا كتبنا دالّة تقليديّة في المثال أعلاه مع return بدل yield لحصلنا في كلّ مرّة على الاسم الأول (Muhammad). وهذه الميزة في المولّدات يمكن استغلالها لإنشاء حلقات لا نهائية دون إعاقة متابعة البرنامج: function* numberGenerator() { for (let i = 0; true; i++) { yield i; } } let numGen = numberGenerator(); numGen.next(); // { value: 0, done: false } numGen.next(); // { value: 1, done: false } numGen.next(); // { value: 2, done: false } numGen.next(); // { value: 3, done: false } // ...دوالّ المولّدات تُعطي عند استدعاءها مُكرّرات، وهذا يعني إمكانيّة استخدامها في حلقة for... of (احذر من تطبيق مثال كهذا على مولّد غير منتهٍ كما في المثال السابق!): for (let name of getName()) { console.log(name); } // "Muhammad" // "Salem" // "Abdullah"لكلّ مُكرّر وظيفة .next() مهمّتها بدء تنفيذ الدّالّة أو متابعة تنفيذها ثم إيقافها مؤقّتًا عند كلّ كلمة yield. استدعاء next() على المُكرّر يعيد لنا في كلّ مرة كائنًا ذا خاصّتين: الأولى value وهي أيّ شيء نُعيده بكلمة yield، والثّانية done وهي قيمة منطقيّة (Boolean) تشير إلى حالة انتهاء تنفيذ الدّالة.تقبل الوظيفة .next() للمُكرّرات مُعاملاً اختياريًّا تستقبله وتُرسله لدالّة المولّد بعد متابعة التنفيذ، ويمكن استخدامها لإرسال رسائل لدالّة المولّد بحيث نؤثّر في تنفيذه: function* numberGenerator() { for (let i = 0; true; i++) { var reset = yield i; if (reset) i = -1; } } let numGen = numberGenerator(); numGen.next(); // { value: 0, done: false } numGen.next(); // { value: 1, done: false } numGen.next(); // { value: 2, done: false } numGen.next(); // { value: 3, done: false } numGen.next(true); // { value: 0, done: false }في هذا المثال مرّرنا القيمة true إلى الوظيفة .next() على المُكرّر، والذي بدوره يُرسلها لدالّة المولّد كنتيجة yeild i في الدّورة الموافقة للحلقة، لنقومَ بحفظها في متغيّر reset ونُجريَ فحصًا عند متابعة التنفيذ لإعادة تعيين قيمة i، التي ستزداد بمقدار واحد مع بدء الدورة التالية لحلقة for جاعلةً قيمة i مساوية للصّفر. خصائص المولّدات تجعلها مناسبة جدًا لكتابة شيفرة غير متزامنة بصورة أسهل تكاد تبدو فيها وكأنها شيفرة متزامنة خالية من الاستدعاءات الراجعة المتداخلة (Nested callbacks)؛ هذه الفكرة تحتاج إلى تركيز لأنها أساس لعدد من المكتبات مثل co وsuspend التي ظهرت مؤخّرًا وتصاعدت شعبيّتها بسرعة لأنّها تحلّ مشكلة جوهرية في استخدام JavaScript، ألا وهي التعامل مع الدوال غير المتزامنة (asynchronous functions) وذلك بالاعتماد كُليًّا على المُولّدات. لنفترض أنّ لدينا موقعًا لقراءة الكتب يعرض ملفّ المستخدم الشّخصيّ مع عدد الكتب التي قرأها وعنوان آخر كتاب مع تقييم المستخدم له: var list = document.querySelector("#book-list"); getJSON("http://reading-website.com/users/fwz.json", function(err, user) { if (err) return; // افعل شيئًا بما بخصوص الخطأ var num_books = user.books.length; var most_recent_book_id = user.books[num_books - 1]; getJSON("http://reading-website.com/users/fwz/ratings/" + most_recent_book_id + ".json", function(err, user_rating) { getJSON("http://reading-website.com/books/" + most_recent_book_id + ".json", function(err, book) { var fragment = document.createDocumentFragment(); var h2 = document.createElement("h2"); h2.textContent = user.full_name; var h3 = document.createElement("h3"); h3.textContent = "الكتب التي قرأها"; for (let book of books) { let li = document.createElement("li"); li.textContent = book.title + (book.id == most_recent_book_id ? " " + user_rating : ""); fragment.appendChild(li); } list.appendChild(fragment); }); }); })في المثال السابق احتجنا إلى إرسال 3 طلبات AJAX يعتمد أحدها على الآخر، ولأنّنا لا نستطيع إرسال طلب بتقييم المستخدم للكتاب قبل معرفة مُعرّف الكتاب، فلا بدّ من أن يرسل الطلب الخاصّ بتقييم الكتاب ضمن الاستدعاء الرّاجع لطلب معلومات المستخدم، ثمّ يمكن جلب عنوان الكتاب ضمن الاستدعاء الرّاجع للطلب السّابق، وهذا يعني زيادة تعقيد الشفرة مع تداخل الاستدعاءات الرّاجعة لتبدو أشبه بسباغيتي لا تُعرف بدايته من نهايته. تخيّلوا -لغرض التّخيّل- لو أمكننا كتابة هذه الشفرة (وهي غير متزامنة) لتبدو لقارئها وكأنها نص برمجي يسير بترتيب متزامن وبديهيّ... ألن يكون هذا أعظم شيء منذ اختراع JavaScript؟ var list = document.querySelector("#book-list"); try { var user = getJSON("http://reading-website.com/users/fwz.json"); var num_books = user.books.length; var most_recent_book_id = user.books[num_books - 1]; var user_rating = getJSON("http://reading-website.com/users/fwz/ratings/" + most_recent_book_id + ".json"); var book = getJSON("http://reading-website.com/books/" + most_recent_book_id + ".json"); var fragment = document.createDocumentFragment(); var h2 = document.createElement("h2"); h2.innerText = user.full_name; var h3 = document.createElement("h3"); h3.innerText = "الكتب التي قرأها"; for (let book of books) { let li = document.createElement("li"); li.innerText = book.title + (book.id == most_recent_book_id ? " " + user_rating : ""); fragment.appendChild(li); } list.appendChild(fragment); } catch (e) { // افعل شيئًا بما بخصوص الخطأ // Error } نحن نعلم أن الأمور لا يمكن أن تكون بهذه الروعة، وأنّ الشفرة أعلاه لن تعمل... نحن نعلم أن شيفرتنا تحتاج تفاصيل المستخدم للحصول على الكتب، وأننا نحتاج للكتاب لجلب عنوانه وتقييمه، وتنفيذ هذه المهمّات بشكل غير متزامن لا يعني أنّه ليس علينا انتظار المهمّة الأولى قبل إطلاق الثانية - بل يعني فقط أن المتصفح يمكنه تنفيذ رسم العناصر الأخرى وعرض الصفحات وإرسال طلبات أخرى في هذا الوقت. حسنًا، لدي خبر جيّد وآخر سيئ: أمّا الجيّد فهو أنّنا كتابة شيفرة شبيه بهذه أصبحت قريبة المنال مع الدّوالّ غير المتزامنة (Async Functions)، وأمّا الخبر السيّئ فهو أنّ علينا الانتظار إلى الإصدار 7 من ECMAScript لنستطيع كتابتها! (مع العلم أن المتصفّحات لم تنتهِ من تطبيق ES6!). لكن هذا لا يعني أن نقف مكتوفي الأيدي إلى أن تصدر ES7، بل بإمكاننا إيجاد حلّ وسط لهذه المشكلة؛ لماذا نضطّر إلى تعقيد الأمور بالاستدعاءات الرّاجعة المتداخلة؟ ألا يتوفّر في اللّغة بنية برمجيّة تسمح بإيقاف شيفرتنا ريثما يتمّ أمر ما غير متزامن (الانتظار لإكمال طلب AJAX) ثمّ المتابعة بعد انتهاءه؟ يبدو هذا الحديث مألوفًا! نعلم حتى الآن أننا بحاجة لاستخدام مولّد، ولذلك سنحيط شيفرتنا بدالّة مولّد كخطوة أولى: var list = document.querySelector("#book-list"); function* displayUserProfile() { // شيفرتنا هنا } الآن نحتاج لتنفيذ طلب AJAX الأوّل والانتظار إلى انتهاءه قبل الانتقال إلى الطّلب الثّاني، نعلم أنّ yield توقف تنفيذ المولّد: var list = document.querySelector("#book-list"); function* displayUserProfile() { yield getJSON("http://reading-website.com/users/fwz.json"); // ... } عظيم! لكن كيف نُخبر المولّد بأنّ عليه متابعة التنفيذ؟ var list = document.querySelector("#book-list"); function* displayUserProfile() { yield getJSON("http://reading-website.com/users/fwz.json", resume); // ... } سنمرر دالة اسمها resume للدّالة getJSON، وهذه الدالة ستُستدعى عند انتهاء جلب جواب الطّلب الذي أرسلناه، وهي فرصتنا لإخبار المولّد بمتابعة التنفيذ... فكيف سيكون محتواها؟ var list = document.querySelector("#book-list"); var resume = function(err, response) { displayIterator.next(response); } function* displayUserProfile() { var user = yield getJSON("http://reading-website.com/users/fwz.json", resume); var num_books = user.books.length; var most_recent_book_id = user.books[num_books - 1]; var user_rating = yield getJSON("http://reading-website.com/users/fwz/ratings/" + most_recent_book_id + ".json", resume); var book = yield getJSON("http://reading-website.com/books/" + most_recent_book_id + ".json", resume); var fragment = document.createDocumentFragment(); var h2 = document.createElement("h2"); h2.innerText = user.full_name; var h3 = document.createElement("h3"); h3.innerText = "الكتب التي قرأها"; for (let book of books) { let li = document.createElement("li"); li.innerText = book.title + (book.id == most_recent_book_id ? " " + user_rating : ""); fragment.appendChild(li); } list.appendChild(fragment); } var displayIterator = displayUserProfile(); displayIterator.next(); حفظنا نسخة عن المكرّر في متغيّر ثم استدعينا وظيفته next() في دالّة المتابعة، ممرّرين لها جواب الطّلب ليمكننا تخزينه ضمن المتغيّر user. الدّالة resume تستطيع الوصول إلى displayIterator لأنّه يكون معرّفًا قبل استدعاءها حتمًا، ولا ننسَ أن تعريف المتغيّرات في JavaScript يخضع لعملية الرّفع إلى أعلى النّطاق (variable hoisting) ممّا يجعل المتغيّر displayIterator موجودًا (وإن كان بلا قيمة) منذ بداية تنفيذ الشيفرة. للتأكّد من فهم هذه الشيفرة، سنعيد تحليلها خطوة بخطوة: في طلب AJAX الأوّل تستدعى الدالة resume ويمرّر إليها جواب الطّلب (response)، الذي يمرّر بدوره إلى المُكرّر ليُحفظ في المتغيّر user الذي سيُستخدم في الخطوة التّالية للمولّد لإرسال الطّلب الثّاني. تُكرّر العمليّة ذاتها للطلبين الآخرين ثمّ تُعرض النتائج في الصّفحة. الفائدة التي جنيناها من استخدام المولّدات هي التخلّص من تعقيد الاستدعاءات الرّاجعة نهائيًّا وتحويل شيفرة غير متزامنة وجعلها تبدو وكأنّها متزامنة. ذكرنا القليل عن مكتبات مثل co وsuspend، لكنّها باختصار تعمل بطريقة مماثلة جدًا لمثالنا الأخير: var suspend = require('suspend'), resume = suspend.resume; suspend(function*() { var data = yield fs.readFile(__filename, 'utf8', resume()); console.log(data); })();هذه المكتبات خطوة نحو مستقبل JavaScript، الذي بدأ يتشكل مع مشروع الدّوال غير المتزامنة باستخدام الكلمتين المفتاحيتين الجديدتين async وawait اللّتان ستتوفّران في الإصدار السّابع وتستندان في عملهما إلى أرضيّة الوعود (Promises) الّتي تتوفّر اليوم في ES6. سيكون بإمكاننا كتابة هذه الشيفرة بدل الاعتماد على المولّدات: async function displayUserProfile() { var user = await getJSON("http://reading-website.com/users/fwz.json"); var num_books = user.books.length; var most_recent_book_id = user.books[num_books - 1]; var user_rating = await getJSON("http://reading-website.com/users/fwz/ratings/" + most_recent_book_id + ".json"); var book = await getJSON("http://reading-website.com/books/" + most_recent_book_id + ".json"); var fragment = document.createDocumentFragment(); var h2 = document.createElement("h2"); h2.innerText = user.full_name; var h3 = document.createElement("h2"); h2.innerText = "الكتب التي قرأها"; for (let book of books) { let li = document.createElement("li"); li.innerText = book.title + (book.id == most_recent_book_id ? " " + user_rating : ""); fragment.appendChild(li); } list.appendChild(fragment); }في هذا المثال يجب على getJSON أن تُعيد وعدًا Promise ليستطيع مُفسّر اللّغة انتظاره إلى أن يُحقّق (resolve) أو يُرفض (reject)، والقيمة الّتي تُحقّق تُحفظ ضمن المُتغيّر user، وأما عند رفض الوعد يُرمى خطأ (throw) يمكن تلقّيه (catch) كما في الشيفرة غير المتزامنة. مُعامِل البقيّة (Rest parameter) والناشرة (Spread)بعد كلّ هذا الكلام المُعقّد عن الأشياء غير المتزامنة التي نريد جعلها تبدو متزامنة وما إلى ذلك، سنختم الجزء الثّاني بفكرتين بسيطتين أُضيفتا إلى ECMAScript في الإصدار السّادس وتحلّان مشكلتين شائعتين في كثير من اللّغات البرمجيّة: أمّا الأولى فهي الحاجة إلى تنفيذ نصّ برمجيّ ضمن دالة على عدد غير معروف من المُعاملات، فلنفترض أنّ لدينا دالة تجمع عددين: function add(n1, n2) { return n1 + n2; } add(1) // 1 add(1, 2) // 3ونظرًا لكوننا مبرمجين أذكياء فقد قرّرنا جعل الدّالة تقبل أي عددين أو ثلاثة أو أكثر... لنجعلها تقبل عددًا لا نهائيًّا من الأعداد؛ في الإصدار الحالي سنلجأ إلى استخدام الكائن الخاصّ arguments المتوفّر ضمن نطاق كلّ دالّة تلقائيًا: function add() { return [].reduce.call(arguments, function(memo, n) { return memo + n; }); } add(1) // 1 add(1, 2) // 3 add(1, 2, 3) // 6 حسنًا لقد اضطررنا إلى "استعارة" دالة الاختزال من مصفوفة فارغة لتطبيقها على الكائن الخاص arguments الذي يُعتبر "شبيه مصفوفة" ولا يملك ما تمتلكه المصفوفة من دوالّ، لماذا لا يمكننا كتابة هذا فحسب: function add(...numbers) { return numbers.reduce(function(memo, n) { return memo + n; }); } add(1) // 1 add(1, 2) // 3 add(1, 2, 3) // 6 وأمّا الفكرة الثّانية فهي تكاد تكون عكس السّابقة، فإذا كانت الأولى تجمع بقيّة المعاملات في كائن مُفرَد، فإنّ هذه "تَنشر" محتويات المصفوفة إلى عناصرها المكوّنة لها، ماذا لو لم نكن أذكياء وعجزنا عن الإتيان بدالة تجمع عددًا غير منتهٍ من الأرقام: function addThreeNumbers(n1, n2, n3) { return n1 + n2 + n3; } var myNumbers = [1, 2, 3]; addThreeNumbers(...myNumbers); // 6 لاحظ أن صياغة النشر (Spread) تطابق تمامًا صياغة البقيّة (Rest)، والاختلاف في السّياق فقط. لاحظ أيضًا أنّ معامل البقيّة، وكما يوحي اسمه، يمكن استخدامه لتجميع ما تبقى من مُعاملات الدّالة فقط: function addThreeOrMoreNumbers(n1, n2, ...numbers) { return n1 + n2 + numbers.reduce(function(memo, n) { return memo + n; }); } addThreeOrMoreNumbers(1, 2, 3); // 6في الجزء القادم سنتعرف بمشيئة الله على الوحدات (Modules) التي تُعتبر طريقة جديدة لتنظيم الشفرة اُستلهمَت من عالم Node.js وrequire.js، وسُنلقي نظرة على الأصناف (Classes)، المكوّن البرمجيّ الذي وجد طريقه أخيرًا إلى JavaScriptّ! المصادرشبكة مطوّري موزيلّاGoing Async With ES6 GeneratorsReplacing callbacks with ES6 Generators Iterators gonna iterate1 نقطة