اذهب إلى المحتوى

إبراهيم البحيصي

الأعضاء
  • المساهمات

    43
  • تاريخ الانضمام

  • تاريخ آخر زيارة

  • عدد الأيام التي تصدر بها

    5

كل منشورات العضو إبراهيم البحيصي

  1. تأتي وحدة التسجيل (logging) مبدئيا توزيعة بايثون المعيارية وتقدم حلا لمتابعة الأحداث التي تحصُل أثناء عمل البرمجية. تستطيع إضافة استدعاءات التسجيل للشيفرة البرمجية الخاصة بك للإشارة لما تحقق من أحداث. تسمح لك وحدة التسجيل بإعداد كل من التسجيلات التشخيصية التي تسجل الأحداث المقترنة بعمليات التطبيق، بالإضافة إلى تسجيلات التدقيق التي تسجل الأحداث الخاصة بحركات المستخدم بهدف تحليلها. وحدة التسجيل هذه مختصة في حفظ السجلات في ملفات. لماذا نستخدم وحدة التسجيل؟ تحافظ وحدة التسجيل على سجل الأحداث التي تحدث خلال عمل البرنامج، مما يجعل من رؤية مخرجات البرنامج المتعلقة بأحداثه أمراً متاحاً. قد يكون استخدام الأمرprint أمرا مألوفا لك خلال الشيفرة البرمجية لفحص الأحداث. يقدم الأمر print طريقة بدائية لإجراء عملية التنقيح الخاصة بحل المشاكل خلال عمل البرمجية. بينما يعد تضمين تعليمات print خلال الشيفرة البرمجية طريقة لمعرفة مسار تنفيذ البرنامج والحالة الحالية له، إلا أن هذه الطريقة اُثبت أنها أقل قدرة على الصيانة من استخدام وحدة التسجيل في بايثون، وذلك للأسباب التالية: باستخدام تعليمات print يصبح من الصعب التفرقة بين مخرجات البرنامج الطبيعية وبين مخرجات التنقيح لتشابههما. عندما تنتشر تعليمات print خلال الشيفرة البرمجية، فإنه لا توجد طريقة سهلة لتعطيل التعليمات الخاصة بالتنقيح. من الصعب إزالة كافة تعليمات print عندما تنتهي من عملية التنقيح. لا توجد سجلات تشخيصية للأحداث. من الجيد البدء بالتعود على استخدام وحدة التسجيل المعيارية في بايثون خلال كتابة الشيفرة البرمجية بما أنها طريقة تتلاءم أكثر مع التطبيقات التي يكبر حجمها عن سكريبتات بايثون بسيطة، وكذلك بما أنها تقدم طريقة أفضل للتنقيح. طباعة رسائل التنقيح في وحدة التحكم إذا كنت متعودا على استخدام تعليمات print لرؤية ما يحدث في برنامجك خلال العمل، فمن المحتمل مثلا أنك تعودت على رؤية برنامج يُعرف صنفًاClass وينشئ منه عناصر كما في المثال التالي: class Pizza(): def __init__(self, name, price): self.name = name self.price = price print("Pizza created: {} (${})".format(self.name, self.price)) def make(self, quantity=1): print("Made {} {} pizza(s)".format(quantity, self.name)) def eat(self, quantity=1): print("Ate {} pizza(s)".format(quantity, self.name)) pizza_01 = Pizza("artichoke", 15) pizza_01.make() pizza_01.eat() pizza_02 = Pizza("margherita", 12) pizza_02.make(2) pizza_02.eat() توجد في الشيفرة السابقة الدالة__init__ التي تستخدم لتعريف خصائصname وprice للصنفPizza. كما تحتوي على الدالتين make لصنع البيتزا، وeat لأكلها وتأخذان المعطى quantity ذا القيمة الافتراضية 1. لنشغل البرنامج: >> python pizza.py وسنحصل على المخرج التالي: Output Pizza created: artichoke ($15) Made 1 artichoke pizza(s) Ate 1 pizza(s) Pizza created: margherita ($12) Made 2 margherita pizza(s) Ate 1 pizza(s) تسمح لنا تعليمات print برؤية أن البرنامج يعمل، ولكننا نستطيع أن نستخدم وحدة التسجيل لذات الغرض بدلا من ذلك. لنقم بإزالة تعليمات print من الشيفرة البرمجية، ونستورد الوحدة باستخدام الأمر import logging: import logging class Pizza(): def __init__(self, name, value): self.name = name self.value = value ... المستوى التلقائي للتسجيل في وحدة التسجيل هو مستوى التحذير (WARNING)، وهو مستوى فوق مستوى التنقيح (DEBUG). بما أننا سنستخدم الوحدة بغرض التنقيح في هذا المثال، سنحتاج الى تعديل إعدادات التسجيل لتصبح بمستوى التنقيح logging.DEBUG بحيث تعود معلومات التنقيح لنا من خلال لوحة التحكم. ونقوم بإعداد ذلك بإضافة ما يلي بعد تعليماتة الاستيراد: import logging logging.basicConfig(level=logging.DEBUG) class Pizza(): ... هذا المستوى المتمثل بlogging.DEBUG يشير لقيد رقمي قيمته 10. سنستبدل الآن جميع تعليمات print بتعليمات logging.debug()، (logging.DEBUG ثابت بينما logging.debug() دالة). نستطيع أن نمرر لهذه الدالة نفس المدخلات النصية لتعليماتة print كما هو موجود بالأسفل: import logging logging.basicConfig(level=logging.DEBUG) class Pizza(): def __init__(self, name, price): self.name = name self.price = price logging.debug("Pizza created: {} (${})".format(self.name, self.price)) def make(self, quantity=1): logging.debug("Made {} {} pizza(s)".format(quantity, self.name)) def eat(self, quantity=1): logging.debug("Ate {} pizza(s)".format(quantity, self.name)) pizza_01 = Pizza("artichoke", 15) pizza_01.make() pizza_01.eat() pizza_02 = Pizza("margherita", 12) pizza_02.make(2) pizza_02.eat() لهذا الحد، نستطيع تشغيل البرنامج عبر تنفيذ الأمر python pizza.py وسنحصل على المخرج التالي: Output DEBUG:root:Pizza created: artichoke ($15) DEBUG:root:Made 1 artichoke pizza(s) DEBUG:root:Ate 1 pizza(s) DEBUG:root:Pizza created: margherita ($12) DEBUG:root:Made 2 margherita pizza(s) DEBUG:root:Ate 1 pizza(s) لاحظ أن مستوى التسجيل في المخرج السابق هو DEBUG بالإضافة لكلمة root والتي تشير لمستوى المُسجل (logger) الذي يتم استخدامه. يعني ما سبق أن وحدة التسجيل logging من الممكن أن يتم استخدامها لإعداد أكثر من مُسجل بأسماء مختلفة. فمثلا، نستطيع إنشاء مسجلين باسمين مختلفين ومخرجات مختلفة كما هو موضح بالأسفل: logger1 = logging.getLogger("module_1") logger2 = logging.getLogger("module_2") logger1.debug("Module 1 debugger") logger2.debug("Module 2 debugger") Output DEBUG:module_1:Module 1 debugger DEBUG:module_2:Module 2 debugger بعد أن أصبحت لدينا المعرفة اللازمة لكيفية استخدام الوحدة logging لطباعة الرسائل على وحدة التحكم، دعونا نكمل شرح الوحدة ونتعرف على كيفية استخدام الوحدة في طباعة الرسائل إلى ملف خارجي. التسجيل في ملف الغرض الأساسي للتسجيل هو حفظ البيانات في ملف وليس إظهار معلومات التسجيل على وحدة التحكم. يتيح لك التسجيل في ملف حفظ بيانات التسجيل مع مرور الوقت واستخدامها في عملية التحليل والمتابعة ولتحديد ما تحتاجه من تغيير على الشيفرة البرمجية. لجعل عملية التسجيل تحفظ التسجيلات في ملف، علينا أن نعدّل logging.basicConfig() بحيث تحتوي على معطى لاسم الملف (filename)، وليكن مثلا test.log: import logging logging.basicConfig(filename="test.log", level=logging.DEBUG) class Pizza(): def __init__(self, name, price): self.name = name self.price = price logging.debug("Pizza created: {} (${})".format(self.name, self.price)) def make(self, quantity=1): logging.debug("Made {} {} pizza(s)".format(quantity, self.name)) def eat(self, quantity=1): logging.debug("Ate {} pizza(s)".format(quantity, self.name)) pizza_01 = Pizza("artichoke", 15) pizza_01.make() pizza_01.eat() pizza_02 = Pizza("margherita", 12) pizza_02.make(2) pizza_02.eat() الشيفرة البرمجية هنا هي نفسها الموجودة سابقا عدا أننا أضفنا اسم الملف الذي سنقوم بحفظ التسجيلات فيه. بمجرد تشغيلنا للشيفرة السابقة، سنجد في نفس المسار الملف test.log. لنفتحه باستخدام محرر النصوص nano (أو أي محرر نصوص من اختيارك): $ nano test.log وسيكون محتويات الملف كالتالي: DEBUG:root:Pizza created: artichoke ($15) DEBUG:root:Made 1 artichoke pizza(s) DEBUG:root:Ate 1 pizza(s) DEBUG:root:Pizza created: margherita ($12) DEBUG:root:Made 2 margherita pizza(s) DEBUG:root:Ate 1 pizza(s) المخرج السابق هو نفسه الذي حصلنا عليه في القسم السابق من المقال، غير أنه الآن في ملف باسم test.log وليس على الطرفية. لنغلق المحرر، ونجر بعض التعديلات التالية على المتغيرين pizza_01 و pizza_02: ... # Modify the parameters of the pizza_01 object pizza_01 = Pizza("Sicilian", 18) pizza_01.make(5) pizza_01.eat(4) # Modify the parameters of the pizza_02 object pizza_02 = Pizza("quattro formaggi", 16) pizza_02.make(2) pizza_02.eat(2) عند تنفيذ الشيفرة بعد حفظ التعديلات، ستُضاف التسجيلات الجديدة للملف وسيكون محتواه كالتالي: DEBUG:root:Pizza created: artichoke ($15) DEBUG:root:Made 1 artichoke pizza(s) DEBUG:root:Ate 1 pizza(s) DEBUG:root:Pizza created: margherita ($12) DEBUG:root:Made 2 margherita pizza(s) DEBUG:root:Ate 1 pizza(s) DEBUG:root:Pizza created: Sicilian ($18) DEBUG:root:Made 5 Sicilian pizza(s) DEBUG:root:Ate 4 pizza(s) DEBUG:root:Pizza created: quattro formaggi ($16) DEBUG:root:Made 2 quattro formaggi pizza(s) DEBUG:root:Ate 2 pizza(s) تُعد البيانات الموجودة في الملف مفيدة، ولكننا نستطيع جعلها أكثر إعلاماً بإضافة بعض الإعدادات. بشكل أساسي، فإننا نريد أن نجعل السجلات مفصلة أكثر بإضافة الوقت الذي أنشئ السجل فيه. نستطيع إضافة المعطى المسمى format ونضيف له النص %(asctime)s الذي يشير للوقت، كذلك، للإبقاء على ظهور مستوى التسجيل في السجلات، لابد أن نضيف النص %(levelname)s بالإضافة للسجل نفسه %(message)s. لابد من الفصل بين كل خيار في المعطى format بالعلامة : كما هو موضح بالأسفل: import logging logging.basicConfig( filename="test.log", level=logging.DEBUG, format="%(asctime)s:%(levelname)s:%(message)s" ) ....... عندما ننفذ الشيفرة السابقة، سنحصل على تسجيلات جديدة في ملف test.log تتضمن الوقت الذي أنشئ فيه التسجيل بالإضافة لمستوى التسجيل ورسالة التسجيل: Output DEBUG:root:Pizza created: Sicilian ($18) DEBUG:root:Made 5 Sicilian pizza(s) DEBUG:root:Ate 4 pizza(s) DEBUG:root:Pizza created: quattro formaggi ($16) DEBUG:root:Made 2 quattro formaggi pizza(s) DEBUG:root:Ate 2 pizza(s) 2017-05-01 16:28:54,593:DEBUG:Pizza created: Sicilian ($18) 2017-05-01 16:28:54,593:DEBUG:Made 5 Sicilian pizza(s) 2017-05-01 16:28:54,593:DEBUG:Ate 4 pizza(s) 2017-05-01 16:28:54,593:DEBUG:Pizza created: quattro formaggi ($16) 2017-05-01 16:28:54,593:DEBUG:Made 2 quattro formaggi pizza(s) 2017-05-01 16:28:54,593:DEBUG:Ate 2 pizza(s) تبعا لاحتياجاتك، من الممكن أن تضيف إعدادات أخرى للمسجل بحيث تجعل التسجيلات التي يتم حفظها في الملف مرتبطة بك نوعا ما. التنقيح بواسطة التسجيل في ملفات خارجية يتيح لك فهما شاملا لبرنامج بايثون مع مرور الوقت، معطيا الفرصة لحل المشاكل التي تظهر وتغيير ما تحتاج تغييره في الشيفرة البرمجية استنادا لما لديك من بيانات تسجيلات تاريخية وأحداث وحركات تمت خلال عمل البرنامج. جدول بمستويات التسجيل تستطيع نسب مستوى الأهمية للحدث الذي يتم تسجيله بواسطة المُسجل وذلك بإضافة مستوى الخطورة (Severity Level). مستويات الخطورة موضحة في الجدول الذي بالأسفل. تتمثل مستويات التسجيل تقنيا بأرقام (ثوابت)، بفرق قيمة بين كل مستوى ب 10، تبدأ من المستوىNOTEST ذي القيمة 0. تستطيع أن تُعرف مستويات خاصة بك مرتبطة بالمستويات المعرفة مسبقا. إذا عَرَّفْتَ مستوى بنفس القيمة الرقمية، فإنك تستبدل اسم المستوى المرتبط بتلك القيمة. المستوى القيمة الرقمية الدالة الاستخدام CRITICAL 50 ()logging.critical اظهار الأخطاء الخطيرة، البرنامج قد لا يستمر بالعمل ERROR 40 ()logging.error إظهار مشكلة خطيرة WARNING 30 ()logging.warning الإشارة لحدث غير متوقع حصل أو قد يصحل INFO 20 ()logging.info الإشارة أن الحدث حصل كما هو متوقع DEBUG 10 ()logging.debug فحص المشاكل، وإظهار معلومات تفصيلية خاتمة الوحدة logging هي وحدة ضمن التوزيعة المعيارية لبايثون، وتقدم حلا لمتابعة الأحداث التي تحدث خلال عمل البرمجية مع إمكانية تسجيل هذه الأحداث في ملفات خارجية أو إظهارها على الطرفية. وهذا يتيح لك فهما شاملا لبرنامج بايثون مع مرور الوقت. هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة – بتصرّف – للمقال How To Use Logging in Python 3 لصاحبته Lisa Tagliaferri. اقرأ أيضًا المقالة التالية: كيفية ترحيل شيفرة بايثون 2 إلى بايثون 3 المقالة السابقة: كيفية تنقيح شيفرات بايثون من سطر الأوامر التفاعلي المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون
  2. نعني بالتنقيح Debugging في مجال تطوير البرمجيات، عملية إيجاد وحل المشاكل التي تمنع البرمجية من العمل على نحو سليم. يُقدم مُنقح بايثون بيئة تنقيح لبرامج بايثون، ويدعم إعداد نقاط الفصل (Breakpoints)، التدرج خلال الشيفرة البرمجية (Stepping) سطرًا بسطر والعديد من المزايا. التفاعل مع مُنقح بايثون يأتي مُنقح بايثون مرفقًا مع توزيعة بايثون المعيارية على هيئة وحدة بايثون باسم pdb، ومن الممكن أن يُستخدم في الشيفرة البرمجية كعنصر من الصنف Pdb، ولمزيد من المعلومات تستطيع قراءة التوثيق الرسمي للوحدة pdb. سوف نبدأ العمل باستخدام برنامج صغير يحتوي على متغيرين ودالة تحتوي على حلقة تكرار بالإضافة لسطر استدعاء الدالة من خلال التركيبة if __name__ == '__main__':. num_list = [500, 600, 700] alpha_list = ['x', 'y', 'z'] def nested_loop(): for number in num_list: print(number) for letter in alpha_list: print(letter) if __name__ == '__main__': nested_loop() نستطيع الآن تشغيل البرنامج السابق من خلال مُنقح بايثون في الطرفية باستخدام الأمر التالي: python -m pdb looping.py يستورد الخيار m- في الأمر أي وحدة بايثون وتشغيلها كبرنامج. في هذه الحالة نقوم باستيراد الوحدة pdb وتمريرها كما هو مكتوب في الأمر. عند تنفيذ الأمر السابق سوف تحصل على المُخرج التالي: > /Users/sammy/looping.py(1)<module>() -> num_list = [500, 600, 700] (Pdb) يحتوي السطر الأول في المُخرج على اسم الملف الحالي الذي نقوم بتنقيحه – بالمسار الكامل -، ومن ثم يوجد رقم سطر الشيفرة التي يقف عليها مؤشر المُنقح (في هذه الحالة 1، ولكن في حالة وجود تعليق أو سطر غير تنفيذي سيكون الرقم أكبر من ذلك). السطر الثاني عبارة عن سطر الشيفرة الذي يتم تنفيذه. يُقدم مُنقح بايثون وحدة تحكم (Console) تفاعلية لإجراء عملية التنقيح وتستطيع استخدام أمر المساعدةhelp للتعرف على الأوامر المتاحة في المُنقح، وأيضا من الممكن أن تستخدم صيغة المساعدة مع الأمرhelp command للتعرف أكثر على تفاصيل أمر معين. سيعيد المُنقح عمله مرة أخرى تلقائيا عندما يصل لنهاية البرنامج. إذا أردت الخروج من وحدة تحكم المُنقح، أدخل الأمرquit أوexit. إذا أردت إعادة تشغيل عملية التنقيح مرة أخرى وفي أي مكان من البرنامج، أدخل الأمرrun. استخدام المُنقح للتدرج خلال البرنامج عند العمل على تنقيح البرامج باستخدام مُنقح بايثون، فإنك غالبا ستستخدم أوامر step، list وnext للمرور على الشيفرة البرمجية. خلال هذا الجزء من المقال سنتناول هذه الأوامر. من خلال نافذة الأوامر، نستطيع إدخال الأمرlist للحصول على السياق المحيط للسطر الحالي. فمثلا، من السطر الأول في البرنامج looping.py الذي عرضناه في الأعلى — num_list = [500, 600, 700] — سيكون تنفيذ الأمرlist معه بالشكل التالي: (Pdb) list 1 -> num_list = [500, 600, 700] 2 alpha_list = ['x', 'y', 'z'] 3 4 5 def nested_loop(): 6 for number in num_list: 7 print(number) 8 for letter in alpha_list: 9 print(letter) 10 11 if __name__ == '__main__': (Pdb) السطر الحالي يشار إليه بالعلامة -> في بدايته وهو في حالتنا هذه السطر الأول من البرنامج. بحكم أن البرنامج الذي نستخدمه هنا صغير نسبيا، فإننا تقريبا نحصل على كافة الأسطر في البرنامج عند استخدامالأمر list. بدون تزويد معطيات مع الأمر list، نحصل من استخدام الأمر على 11 سطراً من الشيفرة البرمجية محيطة بالسطر الحالي في المُنقح، ولكننا نستطيع تحديد الأسطر التي نريد عرضها بالشكل التالي: (Pdb) list 3, 7 3 4 5 def nested_loop(): 6 for number in num_list: 7 print(number) (Pdb) في الأمر السابق قمنا بعرض الأسطر 3-7 باستخدام الأمر list 3,7. للمرور خلال البرنامج سطرا بسطر، نستخدم الأمرstep أو next. (Pdb) step > /Users/sammy/looping.py(2)<module>() -> alpha_list = ['x', 'y', 'z'] (Pdb) (Pdb) next > /Users/sammy/looping.py(2)<module>() -> alpha_list = ['x', 'y', 'z'] (Pdb) الفرق بين الأمرstep والأمرnext أن الأمرstep سوف يتوقف مع استدعاء دالة، بينما الأمر next يُنفذ الدالة عند استدعائها. يتجلى الفرق بين الأمرين عند التعامل مع الدوال. في مثالنا المستخدم، سيقوم الأمرstep بالتعداد خلال حلقات التكرار بمجرد الدخول في استدعاء الدالة nested_loop() بحيث يظهر تماما ما تقوم به حلقة التكرار من طباعة رقم ومن ثم الدخول في حلقة تكرار طباعة الحروف المقترنة بالرقم ومن ثم العودة لطباعة رقم جديد وهكذا. (Pdb) step > /Users/sammy/looping.py(5)<module>() -> def nested_loop(): (Pdb) step > /Users/sammy/looping.py(11)<module>() -> if __name__ == '__main__': (Pdb) step > /Users/sammy/looping.py(12)<module>() -> nested_loop() (Pdb) step --Call-- > /Users/sammy/looping.py(5)nested_loop() -> def nested_loop(): (Pdb) step > /Users/sammy/looping.py(6)nested_loop() -> for number in num_list: (Pdb) step > /Users/sammy/looping.py(7)nested_loop() -> print(number) (Pdb) step 500 > /Users/sammy/looping.py(8)nested_loop() -> for letter in alpha_list: (Pdb) step > /Users/sammy/looping.py(9)nested_loop() -> print(letter) (Pdb) step x > /Users/sammy/looping.py(8)nested_loop() -> for letter in alpha_list: (Pdb) step > /Users/sammy/looping.py(9)nested_loop() -> print(letter) (Pdb) step y > /Users/sammy/looping.py(8)nested_loop() -> for letter in alpha_list: (Pdb) الأمرnext، بدلا من ذلك، سيستدعي الدالة دون الدخول في خطوات تنفيذها خطوة-بخطوة. لنخرج من المُنقح ونعد تشغيله مرة أخرى: python -m pdb looping.py الآن لنستخدم الأمرnext: (Pdb) next > /Users/sammy/looping.py(5)<module>() -> def nested_loop(): (Pdb) next > /Users/sammy/looping.py(11)<module>() -> if __name__ == '__main__': (Pdb) next > /Users/sammy/looping.py(12)<module>() -> nested_loop() (Pdb) next 500 x y z 600 x y z 700 x y z --Return-- > /Users/sammy/looping.py(12)<module>()->None -> nested_loop() (Pdb) أثناء عملك في تنقيح الشيفرة البرمجية الخاصة بك، قد تريد فحص قيمة متغير، وهو ما تستطيع تنفيذه باستخدام الأمرpp والذي يقوم بطباعة القيمة باستخدام وحدة pprint: (Pdb) pp num_list [500, 600, 700] (Pdb) أغلب الأوامر في مُنقح pdb لديها اختصارات، فمثلا، الأمرstep له اختصار هوs ولأمرnext يوجدn. يسرد أمر المساعدة help كافة الاختصارات لكل الأوامر. بالإضافة لذلك، تستطيع استدعاء الأمر الأخير بالضغط على زر الإدخال (Enter) في لوحة المفاتيح. نقاط الفصل من المؤكد أنك ستعمل على برامج أكبر من المثال المستخدم هنا، لذلك، غالبا ستحتاج أن تتوقف عند وظيفة معينة أو سطر محدد أثناء تنقيحك للشيفرة البرمجية، وذلك بدلا من المرور على كافة أسطر البرنامج. ستتمكن باستخدام الأمرbreak لإعداد نقاط الفصل من تشغيل البرنامج حتى نقطة معينة مسبقا فيه تـسمى نقطة فصل Break point. عندما تضيف نقطة فصل، سيعطيها المُنقح رقما خاصا بها. الأرقام المُعطاة لنقاط الفصل تكون أرقاماً متعاقبة وتبدأ من 1، وتستطيع الإشارة لنقاط الفصل باستخدام أرقامها أثناء عملك في المُنقح. تُضاف نقاط الفصل في أسطر معينة باستخدام الصيغة التالية <program_file>:<line_number> كما هو موضح بالأسفل: (Pdb) break looping.py:5 Breakpoint 1 at /Users/sammy/looping.py:5 (Pdb) اكتب الأمرclear ومن ثم y لإزالة نقاط الفصل الحالية. تستطيع بعد ذلك إضافة نقطة فصل في مكان تعريف الدالة: (Pdb) break looping.nested_loop Breakpoint 1 at /Users/sammy/looping.py:5 (Pdb) تستطيع كذلك إضافة شرط لنقطة الفصل: (Pdb) break looping.py:7, number > 500 Breakpoint 1 at /Users/sammy/looping.py:7 (Pdb) لو أدخلنا الآن الأمرcontinue، فإن البرنامج سيتوقف عندما تكون قيمة المتغيرnumber أكبر من 500. (Pdb) continue 500 x y z > /Users/sammy/looping.py(7)nested_loop() -> print(number) (Pdb) للحصول على قائمة بنقاط الفصل المُعدة حاليا للعمل، استخدم الأمرbreak فقط (دون معطيات) وسوف تحصل على معلومات نقاط الفصل التي قمت بإعدادها. (Pdb) break Num Type Disp Enb Where 1 breakpoint keep yes at /Users/sammy/looping.py:7 stop only if number > 500 breakpoint already hit 2 times (Pdb) نستطيع تعطيل نقطة فصل من البرنامج باستخدام الأمرdisable ومن ثم ندخل رقم نقطة الفصل. في هذه الجلسة (المثال المشروح) قمنا بإضافة نقطة فصل أخرى ومن ثم عطلنا النقطة الأولى: Pdb) break looping.py:11 Breakpoint 2 at /Users/sammy/looping.py:11 (Pdb) disable 1 Disabled breakpoint 1 at /Users/sammy/looping.py:7 (Pdb) break Num Type Disp Enb Where 1 breakpoint keep no at /Users/sammy/looping.py:7 stop only if number > 500 breakpoint already hit 2 times 2 breakpoint keep yes at /Users/sammy/looping.py:11 (Pdb) لتفعيل نقطة الفصل نستخدم الأمرenable، ولإزالة نقطة الفصل نستخدم الأمرclear: (Pdb) enable 1 Enabled breakpoint 1 at /Users/sammy/looping.py:7 (Pdb) clear 2 Deleted breakpoint 2 at /Users/sammy/looping.py:11 (Pdb) تقدّم لك نقاط الفصل في pdb قدرة كبيرة في التحكم، فمثلا، من الإضافات أنك تستطيع تجاهل نقطة الفصل خلال الدورة الحالية من البرنامج باستخدام الأمرignore أو تنفيذ حدث معين عند الوصول لنقطة فصل معينة باستخدام الأمرcommand. تستطيع كذلك إضافة نقاط فصل مؤقتة بواسطة الأمرtbreak، بحيث يقوم المُنقح بحذفها تلقائيا عند الوصول إليها وتنفيذها للمرة الأولى (لإضافة نقطة فصل مؤقتة في السطر رقم 3، ندخل الأمرtbreak 3 ). تضمين المُنقح في الشيفرة البرمجية تستطيع تشغيل جلسة تنقيح في الشيفرة البرمجية مباشرة وذلك باستيراد الوحدة pdb إضافة الدالة pdb.set_trace قبل السطر الذي تريد أن تبدأ الجلسة من عنده. في مثالنا المستخدم خلال هذا المقال، سوف نقوم باستيراد الوحدة pdb واضافة الدالة المذكورة قبل البدء بحلقة التكرار الداخلية في الدالة nested_loop: # Import pdb module import pdb num_list = [500, 600, 700] alpha_list = ['x', 'y', 'z'] def nested_loop(): for number in num_list: print(number) # Trigger debugger at this line pdb.set_trace() for letter in alpha_list: print(letter) if __name__ == '__main__': nested_loop() بإضافة المُنقح في الشيفرة البرمجية الخاصة بك، فأنت لست بحاجة لتشغيل الشيفرة بطريقة معينة أو أن تتذكر إعدادات نقاط الفصل، وستتمكن من تشغيل البرنامج بطريقة عادية وتفعيل المُنقح من خلال التنفيذ. تعديل مسار عمل البرنامج يتيح لك مُنقح بايثون تغيير مسار التنفيذ (Execution Flow) خلال زمن التنفيذ باستخدام الأمرjump، وهذا يعني أنك تستطيع أن تقفز إلى الأمام خلال البرنامج لمنع بعض الشيفرة البرمجية من التنفيذ أو العودة لتنفيذ جزء من الشيفرة مرة أخرى. سوف نعمل على شرح هذه النقطة باستخدام برنامج صغير يقوم بإنشاء متغير من نوع قائمة (List) من الأحرف النصية موجودة ضمن المتغير النصي sammy = "sammy" : def print_sammy(): sammy_list = [] sammy = "sammy" for letter in sammy: sammy_list.append(letter) print(sammy_list) if __name__ == "__main__": print_sammy() إذا قمنا بتشغيل البرنامج بطريقة عادية باستخدام python letters_list.py فإننا سوف نحصل على النتيجة التالية: Output ['s'] ['s', 'a'] ['s', 'a', 'm'] ['s', 'a', 'm', 'm'] ['s', 'a', 'm', 'm', 'y'] لنستعرض كيفية استخدام مُنقح بايثون في تغيير مسار عمل البرنامج السابق بحيث نقوم بالقفز قُدما خلال الشيفرة البرمجية للبرنامج أثناء التشغيل بعد الدورة الأولى من حلقة التكرار: python -m pdb letter_list.py > /Users/sammy/letter_list.py(1)<module>() -> def print_sammy(): (Pdb) list 1 -> def print_sammy(): 2 sammy_list = [] 3 sammy = "sammy" 4 for letter in sammy: 5 sammy_list.append(letter) 6 print(sammy_list) 7 8 if __name__ == "__main__": 9 print_sammy() 10 11 (Pdb) break 5 Breakpoint 1 at /Users/sammy/letter_list.py:5 (Pdb) continue > /Users/sammy/letter_list.py(5)print_sammy() -> sammy_list.append(letter) (Pdb) pp letter 's' (Pdb) continue ['s'] > /Users/sammy/letter_list.py(5)print_sammy() -> sammy_list.append(letter) (Pdb) jump 6 > /Users/sammy/letter_list.py(6)print_sammy() -> print(sammy_list) (Pdb) pp letter 'a' (Pdb) disable 1 Disabled breakpoint 1 at /Users/sammy/letter_list.py:5 (Pdb) continue ['s'] ['s', 'm'] ['s', 'm', 'm'] ['s', 'm', 'm', 'y'] قمنا خلال جلسة التنقيح السابقة بوضع نقطة فصل عند السطر رقم 5 لمنع الشيفرة البرمجية من الاستمرار، ومن ثم قمنا بطباعة بعض الأحرف والاستمرار من خلال الأمرcontinue لإظهار ماذا يحدث. ثم استخدمنا الأمرjump لتجاهل السطر 6. عند هذه النقطة، المتغيرletter يساوي القيمة a ولكننا تجاهلنا السطر الذي يقوم بإضافة هذه القيمة للقائمة. بعد ذلك قمنا بتعطيل نقطة الفصل وتركنا البرنامج يستمر في التنفيذ. في النهاية، فإن الحرفa لم يُضف للقائمة sammy_list. لنعد تشغيل جلسة التنقيح، ونستخدم المُنقح في العودة للخلف خلال التشغيل بهدف إعادة تنفيذ جملة الإضافة للقائمة sammy_list والتي تم تنفيذها خلال التكرار الأول من حلقة التكرار: > /Users/sammy/letter_list.py(1)<module>() -> def print_sammy(): (Pdb) list 1 -> def print_sammy(): 2 sammy_list = [] 3 sammy = "sammy" 4 for letter in sammy: 5 sammy_list.append(letter) 6 print(sammy_list) 7 8 if __name__ == "__main__": 9 print_sammy() 10 11 (Pdb) break 6 Breakpoint 1 at /Users/sammy/letter_list.py:6 (Pdb) continue > /Users/sammy/letter_list.py(6)print_sammy() -> print(sammy_list) (Pdb) pp letter 's' (Pdb) jump 5 > /Users/sammy/letter_list.py(5)print_sammy() -> sammy_list.append(letter) (Pdb) continue > /Users/sammy/letter_list.py(6)print_sammy() -> print(sammy_list) (Pdb) pp letter 's' (Pdb) disable 1 Disabled breakpoint 1 at /Users/sammy/letter_list.py:6 (Pdb) continue ['s', 's'] ['s', 's', 'a'] ['s', 's', 'a', 'm'] ['s', 's', 'a', 'm', 'm'] ['s', 's', 'a', 'm', 'm', 'y'] خلال جلسة التنقيح السابقة، قمنا بإضافة نقطة فصل عند السطر 6، وقمنا بالعودة للخلف للسطر 5 بعد الاستمرار.طبعنا ما تحتويه القائمة خلال التنفيذ وذلك لكي نظهر أن الحرف s قد أٌضيف مرتين. وبعد ذلك قمنا بتعطيل نقطة الفصل وجعلنا البرنامج يستمر في التنفيذ. النتيجة الظاهرة توضح أن الحرف s أضيف مرتين في بداية القائمة sammy_list. يمنع المنقّح بعض استخدامات الأمرjump ، وخاصة عندما يُعدَّل مسار البرنامج للأمام أو الخلفمن خلال جمل لم تُعرَّف. فمثلا، لا تستطيع القفز إلى دوال قبل أن تعرّف المعطيات الخاصة بها، وكذلك لا تستطيع الدخول في وسط جملة try:except أو الخروج من كتلة الشيفرة البرمجية لـ finally. ملخص ما نستطيع ذكره عن الأمرjump، أنك من خلال استخدامه في مُنقح بايثون تستطيع تغيير مسار التنفيذ خلال التنقيح لمعرفة هل هذا التغيير مفيد ضمن ظروف ومعطيات محددة أم لا، وكذلك يساعدك في فهم أصل أو مكان الخلل والمشاكل التي تظهر خلال تنفيذ البرنامج. جدول أوامر المنقح pdb هنا نلخص لكم أوامر المُنقح pdb مع شرح بسيط عما يقدمه كل أمر لكي يساعدك على التذكر خلال تنقيح البرامج على بايثون: الأمر الاختصار العمل help h يقدم قائمة بالأوامر المتاحة أو شرحا لأمر معين jump j تحديد السطر القادم للتنفيذ list l طباعة السياق (ما حول السطر الحالي) للتعرف أكثر على محيط التنفيذ الحالي next n الاستمرار في التنفيذ حتى السطر القادم الذي تصل فيه الدالة الحالية أو تعود step s تنفيذ السطر الحالي والتوقف عند أول فرصة متاحة pp pp طباعة متغير أو تعبير معين quit أو exit q الخروج من البرنامج return r الاستمرار في التنفيذ حتى تعود الدالة الحالية تستطيع أن تقرأ أكثر عن الأوامر وكيفية العمل مع المنقح من خلال الاطلاع على التوثيق الرسمي في بايثون. خاتمة يعد تنقيح البرمجيات خطوة مهمة في أي مشروع لبناء وتطوير برمجية. يقدم مُنقح بايثون بيئة تفاعلية تستطيع الاستفادة منها في تنقيح أي برنامج بايثون. الأوامر المتاحة خلال المنقح تتيح لك وقف البرنامج الخاص بك، والاطلاع على قيم متغيراته خلال التنفيذ وتعديلها إذا رغبت، وكذلك التحكم في مسارات التنفيذ وغيره من الخصائص التي تساعدك في فهم ما يقوم به برنامجك بشكل كامل ولكي تضع يدك على المشاكل والقضايا التي تظهر خلال التنفيذ. هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة – بتصرّف – للمقال How To Use the Python Debugger لصاحبته Lisa Tagliaferri. اقرأ أيضًا المقالة التالية: كيفية تنقيح شيفرات بايثون من سطر الأوامر التفاعلي المقالة السابقة: كيفية تطبيق التعددية الشكلية (Polymorphism) على الأصناف المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون
  3. إذا كانت وظيفتك هي إنشاء تصميمات ويب عالية الجودة، عليك أن تدرك أنّ مساهمتك في العملية التسويقية مهمة جدًا، لذلك، فإن قضاء بعض الوقت لصقل معرفتك بتعلم مصطلحات التسويق المشهورة سوف يمنحك فهمًا أكثر لعملك من خلال سياق أكبر في التسويق. كميزة إضافية، من الممكن تعلم بعض المصطلحات التي تستطيع استخدامها في نقاشاتك مع المُشغلين والعملاء وإثارة إعجابهم بمعرفتك وتمكنك في هذا المجال. هذه بعض المصطلحات التي تحتاج للبدء بالتعرف عليها: اختبار أ/ب (A/B Testing) هو نسخة المسوق في المنهج العلمي. عندما تكون هناك مشكلة ما في تصميم موقع، بدلًا من إجراء تعديل شامل على التصميم، او تغيير المحتوى والخطوط أو تغيير الألوان بشكل كامل، يعمد المسوقون لاستخدام اختبار أ/ب كأداة لتجريب نُسخ بديلة للموقع وعادةً ما يتم تغيير عنصر واحد فقط. التحليلات (Analytics) يشير مصطلح التحليلات ببساطة الى بيانات كأرقام. معدل فتح البريد الإلكتروني، وعدد زائري مدونة ما، وعدد النقرات المدفوعة، وعدد المشاهدات وغيره من الأرقام، كل ما سبق يُمثّل مجموعة متنوعة من البيانات التي يُمكن استخلاصها من تحليلات عملية التسويق. B2B يُشير مصطلح B2B الى مفهوم "من قطاع أعمال إلى قطاع أعمال" ونقصد به هنا الطرفان المباشران لعملية التسويق، ويُمثل الطرفان قطاعي أعمال مختلفان بحيث يكون جمهور قطاع أعمال ما هو قطاع أعمال أخر. B2C يشير مصطلح B2C الى مفهوم "من قطاع أعمال إلى المستهلك" وفيه يستهدف قطاع أعمال ما جمهورًا من المستهلكين العاديين (ليسوا قطاع أعمال). معدل الارتداد (Bounce Rate) معدل الارتداد هو نسبة الزائرين الذين يغادرون موقعك بمجرد الدخول لصفحة واحدة. هذه الإحصائية يتم متابعتها لتحديد الجزء الذي يفقد فيه الزائر الاهتمام بالموقع. ستجد هنا دليل يُساعدك في تحسين معدل الارتداد لموقعك. هوية العلامة التجارية (Brand Identity) تتضمن هوية قطاع أعمال ما كل شيء يربطه الناس بالعلامة التجارية الخاصة بهذا القطاع. من الممكن أن تتضمن هوية قطاع الأعمال كل من اسم الشركة، شعار الشركة، ألوان العلامة التجارية، نوع خطوط الطباعة، الصور، محتوى صوتي وهكذا. شخصية المشتري (Buyer Persona) ينشئ قطاع الأعمال هوية جمهوره من خلال بناء ما يًسمى بـ "شخصية المشتري" وتُبنى معالم هذه الشخصية باستخدام المعلومات الجغرافية والسكانية أو تاريخ سلوك المستخدمين وغيره من المعلومات، وتمنح هذه الشخصية الجمهور القدرة على تشكيل محتواه، وتحديد جهود التسويق بالطريقة التي يرغبها. دعوة إلى الإجراء (Call-to-Action) كل صفحة في الموقع لابد أن يكون لها هدف واضح لجمهور الأعمال وكل جزء من التسويق لابد أن يكون مضمونًا. دعوة إلى الإجراء يمثل رسالة تقوم بتوجيه الزائر للقيام بالخطوة التالية لإكمال تحقيق الهدف المُراد منه. المحتوى (Content) المحتوى هو كل شيء يحتويه الموقع من نصوص، وصور، ومقاطع فيديو، ونداء إجراء، ومكونات القائمة الجانبية، والحركات والأيقونات ...إلخ. التحويل (Conversion) التحويل هو غاية التسويق وهدفه النهائي، ويتغير شكل التحويل من موقع لأخر، فالتحويل في مواقع العضويات يتحقق باشتراك الزائر في العضوية. في مواقع التجارة الإلكترونية، التحويل هو شراء سلعة أو منتج. للمدونات، التحويل هو الاشتراك في المدونة ومتابعة الخلاصات فيها. الاشتراك (Engagement) يشير مصطلح الاشتراك الى أي تفاعل بين قطاع أعمال ما والمستخدم النهائي لديه. يستخدم المسوقون هذا المصطلح للإشارة عادةً إلى التفاعل الحاصل في المنصات الاجتماعية والذي يحدث بأشكال متعددة مثل الإعجاب، المشاركة والتعليق. محتوى متجدد (Evergreen Content) يكون المحتوى متجددًا عندما لا يتقيد بوقت ما. وبكلمات أخرى، يبقى هذا المحتوى قيمًا وذو علاقة بغض النظر متى يقوم شخص رؤيته. الارتباك والمقاومة (Friction) تحدث حالة الارتباك ومقاومة التحويل في عملية التسويق عند إزعاج المستخدم وإرباكه أثناء محاولته إتمام إجراء معين. التسويق الضمني (Inbound Marketing) يتطلب التسويق الضمني تكتيكات خفية، وغالبًا يركز على التجربة وشرح الخبرة والمعرفة أكثر من محاولة البيع. الاصطياد (Jacking) الاصطياد (مثل الاختطاف) يحدث عند استخدام فكرة رائجة لصالح قطاع أعمال ولغرض خاص. من أمثلة ذلك هو استخدام newsjacking (تصيد الأخبار الشائعة)، و trendjacking (تصيد المواضيع الشائعة), و memejacking (تصيد النهفات والنكات الشائعة). مؤشر الأداء الأساسي (Key Performance Indicator) نعني بهذا المصطلح (يُختصر بـ KPI) قياس درجة النجاح في تحقيق هدف ما. الكلمة المفتاحية (Keyword) الكلمات المفتاحية هي ما يُركز عليه المسوقون في بناء كل جزء من المحتوى المكتوب. الكلمات المفتاحية هي أحد الطرق التي تُستخدم في تهيئة المحتوى ليناسب محركات البحث. صفحة الهبوط (Landing Page) كل صفحة في موقع ما هي صفحة هبوط. بلغة التسويق، وبشكل خاص، تُصمم صفحات الهبوط لتحقيق هدف خاص بالبيع. العميل المحتمل (Lead) يشير مصطلح Lead في لغة التسويق إلى العميل المحتمل. التسويق المؤتمت (Marketing Automation) التسويق المؤتمت هو عملية تشغيل "مهمة تسويقية" وتنظيمها باستخدام برمجية ما. التهيئة للأجهزة المحمولة (Mobile Optimization) التهيئة للأجهزة المحمولة هو المصطلح العام المستخدم عند تصميم موقع وأخذ الهاتف المحمول في الحسبان. التصميم المتجاوب وتقييم سرعة الصفحة هما مثالان للتهيئة للأجهزة المحمولة. تحسين الصفحة – الداخلي (On-page Optimization) يشير هذا المصطلح إلى أي نوع من التحسينات المتعلقة بعملية البحث التي يتم تطبيقها داخل صفحة ويب. يشمل ذلك استخدام التخطيط المتجاوب وتهيئة النص باستخدام البيانات الوصفية. تحسين الصفحة – الخارجي (Off-page Optimization) يشير هذا المصطلح إلى أي نوع من التحسينات المتعلقة بعملية البحث التي يتم تطبيقها خارج الموقع. الشبكات الاجتماعية وفرص الارتباط هما مثالان على ذلك. التسويق التقليدي (Outbound Marketing) هي العملية التقليدية للتسويق وتكتيكاتها محصورة بغرض البيع المباشر. نقطة الألم (Pain Point) كل حل أو منتج أو خدمة الهدف منه هو حل نقطة ألم للزبون. غالبًا يتم تعريفها عند بناء شخصية المشتري. التطويع (Repurposing) التطويع هو استخدام جزء من محتوى وتجديده كشيء اخر، فمثلًا، يقوم المسوقون بأخذ منشور مدونة ومناقشته خلال جلسة فيديو حية. التهيئة لمحركات البحث (Search Engine Optimization) يشير مصطلح التهيئة لمحركات البحث (السيو SEO) الى أي تكتيك تسويقي يهدف إلى تحسين ترتيب الموقع خلال ظهور نتائج البحث. تحسين الصفحة داخليا وخارجيا هما مثالان للسيو. الإثبات الاجتماعي (Social Proof) يشير الإثبات الاجتماعي إلى اعتماد المستهلك على شهادة الغير من المستخدمين لتحديد نظرته وموقفه تجاه علامة تجارية ما. الشهادات، مراجعات المنتج، التقييمات وصور منتجات الشركة المُصورة من قبل المستخدمين هي أمثلة على الإثبات الاجتماعي. القائد المُفكر (Thought Leader) يُنظر للقائد المُفكر كشخص مؤثر وخبير في مجال مُعين. من منظور تسويقي، يُشير المسوقون للمحتوى التسويقي الداخلي كمحتوى قائد مُفكر. تجربة المستخدم (User Experience) في التسويق، يُستخدم مصطلح تجربة المستخدم (UX) للإشارة غالبًا الى خبرة المستخدم في استخدام شيء ما. يوجد طريقة في تصميم الويب تُسمى تصميم UX والتي تُركز بشكل كبير على بناء كل إنش في الموقع بناءً على إعدادات المستخدمين، سلوكهم وأهدافهم. ترجمة -وبتصرّف- للمقال The Marketing Terms You Need to Know as a Web Designer لصاحبه Brenda Stokes Barron
  4. صُممت صفحة الهبوط هذه كي تتناسب مع الإجراء الذي قاموا به للتو، لذا فإن من الضروري أن تكون دعوة الإجراء (Call to Action) واضحة ومباشرة في صفحات الهبوط المؤثرة. استخدام الإعلانات لتوجيه العملاء لصفحات الهبوط الجذابة هي طريقة رائعة لزيادة الوصول لعلامتك التجارية. بما أن شركة جوجل بدأت بإعطاء أفضلية في التصنيف للمواقع التي تتضمن محتوى فيديو، أصبح استخدام الفيديو طريقة ممتازة لتسريع ولفت الانتباه لعلامتك التجارية. يتميز محتوى الفيديو بالجاذبية لعدة أسباب، لذا فإن الاستفادة من قوة هذا المحتوى في صفحات الهبوط لديك هو أمر حتمي وسيساعدك في الحصول على انطباع عظيم من المرة الأولى من قبل عملائك المحتملين. قوة محتوى الفيديو صفحات الهبوط التي تحتوي على الفيديو هو أحد الاتجاهات القوية في مجال التسويق بالمحتوى. بينما أغلب صفحات الهبوط صُممت بهدف تشجيع العميل على اتخاذ اجراء مُعين مثل تنزيل كتاب الكتروني او الانضمام لقائمة بريدية، فإن محتوى الفيديو يُدخل البهجة والسرور ويسلي العملاء المحتملين. تتميز صفحات الهبوط التي تتضمن محتوى فيديو بقصرها، سهولة استيعابها، وجاذبيتها للمستخدم العصري. ليس كل مستخدم يريد أو لديه الوقت لقراءة محتوى نصي أو تصفح معرض صور، ولكن محتوى الفيديو من الممكن مشاهدته أو الاستماع له بينما يقوم المستخدم بأعمال أخرى، لذلك فمن الأسهل نوعًا ما أن يتم تشجيع العملاء على رؤية محتوى فيديو على أن يقوموا بمشاهدة أشكال أخرى من المحتوى. صفحات الهبوط التي تتضمن محتوى فيديو تستطيع أن ترفع نسبة التحويلات (conversions) إلى 80% وأغلب زوار المواقع يمكثون أطول في الصفحات التي تحتوي على فيديو بنسبة 88% من الصفحات الأخرى. ملاحظة: التحويلات (conversions) هي عملية تحول المستخدم أو الزائر العادي لصفحتك إلى عميل يقوم بشراء خدماتك. محتوى الفيديو فعال خصيصًا لمواقع التجارة الإلكترونية حيث يثق المستخدمون بنسبة 52% أكثر في عمليات شرائهم من المواقع التي تتضمن محتوى فيديو. أضف إلى ذلك أن المستخدمين الذين يشاهدون فيديو على المتاجر الإلكترونية يمكثون في هذه المواقع دقيقتين أكثر، مع احتمالية زائدة للشراء بنسبة 64%. صفحات الهبوط التي تتضمن فيديو مقابل صفحات الهبوط التقليدية يتغير التسويق الرقمي باستمرار ويبقى مجالًا تنافسيًا بشكل كبير. يُغمر المستهلكون بالإعلانات والمحتوى المدعوم والعديد من الأشكال الأخرى للتسويق والتي ببساطة لا تكترث لأي شيء يُعيد تشكيل الإعلان من جديد. يقوم خبراء التسويق الحديث بمهمة خلق انطباعات جديرة بالبقاء في ذاكرة المستهلكين بهدف التميز عن المنافسين الآخرين وبهدف عرض قيمة حقيقة يهتم بها من يشاهدها. تبقى صفحات الهبوط العادية طريقة ممتازة لعرض المحتوى الذي يهتم به المتصفحون، وأنهم في المكان الصحيح الذي يناسب حاجاتهم، وأن هذا المكان هو الذي يتعرفون فيه أكثر عنك وعن علامتك التجارية. بعد الضغط على إعلانك التجاري الموضوع في المكان الصحيح، يتم توجيه المتصفحين الى صفحة الهبوط التي سوف يستفيدون فيها من عرضك مثلًا، أو يحصلون فيه على هدية مجانية او للتعرف عنك أكثر. إن من الضروري أن تكون القيمة والأهمية أمران واضحان للمستخدم وإلا فإنه ببساطة سوف يعتقد أنك ضيعت وقته. يجب أن يكون هناك حافز واضح لجعل المستخدم يضغط على الرابط، ويجب أن تكون النتيجة كما يتوقعه المستخدم، وإذا لم يحدث ذلك، فسوف تتضرر قيمتك لديهم وسيتضرر بالإضافة لذلك معدل التحويل الخاص بك. وبمجرد أن يعتبر أحد المستخدمين أن علامة تجارية معينة أصبحت غير جديرة بالثقة أو ليست من ضمن اهتماماته، فإنه من الصعوبة الكبيرة إعادة الثقة المفقودة. تقوم شركة جوجل بإعطاء أولوية للمواقع التي تحتوي على محتوى فيديو وهذا أحد الفوائد الكبيرة لصفحات الهبوط المرئية. إذا تم تهيئة موقعك لمحركات البحث فإن ترتيب موقعك والوصول إليه سيتحسنان بفضل صفحات الهبوط التي تتضمن فيديو وهذا الوصول الإضافي قيمٌ جدًا لقوة التنافس الحاصل بين المواقع في الحصول على ترتيب أعلى. الهاتف أولًا أدرك العديد من المسوقين مدى القدرة المذهلة لتطوير المحتوى تحت شعار "المحمول أولًا". يستمر معدل مشاهدة المحتوى الخاص بالمحمول في الزيادة ومنطلقًا كالصاروخ كلما زاد عدد مستخدمي الهواتف الذكية وكلما زاد معدلهم في تصفح الانترنت، التسوق ودفع الفواتير. كلما تقدمت تقنية الهاتف الذكي يصبح من السهل شراء أجهزة محمولة ذات عتاد قادر على عرض فيويو عالي الجودة، وتبعًا لذلك، فإن محتوى الفيديو يزداد استهلاكه أكثر فأكثر مع مرور الوقت. أحد الركائز الأخرى للتسويق العصري هو التصميم المتجاوب (Responsive Design). الفضل يعود للوفرة في الخيارات المتاحة للمستهلكين والموجودة في الأجهزة القادرة على الاتصال بالإنترنت، حيث أصبح من المهم أكثر مما مضى أن تُصمم موقعك ومحتواك ليكون مناسبًا للعرض على أغلب الأجهزة المختلفة. فمثلًا من المهم أن تفكر بخيار التنقل بالإبهام (Thumb Navigation) عند تصميم مواقعك بما أن العديد من مستخدمي الهاتف لن يقوموا باستخدام الفأرة للتنقل في المحتوى الخاص بك. إن تهيئة المحتوى ليناسب أجهزة الهواتف الذكية هو أمر ضروري في التسويق الرقمي الحديث، ويدرك محترفو التسويق قيمة الاستثمار في المحتوى المرئي المعروض في مواقع مُهيئة ومُصممة جيدًا. تفتح الهواتف الباب واسعًا لإجراء حملات تسويقية متقدمة ومخصصة، فمثلاً، يستطيع المسوقون تفصيل المحتوى بناءً على الموقع الجغرافي للمستخدم وذلك بهدف تقديم محتوى ذو علاقة أكثر فأكثر. قد يكون هذا الأمر خيارًا غير واقعي لكل جزء في محتوى الفيديو، ولكن يبقى مهمًا تذكر أن المحتوى المُوجه بناءً على المكان الجغرافي له أثاره العميقة على معدل التحويل لديك. نصائح لصفحات الهبوط التي تتضمن فيديو يوجد عدد كبير من الأدوات المتاحة لتطوير محتوى الفيديو بتكاليف بسيطة أو تكاد تنعدم. من الناحية العملية، تستطيع أي مؤسسة أو جهة عمل إنتاج محتوى فيديو عالي الجودة، لذا من المهم الاستفادة من هذه الفرصة بأسرع ما يمكن. قيمة المحتوى المرئي أصبحت معروفة منذ سنوات، وإذا لم تستثمر هذا الأمر لصالح علامتك التجارية، فإن منافسيك بالتأكيد قاموا بذلك لصالحهم. النصائح التالية تُساعدك في بناء صفحات هبوط ذات محتوى فيديو عالي الجودة: ابدأ الآن. إذا لم تدرك بعد أهمية محتوى الفيديو فإن منافسيك أدركوا ذلك قبلك. من المهم الاستثمار في محتوى الفيديو في صفحات الهبوط بأسرع وقت ممكن. الفيديو الخاص بك سيتحسن مع زيادة الفحص والخبرة ومع مرور الوقت، لذا لا ترتبك أو تتراجع إذا لم تكن التجربة الأولى لك مثالية كما تتوقعها. لا تجعل التكلفة تؤثر عليك. إن تكلفة انتاج محتوى الفيديو أرخص مما يدركه أغلب المسوقين. حتى مع امتلاكك خبرة قليلة أو منعدمة في إنتاج الفيديو، لا ينبغي عليك إبهار جمهورك من البداية، يكفي عليك أن تقدم لهم شيئًا ممتعًا وذو قيمة. المحتوى المرئي البسيط مثل الحديث عن قصص نجاح العملاء لديك، عرض نماذج من المنتجات، أخبار الشركة، كل ما سبق هو طريقة عظيمة للبدء في تقديم محتوى فيديو لعملائك. اعرف جمهورك. تُعتبر بيانات العملاء ذات الجودة العالية أحد أهم وأقوى الأدوات في ترسانة المسوق. إذا كان لديك فكرة واضحة جدًا عن العملاء الذين تريد جذبهم إليك، قم ببناء محتوى مرئي يستهدف حاجاتهم ورغباتهم، وقم بالإجابة فيه عن أسئلتهم المتوقعة التي يسألونها عن علامتك التجارية. اعرف كيف تُهيئ محتوى الفيديو. محتوى الفيديو جذاب، ولكنه قد يكون مزعجًا للبعض. فمثلا، خيار التشغيل التلقائي قد يُفجر الصوت الخاص بالفيديو لدى العميل. تأكد أن المستخدم يستطيع وقف وتشغيل الفيديو متى ما أراد ذلك، بالإضافة لقدرته على توسيع المحتوى ليشمل الشاشة كلها وكتم الصوت إذا أراد. من الممكن أيضًا استخدام خاصية التنصيص المغلق (Closed Captioning) خلال المحتوى المرئي بهدف الوصول إلى شريحة أكبر من الجمهور. اجعل محتوى الفيديو ذو علاقة وذو قيمة. إذا كنت تعرف قاعدة العميل لديك، فليس من الصعب أن تبني محتوى فيديو يُستَمتع به. لا تُضيع وقت العميل المهم. لابد من وجود سبب وجيه يجعل العملاء لديك يضغطون على الرابط الإعلاني الخاص بك، وبعد ذلك تأكد من أنهم سيكونون مسرورين بالمحتوى المرئي الذي سيرونه. استمر في المتابعة. فالجولة تتبعها جولة، لذا لا تبني محتوى فيديو منفصل عن بعضه البعض، واربط المحتوى بمحتوى اخر سيأتي في المستقبل. بناء سلسلة محتوى فيديو مترابط من الطرق القوية في بناء وزيادة الاهتمام بعلامتك التجارية وبتوجيه الانضمام لها. المستخدمون الذين يرون الفيديو الخاص بك لأول مرة سيبقون على اتصال لرؤية ما سيأتي، والذين يأتون لاحقا ويرون محتوى جذاب سيقومون بالعودة والنظر في المحتوى السابق لتفحص ما فاتهم. هذه ليست سوى بعض الطرق التي تُمكنك من الاستفادة من قوة محتوى الفيديو في صفحات الهبوط. لقد أصبح من المعروف منذ سنين لدى المسوقين مدى أهمية صفحات الهبوط ومحتوى الفيديو لذا من المهم الاستفادة من الجمع بينهما لصالح علامتك التجارية وللحصول على مستوى تحويل عالي وللبقاء قويًا في سوق يتميز بمنافسة عالية. ترجمة -وبتصرّف-للمقال How You Can Benefit from Video Landing Pages لصاحبه Stephen Moyers
  5. قد تبدو قوائم توزيع البريد الالكتروني والبريد الإلكتروني المشترك أبسط الطرق لمتابعة خدمة العملاء – في بعض الأحيان هم كذلك. إذا كنت تُمثّل شركة صغيرة الحجم -أو بدأت كشركة صغيرة- فقد يكون ذلك طبيعيًا. قد تكون استخدمت نظام Office 360 Microsoft Outlook لمعالجة قضايا العميل كلما ظهرت. ولكن الحلول السهلة والبسيطة لا تكون دائمًا الأفضل. فوضى قوائم توزيع البريد الإلكتروني وكذلك القيود الموجودة في برنامج البريد الإلكتروني المشترك لشركة مايكروسوفت تجعل الموظفين يشعرون بالإحباط وتجعل العملاء يتطلعون لتجربة أفضل قد يجدونها في مكان اخر إذا لم تُنشئ نظامًا جيدًا. عندما تعالج كل محادثات العملاء باستخدام أدوات مثل Gmail أو Outlook فمن السهل أنّ تتذكر لماذا قوائم التوزيع والبريد الالكتروني المشترك لم يتم بناؤهما لخدمة العميل. كلما تطورت الشركة، المنافع التي ستظهر من الانتقال لعملية مبسطة ستفوق بكثير المرحلة الأولى لمنحني التعلم والاستثمار لاستخدام نظام جديد. حلول الدعم من خلال البريد الإلكتروني هذه بعض الأدوات المختلفة قليلًا والمستخدمة على بناءً على البريد الالكتروني ولكنها تشكل تحدٍ لفرق الدعم: صندوق البريد الوارد المشترك صندوق بريد واحد يستخدم اسمًا مستعارًا (مثال: academy@hsoub.com). الرسائل الإلكترونية الواردة تظهر في هذا الصندوق المشترك. يستطيع الموظفون الدخول والرد مما يجعل ارسال الرد وكأنه من الاسم المستعار. قائمة توزيع البريد الإلكتروني هذه الأداة تَستخدم عنوان بريد إلكتروني مُخصص لمجموعة من الناس. إذا قام أحدٌ بإرسال بريد لهذه القائمة فإن البريد يصل لكل المشتركين فيها. يقوم الموظفون بالرد من عناوين بريدهم الخاصة. مجموعة Office 365 مصطلح "مجموعة" هو طريقة في Outlook للإتصال المشترك. انها تعمل كقائمة توزيع بريد إلكتروني ولكنها تحفظ الرسائل وتتضمن بريدًا مشتركًا، وتقويمًا، ومفكرةً ومكتبةً. مجموعة جوجل هذه أداة من جوجل تُمكنك من انشاء بريد إلكتروني مشترك أو قائمة توزيع من خلال بريد Gmail. خمس إشارات تحتاجها للانتقال من قائمة توزيع أو بريد مشترك إلى نظام الدعم الفني تدفق العمل الخاص بدعم العميل مهمُ جدًا لأنه يؤثر غالبًا على تجربة العميل. إذا كان أحدٌ ما غير سعيد بتجربة فإن أصغر خلل سيصل به إلى قرار المغادرة. إليك عدة إشارات واضحة تساعدك في التخلي عن حساب البريد الالكتروني المشترك والانتقال لنظام الدعم الفني: إذا كنت تتابع العملاء من خلال رسائل بريد الكتروني ولم يكن هناك ردًا على الاطلاق بعد عدة أيام. قيام أعضاء فريق بالرد على نفس العميل بشكل مُكرر أو بمعلومات متناقضة. الذي قام بذلك. ليس لديك تقارير عن حجم البريد الإلكتروني الذي تستقبله، أو سرعة رد الفريق، أو من هو الشخص الذي يقوم بالرد، أو ما مدى سعادة العملاء بالردود التي يقرأونها. دون عمل إعادة توجيه لرسالة البريد الإلكتروني لعدة أشخاص. وجود ما سبق يدل على أنك تستخدم قائمة توزيع وصندوق بريد مشترك وهذا يجعل شركتك تظهر بشكل غير محترف ويسلب من فريقك الإنتاجية. بمجرد ظهور أحد الدلائل السابقة، فهذا يعني أنه حان الوقت لبحث خيارات نظام الدعم الفني. ما الذي عليك البحث عنه في نظام الدعم الفني؟ كيف تبدأ عملية استكشاف نظام دعم فني جديد؟ فكر في البداية في أولوياتك، إذ تعلمنا في Help Scout أنه يوجد بعض العناصر الأساسية لا تستغني عنها أي شركة. عند حصولك على تلك المميزات تستطيع أن تُقدم خدمة عملاء أفضل دون الحاجة لحساب بريد إلكتروني مشترك. الشفافية غياب الشفافية مشكلة عامة في Office 360، حيث لا يوجد طريقة لمعرفة إذا كان الغير يعمل على حل نفس القضية في نفس الوقت. إذا كان لديك فريق بإنتاجية عالية فإن هذا قد يعمل ضد مصلحة الشركة حيث ستجد رسائل متعددة تُرسل من نفس العنوان وتحتوي نفس المضمون في وقت واحد. لك أن تتخيل حجم الإحباط الذي سيشعر به العميل عندما يفتح كل هذه الرسائل. خاصية Traffic Cop indicators في Help Scout تُظهر لكل شخص من الذي يعمل على أي قضية من قضايا العملاء وفي الوقت الحقيقي: انه نظام رسومي سهل: مثلث أصفر يُظهر أن مستخدمًا اخر يقوم بمراجعة المحادثة، والمثلث الأحمر يُشير الى أن شخصًا أخرًا يقوم بالرد على المحادثة. بالإضافة لذلك، Help Scout لا يسمح لك بإرسال رسالة لعميل إذا وُجد شخصٌ آخر ردّ خلال الوقت الذي كنت تعمل فيه على ذلك الرد. أنظمة الدعم الفني تُسهّل من التعاون لحل قضايا العملاء الصعبة. بدلًا من إعادة توجيه المشاكل للمشرفين أو باتجاه أعضاء الفريق الداخليين، تستطيع العمل على حل المشكلة مع الغير على منصة Help Scout. يستطيع المدراء تفحّص حالة قضية بشكل مباشر دون الحاجة للرجوع لكل شخص. المسؤولية أثناء تدفق العمل ستخسر كمًا كبيرًا من الإنتاجية عندما يعمل أكثر من موظف على نفس القضية. على الرغم من أن العميل لا يستقبل عدة ردود، ولكن مدة الوقت اللازم لحل مشكلة ما سيحبط العاملين لديك. تستطيع أن تحاول بناء حلول بديلة، ولكن في الحقيقة، القيود الموجودة في البريد المشترك لـ Office 360 تُكبل نظامهم. هناك بالتأكيد طريقة أفضل. مجلدات التعيين في نظام Help Scout تُظهر لك كل شيء تم تكليفه لفريقك. بإمكانك ترتيب القائمة حسب الشخص المُكلَف للتأكد من عدم ضياع أي شيء في حال خروج عضو ليوم أو مغادرته الشركة. تستطيع من هناك إزالة شخص من مهمة وربطها بشخص آخر. هذا الحل السهل يخلق الاستقرار ليس فقط للعاملين لديك، بل أيضًا لعملائك. تقارير لا تقدر بثمن عن عملائك وموظفيك أغلب أنظمة الدعم الفني الأساسية تُقدم تقارير عن العملاء أبعد بكثير من الذي يقدمه Office 360. التفاعل مع العملاء يكشف عن كنز معلومات: أكثر مشاكل تجربة العملاء، وما يحتاجه العملاء، وما هو الشيء الذي يجعل حياتهم أفضل خلال عملك. يُمًكِّنُك Help Scout من متابعة أغلب أنواع المقاييس الخاصة بعملائك وعملك. هذه بعضٌ منها: إجمالي المحادثات عدد العملاء الذين يحتاجون للمساعدة كل يوم أكثر الأيام والأوقات ازدحامًا بطلبات الدعم أشهر المواضيع التي يكتب عنها الناس التغير في الطلبات النموذجية فرص أتمته العمليات تستطيع تحسين كل جزء من عملك من خلال أدوات التقارير في Help Scout ابتداءً من عدد الموظفين الذين يعملون في كل وردية عمل الى حل القضايا المحورية في المنتج التي تؤدي لعدم رضا العميل. يقدم لك Help Scout تقارير عن أداء الأفراد وكفاءة الفريق بقياسات حديثة وواضحة. إذا قام أحدهم بإرسال بريد مرفوض لأحد العملاء ستعرف ذلك من أول يوم. كمدير، هذه التقارير تساعدك في السيطرة للوصول لكفاءة أكبر ونجاحات مثمرة. باختصار، مثل هذه التقارير هي طعامك وشرابك لقيادة فريق مميز. الانتقال من قائمة توزيع بريد إلكتروني أو بريد الكتروني مشترك لنظام دعم فني قد يبدو مُخيفًا، ولكنه يستحق التجربة. بطريقة مبسطة وشفافة لتدفق العمل خلال نظام دعم فني سيتمتع فرق العمل لديك بالقوة وتتلاءم مع تطور الشركة، مع التأكد من أن أفضل عملاءك لن ينزلق نحو مشاكل البريد الإلكتروني المشترك. ترجمة -وبتصرّف-للمقال How to Tell It’s Time to Ditch Your Shared Email Inbox لصاحبه Elizabeth Wellington
  6. تواجه الشركات الناشئة في عالم الأعمال العديد من العوائق في ظل التنافس الشديد في هذا العالم. أحد أبرز هذه العوائق هو تدني ثقة المستهلك، فلا يوجد أحد سيقدم ماله لجهة ما لا تمتلك تقييمات ومراجعات سابقة أو لا تمتلك كيانًا مضمونًا يمكن الرهان عليه. يوجد العديد من الحلول للمشكلة السابقة وأحد أكثر هذه الحلول فعالية هو التسويق باستخدام الشبكات الاجتماعية وتأييد الموظف هو أحد الأشكال الجديدة لهذا الحل. يميل الناس غالبًا للثقة بالتزكيات الغير احتفالية وخاصة من الأقران، لذلك فإن جعل الموظفين يتحدثون عن عملك سيكون مُفيدًا جدًا. ولكن كيف يُمكن تفعيل ذلك؟ وكيف يُمكنك تطبيق تقنية مُثمرة؟ فوضع خطة لجعل الموظف يتحدث عن عملك هو أمر سهل ولكن من الصعب إتقانه، لذلك من الأهمية بمكان جمع ما يمكنك من المعلومات من البداية. ما هو تأييد الموظف؟ عندما يقوم الموظفون بإعادة نشر أو إعادة تغريد منشوراتك وتأييدك في الشبكات الاجتماعية ومناقشة عملك مع الغير بشكل حي على شبكة الإنترنت فهذا هو تأييد الموظف في العالم الحديث. ويشمل ما سبق قيام الموظفين بمشاركة المحتوى المنشور من الغير والذي يخص الجهة المُوظِفة لهم. نستطيع أن نختصر ذلك بقيام العاملين بدعم شركتهم من خلال منصاتهم وحساباتهم الاجتماعية الخاصة بهم بدلًا من الاكتفاء بالقيام بذلك في الصفحات الرسمية والمشهورة فقط. على الرغم بمعرفة الجميع أن الإنترنت غيرت من طريقة ونهج قطاع الأعمال فيما يخص التسويق وجعلت الاعتماد على مساعدة الموظفين أمرًا نادرًا، ولكن هذا الأمر في الحقيقة أصبح مُعيبًا الأن لما يُشكله دعم الموظفين في زيادة ثقة المستهلك وزيادة عدد الزيارات لموقعك. كيف تستخدم تأييد الموظفين؟ بينما العديد من الشركات لديها برامج غير رسمية لتشجيع دعم الشركة من خلال الشبكات الاجتماعية عبر موظفيها، إلا أن هذه الطريقة صعبة في المتابعة كما أنها أقل فاعلية، فأنت تستطيع أن تجعل عينيك تراقب حسابات الموظفين ولكن من الصعب قياس مشاركة الموظف ومتابعة مدى التأثير الناتج عنه، بالإضافة لصعوبة متابعة حسابات العاملين مجتمعة. لحسن الحظ، يوجد منصات "شبكات اجتماعية مصغرة" تستهدف دعم الموظفين مثل LinkedIn Elevate, أو Smarp, أو Sociabble, أو Dynamic Signal. هذه المنصات تُقدم العديد من الخصائص والتحليلات. يسمح لك تتبع الارتباط "Link Tracking" برؤية أكثر المنشورات التي يتم الضغط عليها ورؤيتها، بالإضافة لأكثر محتوى شُهرةً وبذلك تزداد المنافسة لدى الموظفين للحصول على الصدارة وكسب النقاط والحصول على جوائز. هذه المنصات تجعل من السهل نشر المقالات لأن كل شيء يتم إدارته من مكان واحد ومريح، وهذا هو المفتاح إذا أردت أن تأخذ برنامج تأييد الموظفين في مؤسستك على محمل الجد. بهذه الطريقة، وبمجرد نشر المحتوى، فإن الموظفين يستطيعون بسهولة وسرعة مشاركة هذا المحتوى على الشبكات الاجتماعية المختلفة، وهو أمر أسهل من إرسال مذكرات يومية للموظفين لنشر ذلك على تويتر وفيس بوك. هذا النوع من الأدوات يقدم طريقة سهلة للموظفين ومُشغليهم للتواصل، التعاون والبقاء في صورة أخر المستجدات. قد يكون دور البريد الإلكتروني لم ينتهي بعد، ولكن المقدرة على وضع معلومات ذلت علاقة بالمؤسسة ونشرها من خلال تطبيق ما سيكون مريحًا أكثر بدلًا من إرسال بريد إلكتروني ضخم لمجموعة كبيرة من الموظفين. لماذا نحتاج دعم الموظفين؟ إذا أردت، فإنه من الممكن أن تُنشئ بعض حسابات تويتر للمدراء في شركتك، وتقوم بإعداد برنامج وظيفته هي إعادة نشر ما تقوم بإضافته في مدونتك أو صفحتك، وأن يعمل بشكل يومي، ولكن هذا الأمر لن يخرج عن إطار المألوف والمتعارف عليه. إن أغلب مستخدمي الشبكات الاجتماعية لديهم عين حساسة ويستطيعون ملاحظة الحسابات الآلية عن بعد ميل ولن يكون ذلك بالطبع جميلًا في حال استخدمت نفس الطريقة لحساب شركتك. إن المهتمين الذين يزورون حساباتك على المنصات الاجتماعية قد يتركوا متابعة هذه الحسابات بسبب المحتوى المشور بطريقة آلية. لذلك، فإن المحتوى المُثير الذي يتضمن لمسة حقيقة من شخص حقيقي بالإضافة لتفاعل المستخدم لهو أمر يحظى باحترام وتقدير الناس. قد تكون مشغولًا عن البقاء أمام الشاشة لمتابعة المنصات الاجتماعية، ولكن موظفيك قد لا يكونوا كذلك. يقل اهتمام المستهلكين هذه الأيام بالمدراء التنفيذين ويتجه التركيز نحو الشخص العادي، ناهيك عن أن المتابعين العاديين لديهم شبكات خاصة من الأصدقاء والأقران الذين يستطيعون الوصول لمحتواهم، وهذا الوصول للمحتوى هو أحد مفاتيح النجاح. من الواضح لحد ما أن ذلك هو وسيلة تسويق قيمة تستطيع توليد محتوى طبيعي وجذاب. لقد انتبهت الإنترنت لظاهرة Clickbait والحسابات الآلية، لذا فمن المهم الان بذل جهد إضافي لمواجهة ذلك. عندما نتحدث عن التأثير الاجتماعي، فإن الموظفون هم الذين يملكون ثقة الجمهور والعامة. تشجيع تأييد الموظفين بعد أن تعرفت على أهمية دعم الموظفين ولماذا أنت بحاجة له، يأتي السؤال المهم، كيف أستطيع تحفيز الموظفين للقيام بذلك؟ إذا كان نظام الدعم الذي تريده لا يُقدم للموظف حوافز ومكافئات أو تأثير إيجابي على وظيفتهم، فإنهم لن يضيعوا وقتهم في ذلك. عليك تقديم بعض التشجيع لذلك. في البداية، إن إجبار الموظف على الحديث عن شركتك في الشبكات الاجتماعية ليس هو الطريق، حيث تقل المعنويات حتى تنعدم عند إجبار الناس على القيام بشيء لا يهتمون به، وبدلًا من ذلك، حفزهم وزد اهتمامهم بما يقومون به. قم بتطبيق نظام مكافئات أو لائحة صدارة حتى تزيد المنافسة بينهم. تجنب إعطاء فقرات كبيرة من القواعد والقوانين المعقدة، وقم بتجهيز تعليمات مُختصرة وواضحة تسمح للعاملين لديك بأخذ زمام المبادرة وتوليد ونشر المحتوى الذي يستمتعون به ويرونه مناسبًا. سيعرف عملائك متى أن الموظفين لديك يحبون ما يقومون به وستكسب بذلك ثقة كليهما. الشيء المهم هو: لا تُملي كل حركة. القواعد مُهمة، ولكن لا تتطرف فيها. قد يظهر أنه من الخطر أن تمنح شخصًا حرية واسعة للتأثير في صورة شركتك، ولكن المكافئات التي سيحصل عليها الموظف مهمة بالنسبة له. هذه العقيدة تمنح الموظف القدرة على إنشاء محتوى حقيقي ليس كالمحتوى المُنشأ من قبل برنامج ألي أو في مصنع. ربما تسمح أيضًا لهم بإنشاء محتواهم الخاص. الكتاب المبدعين، الفنانين ومحررو الصور والفيديو يُشكلون اصلًا من أصول شركتك إذا استطعت اكتشافهم. أنشئ محتوى ذو صلة عندما لا يملك طاقمك شيئًا لمشاركته، فعن ماذا سيتكلمون إذن؟ إذا لم تكن في خضم الشبكات الاجتماعية بشكل قوي فإنه حان الوقت للبدء بذلك. أدخل توتير، اكتب في فيسبوك وابدأ في كتابة منشوراتك وتغريداتك. بجانب جذب اهتمام إضافي لشركتك، سيكون من الجيد توفير مادة قابلة لإعادة النشر وفتح السبيل للنقاش بين الموظفين ولا تنس إضافة المرح إذا استطعت. فإذا كانت شركتك تُقيم حفلًا قم بالتقاط الصور وانشرها في المحتوى. محتوى كهذا سيقدم نَفَسًا نقيًا لصفحتك ويضع عملائك في وسط مناخ الشركة. باختصار، فإن أيًا ما تنشره في الشبكات الاجتماعية لابد أن يكون ثاقبًا أو جديرًا بالذكر ومَرِحًا. مشاركة مواضيع ذات عناوين عفوية، ذات صلة وتحتوي على صور أو فيديوهات سريعة، كل ذلك هو مناسب. تبني هذه الفكرة العامة سيفيد شُهرتك على المنصات الاجتماعية بالإضافة لبرنامج تأييد الموظفين لديك. طور إستراتيجيتك التسويقية باستخدام تطبيق التأييد الطريقة المؤكدة لتطبيق خطة التسويق هو باستخدام منصة تشبيك. يوجد العديد من التطبيقات التي تساعد في ذلك، ولكن من الأفضل أن تختار تطبيقًا يشمل تأييد الموظفين. تطبيقات مثل هذه تُغطي أغلب الخصائص التي تُقدمها تطبيقات التشبيك للأعمال، بالإضافة أنها تُقدم تحليلات ذات صلة تُساعدك في معرفة ما هو المحتوى الذي تستهدفه وما هي طبيعة العملاء المهتمين في الشركة. دون ذلك، عليك الاعتماد على أتباع وقياس كفاءة ذلك – وهذا غالبًا غير جدير بالثقة. ابدأ بشكل صغير وابحث عن أفضل العاملين لديك الذين يتسمون بالحرص وحب تجربة الأشياء الجديدة. نفذ مسابقات وحدد الأهداف المطلوبة منهم، وإذا نجحوا قم بعرض ذلك على الموظفين الذين يرغبون بالمشاركة حتى تصل لبرنامج مستقر. ومع الوقت، سيكون لديك برنامج ناجح قام بكل من زيادة عدد الزائرين وإعطاء الفرصة للعاملين لديك لتعزيز مهاراتهم ووظائفهم. الأمر يستحق التجربة، فالمخاطر قليلة ولكن الفائدة هائلة. ترجمة -وبتصرّف- للمقال Why Employee Advocacy Must Be Part of Your Social Media Marketing Plan لصاحبه Brenda Stokes Barron
  7. اذا كان حسابك قديم، وحتى لو كنت مُنشئ تطبيقات عليه، ستحتاج الان إلى ان تملأ الطلب وترسله ثم بعد الموافقة عليه تستطيع إنشاء تطبيقات جديدة
  8. وعليكم السلام،،، لم يعد بالامكان حاليًا ان تُنشئ تطبيق بشكل مباشر على حساب مطوري تويتر إلا بعد ان تُقدم طلب Application لهم ومن ثم يراجعونه. بعد موافقتهم تستطيع ان تُنشئ التطبيق وتُطبق المثال الموجود في المقال. تحياتي وبالتوفيق
  9. في جو العمل، يوجد العديد من الطرق التي تُساعدك في تخفيض التكاليف وتوفير الوقت وزيادة الأرباح. قد يبدو مصطلح "نصائح التسويق" جذابًا، ولكن هل هي مفيدة لصالح العلامات التجارية؟ تُشير الدلائل إلى أن النصائح المناسبة في الوقت المناسب تُسرع من الحملات التسويقية دون إنفاق المزيد من المال. هذه ثمانية نصائح لتحسين جهودك التسويقية من دون مشاحنات. حدث محتواك المتجدد يبقى المحتوى المتجدد مُهمًا لسنوات-أو على الأقل خلال المستقبل المنظور. هذا المحتوى هو الذي يجعل قُراءَك يبحثون عنه دائمًا. عناوين مثل "أهم المصطلحات الصناعية" أو "كيف تقوم بـ " هي عناوين لا تفقد عنفوانها مع الزمن. المحتوى المتجدد في موقعك سيُحسن من أدائه في محركات البحث ويبقى أثره لمدة طويلة. بالإضافة إلى ذلك فهو لا يحتاج لمتابعة دائمة حيث لا تحتاج لتحديث المحتوى المتجدد بشكل دوري. نشر المحتوى المتجدد يُميز شركتك كأحد المصادر المهمة في السوق الذي تعمل فيه، ويمنحك عوائد تسويقية بقليل من الجهد بمجرد إنشائه. كنصيحة تسويقية، يجب أن يكون عُمر المحتوى المتجدد طويلًا حتى تستفيد منه. لابد من إعطاء الأولوية لجودة المحتوى، وأضف لذلك وجود روابط تُوجه الزوار لهذا المحتوى بشرط أن تكون هذه الروابط موجودة في مصادر ذات سمعة حسنة ومشهورة وألا تنتهي صلاحية هذه الروابط. تحتاج أيضًا لاستخدام عناوين تُلبي حاجات جمهورك ولها علاقة بما يهتمون به. يجب أن يكون المحتوى المتجدد مقروءًا بشكل واضح، مع وجود بعض العناوين والنقاط التي تُقسم الفقرات الطويلة في المحتوى. المحتوى يجب أن يكون أطول من المواضيع العادية في المدونة وذلك حتى تُقدم محتوى ذُو قيمة للقراء، وكلما مكث القُراء أطول في الموقع كلما حصلت على نقاط أفضل في ترتيب البحث في جوجل. أعد نشر المحتوى القديم مع التحديثات نشر المحتوى الجديد هو أمر مُهم لموقعك ولكنه لا يجب أن يكون النوع الوحيد من المحتوى الذي تُسوقه. ابحث في المحتوى القديم واختار منه الذي له صلة بالجمهور أو الذي يهتمون به. بعد ذلك، أضف بعض التعديلات والتحديثات للمعلومات وأعد نشرها في موقعك وحساباتك الاجتماعية. استخدم كلمات مثل "تحديث" أو "معلومات جديدة" لإبلاغ جمهورك أنك قمت بإضافة شيء ذو صلة بموضوع قديم. تكرار المحتوى طريقة سريعة وسهلة للبقاء متصلًا ومشاركًا دون الحاجة لوقت أو تكاليف. راجع استراتيجية السيو لديك قد يكون التهيئة لمحركات البحث موضوعًا مُكررًا، ولكنه ذو أهمية كبيرة لنجاح جهود التسويق لديك. راجع استراتيجيات التهيئة التسويقية لمحركات البحث وتأكد أنك لا تُضيع المال أو الفرص. فمثلًا، قم بفحص الكلمات المفتاحية غير النموذجية وتأكد هل تستحق الاستثمار أم لا، فقد تجد أن بعض الكلمات المفتاحية الأخرى مهمة أكثر لشركتك. أزل البقية التي ليس لها علاقة واحفظ موازنة التسويق ببساطة ودون عناء. استخدم أدوات مثل Google Keyword Planner او Google Trends للبحث عن المواضيع الساخنة التي يهتم بها جمهورك. يساعدك مخطط جوجل للكلمات المفتاحية في اكتشاف جديد الكلمات المفتاحية التي لها علاقة بمحتواك، وتمكنك من مقارنة اتجاهات الكلمات المفتاحية وأحجام البحث فيها، بالإضافة أنها تساعدك على إنشاء المجموعات وإعداد العروض. راقب آخر الأخبار الخاصة بمجال عملك واستخدم الكلمات المفتاحية المتعلقة بالموقع الجغرافي في إعداد التهيئة المحلية لمحركات البحث Local SEO – إرسال موقع شركتك الجغرافي إلى محركات البحث-. تحقق من منافسيك وتفقد الكلمات المفتاحية التي تعمل لصالحهم. بإمكانك استخدام منصة مدفوعة الأجر مثل Moz.com لإجراء تهيئة الكلمات المفتاحية دون عناء. أعد تسويق محتواك إعادة تسويق المحتوى هو أفضل أصدقائك، حيث يساعدك في متابعة المستخدمين الذين يزورون موقعك ولم يقوموا بإجراء عملية شراء. هؤلاء المستهلكين سوف يرون محتواك في مكان آخر مثل الفيس بوك أو اليوتيوب. يمنحك إعادة التسويق فرصة ثانية لتكون علامة تجارية ذات قيمة للمستهلك. إنه يذكر المستخدمين بإكمال الإجراء الذي بدأوه بزيارة موقعك، ويعمل كطريقة لجعلهم يبقون على اتصال. أعد تسويق محتواك باستخدام الكوكيز، إعلانات البانر وتكتيكات التسويق لجمهور محدد. أدوات مثل Google Display Network و Facebook Remarketing تساعدك في أغلب ما تم ذكره في هذه النصيحة. عُد الى جمهورك المستهدف مع مرور الوقت سيكون من السهل خسارة جمهورك الذي تستهدفه وتحاول جذبه لمحتواك. من الجدير بك أن تتفحص بشكل دوري الجمهور الذي تستهدفه وما يجب عليك فعله بشكل مغاير لتُبقي حملاتك التسويقية مثيرة وممتعة وذات طابع. حلل إعلاناتك واعرف أيًا من حملاتك التسويقية تعود بالنتائج المرجوة منها. قد تكتشف أنك تقوم بأخطاء صغيرة مثل وجود تناقض في العلامة التجارية وهذا سيؤثر على نجاح إعلاناتك. يجب أن تعمل صفحات الهبوط في جميع الأوقات، ويجب أن يرى إعلاناتك مستخدمون ذَوُو علاقة. إذا قمت بالانتقال من فيس بوك الى الانستقرام أو سناب شات مثلًا، عليك أن تكون على رأس التغييرات التي تحدث في ذلك، وأن تقوم بإعادة تفصيل المحتوى وترتيبه ليناسب المنصات الجديدة. راجع دائمًا الفئات العمرية، الجنس والتركيبات السكانية. القيام بذلك سيساعدك في تجنب الإنفاق الغير مجدي على إعلانات تفقد درجتها وتفشل في تحويل جمهورك الى عملاء. راجع مدونتك لا تدع مدونتك تسقط وتحيد عن الطريق بينما تُركز على جهودك وحملاتك التسويقية الأخرى. مدونتك أساسية في نجاح التسويق لعلامتك التجارية. قضاء بعض الساعات أسبوعيًا في تهيئة مدونتك، إنشاء محتوى جديد، والنشر بانتظام، كل ذلك يُسرع في انضمام المستخدمين لك. تذكر النصائح البسيطة التالية: حدث المدونة أولًا بأول. إذا أصبحت المدونة قديمة وليست على علاقة بمحتواك الذي تسوقه، فإن ذلك يؤذي موقعك بشكل جيد. عليك بالنشر بشكل ثابت، فمحرك جوجل يعتني بالمواقع التي تنشر محتوى بشكل دوري، لذا فإن تحديث المدونة أولًا بأول يُسرع من تهيئة الموقع لمحركات البحث ويُحسن من ظهوره في نتائج البحث وبكل تأكيد سيزيد من ظهور علامتك التجارية. اجعل مدونتك ذات طابع. القراء الحاليين يرغبون بالشعور بارتباط حقيقي مع العلامات التجارية. مدونتك هي الخيار الأنسب لتقديم فرصة المشاركة والارتباط بعلامتك التجارية بالإضافة لتقديم محتوى ذو طابع مرغوب. ادرس جمهورك الذي تستهدفه جيدًا واكتب لأشخاص مُعينين. وظف محترف. إذا لم يكن لديك متسع من الوقت والطاقة الضروريان لمتابعة مدونتك، عليك بتوظيف كاتب محتوى محترف. لا تنسى النصائح والممارسات الجيدة التي تساعدك في اختيار الكاتب الجيد، واعتمد الموازنة التي تناسبك. باستخدام المصادر والأدوات الصحيحة، ستكون متابعة المدونة أمرًا سهلًا ومربحًا للغاية. قم بمتابعة المدونة بشكل دوري للتأكد أنها تعمل بالشكل المطلوب. تهيئة المدونة يُحسن من جهود الحملات التسويقية دون الكثير من العمل المطلوب من طرفك. أشعل حملاتك أنشئ ملف مراجع swipe file خاص بعملك ودَون فيه الأفكار والإلهامات المستقاة من منافسيك ومن المواقع التي تحبها. احفظ الصور والتصميمات التي تعتقد أنها مناسبة لعلامتك التجارية والتي من الممكن أن تستفيد منها في المستقبل، ودون أفكار المحتوى الذي ستكتبه. يجب أن يكون ملف المراجع الخاص بك كالكنز الذي يحوي التفكير المبدع والأفكار الجديدة لشركتك. عندما تحتاج بعضًا من الإلهام افتح ملف المراجع الخاص بك واستخدم الأفكار الموجودة فيه لإشعال حملاتك التجارية التي أصبحت مملة أو التي فشلت في جذب الجمهور الذي تبتغيه. ملف المراجع حلٌ بسيط للبقاء في الأمام أثناء لعبة الحملات التسويقية وذلك دون الحاجة لكثير من الوقت والجهد. لا تنسى أصلك قبل كل شيء، إياك ان تبتعد عن المهمة الرئيسية لعلامتك التجارية. في بعض الأحيان، تحتاج الحملات التسويقية المتخبطة أن تتذكر الأهداف الرئيسية لها. ما هي المهمة الرئيسية الخاصة بك – إنقاذ الكوكب مثلًا؟ تبسيط مهام العمل؟ جعل حياة عملائك أسهل؟ تذكر دائمًا مهمتك الرئيسية أثناء عملك وجهودك التسويقية حتى تُقدم للعالم رسالة واحدة ومتماسكة. الرجوع للأصل يُذكر عملائك بالسبب الذي جعلهم ينضمون لك في البداية. قد يُدهشك حجم الإلهام التسويقي الذي ستجده عندما تتذكر السبب الكامن خلف انطلاقك العمل. كن وفيًا لعملك والباقي سيأتي تباعًا. ترجمة -وبتصرّف-للمقال 8 Quick Tips to Boost Your Web Marketing Campaigns لصاحبه Stephen Moyers
  10. المصفوفة في الجافا سكريبت تتكون من قائمة عناصر، وتتيح الجافا سكريبت للمبرمجين العديد من الوظائف التي تعمل على المصفوفات. الوظائف التي تقوم بالتعديل على المصفوفة الأصلية تُعرف بتوابع التعديل (Mutator Methods)، والوظائف التي تُعيد قيمة جديدة أو شكل آخر من المصفوفة تُعرف بتوابع الوصول (Accessor Methods). يوجد نوع ثالث من التوابع يُسمى بتوابع التكرار (Iteration Methods) والتي سنتناولها بالشرح في هذا المقال. تُتيح توابع التكرار العمل على كل عنصر في المصفوفة كلٌ على حدة، وترتبط هذه التوابع بشكل أساسي بحلقات التكرار. للاستفادة أكثر من هذا الدرس، يجب أن يكون لديك معرفة مُسبقة بكيفية إنشاء المصفوفات، فهرستها، التعديل عليها والمرور على عناصرها بواسطة حلقة التكرار. تستطيع مراجعة درس فهم المصفوفات في الجافا سكريبت لأخذ فكرة عما سبق. ما سنتناوله في هذا الدرس يشمل شرح استخدام وظائف التكرار لتنفيذ حلقة التكرار على المصفوفات وإجراء عمليات على كل عنصر في المصفوفة، توضيح كيفية ترشيح نتائج العمل على المصفوفة، تلخيص عناصر المصفوفة في قيمة واحدة والبحث عن قيم وفهارس معينة. فهم الدوال السهمية (Arrow Functions) العديد من الأمثلة في هذا المقال ستستخدم صيغة الدوال السهمية في الجافا سكريبت والتي يتم تمثيلها بواسطة علامة المساواة ثم علامة الأكبر من <=. الدالة في الجافا سكريبت عبارة عن كتلة من الشيفرة البرمجية يتم تنفيذها وإعادة استخدامها أكثر من مرة، وعادةً ما تُكتب وفق الصيغة التالية: var example = function() { // code to execute } example(); النسخة الأخيرة من الجافا السكريبت -حتى وقت كتابة هذا الدرس- تسمح باستخدام الدوال السهمية والتي تُكتب وفق الصيغة التالية: var example = () => { // code to execute } example(); في الدوال السهمية تُكتب الأقواس لاستقبال معاملات الدالة. في حالة كان لدينا مُعامل واحد فقط فنستطيع حينها التخلي عن كتابة الأقواس كما في الصيغة التالية: var example = parameter1 => { // code to execute } سنعتمد في شرح هذا الدرس على الدوال السهمية، وللاطلاع أكثر على هذا الموضوع، تستطيع الذهاب الى هذا المرجع . الوظيفة ()forEach تقوم الوظيفة ()forEach باستدعاء دالة ليتم تنفيذها على كل عنصر في مصفوفة معينة. فمثلًا، نفترض وجود المصفوفة fish بالعناصر التالية: let fish = [ "piranha", "barracuda", "cod", "eel" ]; نستطيع استخدام الوظيفة ()forEach لطباعة كل عنصر في المصفوفة fish على الطرفية: // Print out each item in the array fish.forEach(individualFish => { console.log(individualFish); }) Output piranha barracuda cod eel يوجد طريقة أخرى لعمل ما سبق وذلك باستخدام جملة التكرار for بالإضافة لاستخدام الخاصية length: // Loop through the length of the array for (let i = 0; i < fish.length; i++) { console.log(fish[i]); } استخدام جملة for السابقة في طباعة كل عنصر على حدة يعطينا نفس النتيجة عند استخدام ()forEach، ولكن استخدام الطريقة ()forEach يُعتبر خيارًا مختصرًا ومباشرًا وأفضل لمثل هذه الحالات. الوظيفة ()map تقوم الوظيفة ()map ببناء مصفوفة ناتجة عن استدعاء دالة على عناصر مصفوفة أخرى. نستطيع طباعة عناصر مصفوفة عنصر-عنصر كمثال توضيحي على كيفية استخدام وظيفة ()map ولكن على العكس من الوظيفة ()forEach، لابد من حفظ ناتج الوظيفة في متغير جديد. let fish = [ "piranha", "barracuda", "cod", "eel" ]; // Print out each item in the array let printFish = fish.map(individualFish => { console.log(individualFish); }); printFish; Output piranha barracuda cod eel مثال أخر على استخدام الوظيفة ()map، حيث نستطيع أيضًا أن نُغير قيمة كل عنصر في مصفوفة وحفظ العناصر الجديدة الناتجة عن هذا التغيير في مصفوفة أخرى. فمثلًا، يمكننا إضافة الحرف s الى نهاية كل عنصر في المصفوفة fish لجعل الأسماء أسماء جمع. // Pluralize all items in the fish array let pluralFish = fish.map(individualFish => { return `${individualFish}s`; }); pluralFish; Output [ 'piranhas', 'barracudas', 'cods', 'eels' ] المصفوفة الأصلية fish لم تتغير، ولكن المصفوفة الجديدة pluralFish أصبحت الان تحتوي على النسخة المُعدلة من المصفوفة الأصلية. الوظيفة ()filter تقوم الوظيفة ()filter بإنشاء مصفوفة جديدة تحتوي على عناصر من المصفوفة الأصلية تتوافق مع شرط معين. الهدف الرئيسي من هذه الوظيفة هو إجراء عملية الترشيح، فمثلًا، نستطيع أن نحصل من الوظيفة ()filter على مصفوفة جديدة تحتوي على العناصر التي تبدأ بحرف مُعين، وذلك بالاستفادة من خاصية فهرسة النصوص التي تُمكننا من الحصول على الحرف الأول لكل عنصر. let seaCreatures = [ "shark", "whale", "squid", "starfish", "narwhal" ]; // Filter all creatures that start with "s" into a new list let filteredList = seaCreatures.filter(creature => { return creature[0] === "s"; }); filteredList; Output [ 'shark', 'squid', 'starfish' ] في المثال السابق اختبرنا جميع العناصر التي تبدأ بالحرف s وحفظنا النتيجة في مصفوفة جيدة باسم filteredList. الوظيفة ()reduce تقوم الوظيفة ()reduce باختصار مصفوفة في قيمة واحدة، وعادةً يُرى هذا الاستخدام مع المصفوفات التي تحتوي على أرقام، مثل الحصول على مجموع عناصر مصفوفة: let numbers = [ 42, 23, 16, 15, 4, 8 ]; // Get the sum of all numerical values let sum = numbers.reduce((a, b) => { return a + b; }); sum; Output 108 تُستخدم الوظيفة ()reduce أيضًا مع النصوص وأنواع البيانات الأخرى، وتكون القيمة المُرجعة من الوظيفة إما رقم أو نص أو أي نوع بيانات أخر. الوظيفة ()find تُعيد الوظيفة ()find أول عنصر من مصفوفة يتوافق مع شرط مُعين. لتوضيح ذلك، سوف نُنشئ مصفوفة تحتوي على أسماء مخلوقات بحرية، وسنستخدم الوظيفة ()find في إيجاد المخلوقات البحرية ذات النوع الرخوي مثل الأخطبوط (من الرخويات). let seaCreatures = [ "whale", "octopus", "shark", "cuttlefish", "flounder" ]; // Check if a given value is a cephalopod const isCephalopod = cephalopod => { return [ "cuttlefish", "octopus" ].includes(cephalopod); } seaCreatures.find(isCephalopod); Output Octopus بما أن “octopus” هو العنصر الأول في المصفوفة seaCreatures الذي ينطبق عليه الشرط، فإنه هو القيمة الأولى التي تم إرجاعها. نستفيد من الوظيفة ()find في عملنا مع المصفوفات التي تحتوي على قيمة عديدة. الوظيفة ()findIndex تُعيد الوظيفة ()findIndex فهرس أول عنصر من مصفوفة يتوافق مع شرط مُعين. سنستخدم نفس المثال السابق في وظيفة ()find لإيجاد فهرس أول عنصر من المصفوفة من النوع الرخوي. let seaCreatures = [ "whale", "octopus", "shark", "cuttlefish", "flounder" ]; باستخدام المصفوفة isCephalopod مع الوظيفة ()findIndex سنحصل على رقم الفهرس للعنصر بدلا من قيمته. // Check if a given value is a cephalopod const isCephalopod = cephalopod => { return [ "cuttlefish", "octopus" ].includes(cephalopod); } seaCreatures.findIndex(isCephalopod); Output 1 العنصر “octopus” هو العنصر الأول من المصفوفة الذي ينطبق عليه الشرط وموقعه في الفهرس رقم 1 وهي القيمة التي حصلنا عليها. في حالة لم ينطبق الشرط على أية عنصر في المصفوفة، فإن القيمة المرجعة هي -1. const isThereAnEel = eel => { return [ "eel" ].includes(eel); } seaCreatures.findIndex Output -1 خاتمة في هذا الدرس قمنا بمراجعة أغلب وظائف توابع التكرار في الجافا سكريبت. وظائف التكرار تعمل على كل عنصر في المصفوفة كلٌ على حدة وغالبًا ما يتم تطبيق دالة جديدة معها. تم شرح كيفية المرور على المصفوفة وعناصرها من خلال حلقة تكرار، تغيير قيمة العنصر في المصفوفة، ترشيح المصفوفة، اختصار المصفوفة في قيمة واحدة والبحث في المصفوفة باستخدام قيم العناصر او فهارسها. لأخذ فكرة عن المصفوفات تستطيع الاطلاع على درس فهم المصفوفات في الجافا سكريبت . ترجمة -وبتصرّف- للمقال How To Use Array Methods in JavaScript: Iteration Methods لصاحبته Tania Rascia حقوق الصورة البارزة محفوظة لـ Freepik
  11. المصفوفة في جافا سكريبت هي نوع من أنواع البيانات المتاحة فيها وتتكون من قائمة من العناصر. تحتوي الجافا سكريبت على العديد من الوظائف المُرفقة والتي تعمل على المصفوفات. الوظائف التي تقوم بالتعديل على المصفوفة الأصلية تُعرف بتوابع التعديل (Mutator Methods)، والوظائف التي تُعيد قيمة جديدة أو شكلًا آخر من المصفوفة تُعرف بتوابع الوصول (Accessor Methods). في هذا المقال سنتحدث عن توابع الوصول. للاستفادة أكثر من هذا الدرس، يجب أن يكون لديك معرفة مسبقة بكيفية إنشاء المصفوفات، فهرستها، التعديل عليها والمرور على عناصرها بواسطة حلقة التكرار. تستطيع مراجعة درس فهم المصفوفات في الجافا سكريبت لأخذ فكرة عما سبق. هذا الدرس يوضح كيفية دمج المصفوفات ببعضها، تحويل المصفوفات الى نصوص، نسخ أجزاء من مصفوفة وحفظها كمصفوفة جديدة بالإضافة لتحديد فهارس المصفوفة. الوظيفة ()concat تقوم الوظيفة ()concat بدمج مصفوفتين أو أكثر ببعضها البعض لينتج لدينا مصفوفة جديدة. في المثال التالي نقوم بإنشاء مصفوفتين تحتويان على أنواع الأسماك الصدفية ومن ثم نقوم بدمجهما في مصفوفة واحدة. // Create arrays of monovalves and bivalves let monovalves = [ "abalone", "conch" ]; let bivalves = [ "oyster", "mussel", "clam" ]; // Concatenate them together into shellfish variable let shellfish = monovalves.concat(bivalves); بمجرد استدعائنا للمصفوفة الجديدة، سنرى أنها تحتوي على العناصر الموجودة في كلا المصفوفتين السابقتين. shellfish; Output [ 'abalone', 'conch', 'oyster', 'mussel', 'clam' ] نستطيع تمرير أكثر من مُعطى للوظيفة ()concate لتسمح لنا بكفاءة وبخطوة واحدة أن ندمج مجموعة مصفوفات ببعضها البعض. الوظيفة ()join تقوم الوظيفة ()join بتحويل جميع عناصر مصفوفة معينة إلى قيمة نصية. في المثال التالي، تقوم الوظيفة بعملية تحويل عناصر المصفوفة fish الى قيمة نصية. لاحظ أنه بدون أن نُمرر أي معطى للوظيفة ()join سيتم فصل عناصر المصفوفة عن بعضها بواسطة فاصلة عادية وذلك في القيمة النصية الناتجة. let fish = [ "piranha", "barracuda", "koi", "eel" ]; // Join the elements of an array into a string let fishString = fish.join(); fishString; Output 'piranha,barracuda,koi,eel' اذا أردنا أن يتم فصل العناصر عن بعضها البعض بواسطة مسافة أو أي فاصل اخر، فعلينا أن نضيف الفاصل الذي نرغب به كمُعطى للوظيفة ()join. // Join the elements of an array into a string let fishString = fish.join(', '); fishString; Output 'piranha, barracuda, koi, eel' في المثال السابق، مررنا الفاصل ‘, ’ الذي يحتوي على فاصلة عادية ومسافة وذلك لفصل عناصر المصفوفة ولكي نحصل على نص مقروء بطريقة أفضل. في حالة قمنا بتمرير فاصل فارغ للوظيفة ()join، فإن الفاصلة التلقائية سيتم ازالتها بالكامل. الوظيفة ()slice نستخدم الوظيفة ()slice لنسخ أجزاء من مصفوفة إلى مصفوفة أخرى جديدة. let fish = [ "piranha", "barracuda", "koi", "eel" ]; لنفترض أننا نربد نسخ أخر عنصرين في المصفوفة fish الى مصفوفة جديدة، فإننا سنبدأ بالنسخ من الفهرس 2 الذي يمثل موقع العنصر “koi” وسينتهي النسخ عند رقم الفهرس الذي يلي فهرس أخر عنصر نريد نسخه. بما أن فهرس أخر عنصر “eel” هو 3، فإننا سنمرر للمعطى الثاني القيمة 4. // Slice a new array from 2 to 5 let fishWithShortNames = fish.slice(2, 4); fishWithShortNames; Output [ 'koi', 'eel' ] في هذه الحالة الخاصة، وبسبب أن العنصر “eel” هو أخر عنصر في المصفوفة، فإن المُعطى الأخير في الوظيفة ()slice غير ضروري، حيث أن النسخ سيبدأ من فهرس البداية وينتهي لنهاية المصفوفة عند عدم تمرير المعطى الثاني. // Slice a new array from 2 to the end of the array let fishWithShortNames = fish.slice(2); fishWithShortNames; Output [ 'koi', 'eel' ] انتبه ألا يختلط عليك الأمر مع وظيفة توابع التعديل المسماة ()splice والتي تقوم بإضافة أو حذف العناصر من المصفوفة الأصلية. الوظيفة ()indexOf تُعيد هذه الوظيفة رقم الفهرس لأول وجود لعنصر معين في مصفوفة، وتظهر فائدة هذه الوظيفة بشكل واضح في المصفوفات التي تحتوي قيم عديدة ومتكررة. في المثال التالي، يتكرر وجود العنصر”barracuda” مرتين في المصفوفة fish. let fish = [ "piranha", "barracuda", "koi", "barracuda" ]; باستخدام الوظيفة ()indexOf نستطيع الحصول على موقع العنصر الأول من القيمة “barracuda”. // Find the first instance of an element fish.indexOf("barracuda"); Output 1 إذا قمنا بتمرير قيمة غير موجودة في المصفوفة فإن نتيجة الوظيفة ستكون القيمة الخاصة -1. fish.indexOf("shark"); Output -1 الوظيفة ()lastIndexOf تُعيد هذه الوظيفة رقم الفهرس للوجود الأخير لعنصر معين في مصفوفة. نستطيع اختبار هذه الوظيفة على نفس المثال السابق الذي يحتوي على تكرار القيمة “barracuda” مرتين. let fish = [ "piranha", "barracuda", "koi", "barracuda" ]; // Find the last instance of an element fish.lastIndexOf("barracuda"); Output 3 الوظيفة ()lastIndexOf تبدأ بالبحث عن العنصر ابتداءً من نهاية المصفوفة وبذلك فهي فعليًا تعيد فهرس أول عنصر تجده أثناء عملية البحث. خاتمة من خلال هذا الدرس قمنا بمراجعة وظائف توابع الوصول المرفقة مع مصفوفات الجافا سكريبت. توابع الوصول تقوم بإنشاء نسخة جديدة من المصفوفة بعكس توابع التعديل التي تقوم بتغيير المصفوفة الأصلية. تعلمنا كيفية دمج مصفوفتين أو أكثر، تحويل عناصر المصفوفة الى مجموعة قيم نصية مُجزأة بفاصلة عادية بالإضافة الى كيفية نسخ أجزاء من المصفوفة الى مصفوفة جديدة. وفي النهاية تعلمنا كيفية الحصول على فهرس أول وجود لعنصر أو أخر وجود له في مصفوفة معينة. لمراجعة أساسيات المصفوفات، راجع درس فهم المصفوفات في الجافا سكريبت. ترجمة -وبتصرّف- للمقال How To Use Array Methods in JavaScript Mutator Methods لصاحبته Tania Rascia
  12. مقدمة تُعتبر خدمات تخزين البيانات المرنة والقابلة للتوسع حسب الحاجة، متطلب أساسي لأغلب التطبيقات والخدمات التي يتم تطويرها بالأدوات والتقنيات الحديثة. بغض النظر عن تخزين كميات قليلة أو كثيرة من الصور، الفيديوهات والنصوص الضخمة فإن مُطوري التطبيقات يحتاجون حلًا لتخزين واسترجاع المحتوى الخاص بالمستخدم، سجلات عمله، نسخه الاحتياطية وغيره من الأمور الأخرى. في ظل الأنظمة المنشورة المعقدة، وفي ظل الحاويات المختلفة والبيئات سريعة التغير والزوال، فإن زمن حفظ الملفات ببساطة في وحدة تخزين على خادوم واحد قد انتهى، حيث قام مزودو الخدمات السحابية بتطوير خدمات لتلبية حاجات التخزين في الأنظمة والتطبيقات الحديثة، وغالبًا تندرج خدمات التخزين السحابية تحت نوعين هما التخزين الكائني (Object Storage) والتخزين الكتلي (Block Storage). سنتناول هنا مميزات وسلبيات كل نوع. ما هو التخزين الكتلي؟ تعتبر خدمات التخزين الكتلي بسيطة ومألوفة نوعًا ما، حيث تُقدم هذه الخدمات خدمة التخزين العادية –كما في القرص الصلب– ولكن من خلال الشبكة. يقدم مزودو الخدمات السحابية الأدوات اللازمة التي تُتيح الحصول على جهاز تخزين كتلي بأي حجم وربطه بالآلة الافتراضية الخاصة بك، ومن هنا تستطيع التعامل مع الجهاز كقرص عادي، حيث تستطيع تهيئته، حفظ الملفات عليه، ربط أكثر من قرص بنظام RAID أو حتى إعداد قاعدة بيانات عليه للكتابة مباشرةً على الجهاز الكتلي. بالإضافة لما سبق، فإن أجهزة التخزين المرتبطة بشبكة غالبًا ما تتميز عن الأقراص الصلبة العادية بما يلي: تستطيع أخذ نسخة حية بسهولة للجهاز للأغراض الاحتياطية. من الممكن إعادة تغيير حجم جهاز التخزين الكتلي وتلبية الاحتياجات المتراكمة. تستطيع فك وربط الجهاز بين الآلات الوهمية بسهولة. ما ذكرناه من مميزات هي خصائص مرنة من الممكن أن تكون مفيدة غالبًا لأي تطبيق. فيما يلي نسرد بعضًا من مميزات وعيوب هذه التقنية. مميزات التخزين الكتلي: التخزين الكتلي شائع ومألوف، حيث أن المستخدمين والبرمجيات يفهمونه ويستطيعون التعامل مع ملفاته وأنظمته بشكل واسع. الأجهزة الكتلية مدعومة جيدًا. كل لغات البرمجة تستطيع قراءة وكتابة الملفات منها. صلاحيات وأذونات ملفات النظام في الأجهزة الكتلية مألوفة ومفهومة بشكل جيد. أجهزة التخزين الكتلي لديها وقت استجابة منخفض في عمليات الإدخال والإخراج، مما يجعلها مناسبة لقواعد البيانات. عيوب التخزين الكتلي: التخزين مرتبط بخادم واحد في نفس الوقت. تحتوي كتلة التخزين وملفات النظام على بيانات وصفية (metadata) قليلة عن المعلومات التي تُخزنها مثل تاريخ الإنشاء، المستخدم الذي تعود له البيانات وحجم هذه البيانات. أي معلومات إضافية عما تُخزنه وحدة التخزين الكتلي يجب أن يتم التعامل معها على مستوى التطبيق أو قاعدة البيانات مما يؤدي إلى زيادة الأعباء على المطور لمتابعة هذه المعلومات. يجب عليك أن تدفع مقابل حجم التخزين الذي حجزته حتى لو لم تقم بالاستفادة منه أو استخدامه. تستطيع أن تصل لخدمة التخزين الكتلي عبر خادومٍ واحد يعمل فقط. التخزين الكتلي يحتاج إلى اعدادات تنصيب وعمل أكثر (اختيار ملفات النظام، الأذونات، الإصدارات، النسخ الاحتياطية...وغيرها) مقارنة بالتخزين الكائني. بسبب سرعة عمليات الإدخال والإخراج في التخزين الكتلي ، فإنه يُعتبر خيارًا أفضل لتخزين البيانات في قواعد البيانات العادية. بالإضافة لذلك، فإن العديد من الأنظمة القديمة تتطلب وجود ملفات نظام عادية للتخزين مما يجعل التخزين الكتلي هو الحل في هذه الحالة. إذا لم يتوفر لدى مزود الخدمات السحابية خدمة التخزين الكتلي، تستطيع أن تقوم بتشغيل الخدمة الخاصة بك بنفسك باستخدام OpenStack Cinder، Ceph، أو خدمة iSCSI المُضمنة مع العديد من أجهزة NAS. ما هو التخزين الكائني؟ في عالم الحوسبة السحابية الحديث، يُعرف التخزين الكائني بأنه تخزين واسترجاع البيانات غير المُرتبة وفق بنية معينة (Unstructured) عبر استخدام الواجهة البرمجية ل HTTP. فبدلًا من تقسيم الملفات إلى كُتل متعددة وحفظها باستخدام نظام الملفات على وحدة التخزين، يتم التعامل مع الملفات ككائنات وتُحفظ على الشبكة. قد تكون هذه الكائنات عبارة عن صورة، سجلات، ملفات HTML أو أية كائنات ثنائية كبيرة بذاتها. تكون هذه الكائنات غير مرتبة وفق بنية معينة لأنها لا تحتاج لاتباع مُخطط أو تنسيق محدد. أصبح التخزين الكائني معروفًا بسبب قدرته الكبيرة على تبسيط تجربة المطور في بناء وتطوير خدمات التخزين، حيث تم تطوير مكتبات تدعم بناء الواجهات البرمجية (التي تحتوي طلبات (HTTP في أغلب لغات البرمجة. حفظ ملف ثنائي كبير أصبح أمرًا سهلًا باستخدام طلب HTTP PUT، وكذلك استرجاع الملف وبياناته الوصفية يتم عبر طلب GET عادي. علاوة على ذلك، فأغلب خدمات التخزين الكائني تستطيع مشاركة هذه الملفات مع المستخدمين الآخرين دون الحاجة لإعدادات خاصة على الخادم المُستضيف. بالإضافة لما سبق، لن تَدفع مقابل خدمة التخزين الكائني إلا مقابل المساحة التي تَستخدمها (بعض الخدمات تُدفع مقابل كل طلب HTTP أو حسب استهلاك النقل عبر النطاق) وهذا الأمر يُعد نعمةً لبعض المطورين الذين يحصلون على خدمة استضافة وتخزين عالمية مقابل تكلفة تتلاءم مع حجم الاستخدام. التخزين الكائني ليس الحل الجيد لكل الحالات. لنلقي نظرة على مميزات وعيوب هذه النوع. مميزات التخزين الكائني: واجهة برمجية بسيطة عبر HTTP تدعم أغلب المستخدمين بغض النظر عن نظام التشغيل المستخدم أو لغة البرمجة. تكلفة التخزين تُحدد مقابل ما يتم استخدامه من مساحة وليس ما يتم حجزه. لن تحتاج لحجز خادوم كامل للحصول على خدمة التخزين الكائني لملفاتك الساكنة. بعض مزودي خدمة التخزين الكائني تُقدم خاصية الدمج CDN المُضمنة، والتي تتيح تخبئة ملفاتك حتى يكون تنزيلها أسرع للمستخدمين. خاصية الأَصْدَرَة (Versioning) التي تتيح لك استرجاع النسخ القديمة من ملفاتك واستعادتها بعد التعديل عليها بشكل خاطئ. تتكيف خدمات التخزين الكائني مع احتياجات الاستخدام بدءًا من الاستخدام البسيط وحتى حالات الاستخدام المكثف دون أن يحتاج المطور لزيادة المصادر أو إعادة الهيكلة لمعالجة الحمل المطلوب. باستخدام خدمات التخزين الكائني لن تحتاج لمتابعة وإدارة وحدات التخزين أو أنظمة RAID لأنها من مسئولية مزود الخدمة. تستطيع حفظ بيانات وصفية بجانب البيانات الأصلية وذلك يُبسط لك هيكلة التطبيق الذي تعمل عليه. عيوب التخزين الكائني: لا تستطيع أن تستضيف قاعدة بيانات عادية على خدمة التخزين الكائني بسبب وقت الاستجابة العالي واللازم لقاعدة البيانات. باستخدام التخزين الكائني لا تستطيع التعديل على جزء من الكائن الذي تحفظه، حيث عليك قراءة أو كتابة الكائن كله في العملية الواحدة وهذا له آثار مترتبة على الأداء. فمثلًا، باستخدام نظام الملفات تستطيع بسهولة أن تُضيف سطر في نهاية ملف سجلات، ولكن في نظام التخزين الكائني يجب عليك قراءة الملف كاملا ثم التعديل عليه ثم إعادة كتابته وحفظه مرة أخرى، وهذا الأمر يجعل من التخزين الكائني خيارًا غير مثالي في حفظ البيانات التي تتغير بشكل مستمر. لا تستطع نُظم التشغيل تعيين كائن كمحرك أقراص بشكل سهل. يوجد بعض العملاء والمحولات التي تساعد في تنفيذ هذا الأمر، ولكن بشكل عام، استخدام الكائن وتصفحه ليس متاحًا بسهولة كما هو متاح لدى التنقل خلال المجلدات في نظام الملفات. بسبب هذه الخصائص، التخزين الكائني مفيد في استضافة الملفات الساكنة، حفظ محتوى المستخدم المتمثل مثلًا في الصور ولقطات الفيديو، حفظ ملفات النسخ الاحتياطي والسجلات. يوجد بعض حلول التخزين الكائني التي تتيح لك حفظ ملفاتك وبياناتك دون القلق على كيفية إدارة الأقراص أو خيارات التوسع. تستطيع مثلا أن تُجرب Minio، وهو خادوم مشهور مُطَوَّرْ باستخدام لغة Go ويقدم خدمة التخزين الكائني، أو تستطيع تجربة Ceph أو OpenStack Swift. خاتمة اختيار طريقة التخزين قد يكون قرارًا معقدًا للمطورين. ناقشنا مزايا وعيوب كلًا من التخزين الكتلي والتخزين الكائني. على الأرجح أن أي تطبيق معقد وذو كفاءة سيحتاج لاستخدام كلا النوعين في التخزين لتلبية كافة الاحتياجات اللازمة للعمل. ترجمة -وبتصرّف- للمقال Object Storage vs. Block Storage Services لصاحبه Brian Boucheron
  13. تتكون المصفوفة في الجافا سكريبت من قائمة عناصر، وتحتوي الجافا سكريبت على العديد من الوظائف المرفقة والتي تعمل على المصفوفات. الوظائف التي تقوم بالتعديل عل المصفوفة الأصلية تُعرف بتوابع التعديل (Mutator Methods)، والوظائف التي تعيد قيمة جديدة أو شكل آخر تُعرف بتوابع الوصول (Accessor Methods). للاستفادة أكثر من هذا المقال، يجب أن يكون لديك معرفة مسبقة بكيفية إنشاء المصفوفات، فهرستها، التعديل عليها والمرور على عناصرها بواسطة حلقة التكرار. تستطيع مراجعة درس فهم المصفوفات في الجافا سكريبت لأخذ فكرة عما سبق. المصفوفات تتشابه مع النصوص من حيث أن كلاهما يتكون من مجموعة من العناصر التي يمكن الوصول لها عبر رقم الفهرس. على الرغم من ذلك، من الأهمية أن نتذكر أن النصوص ثابتة وغير قابلة للتعديل (Immutable). ولكن على الجانب الاّخر، فإن المصفوفات غير ثابتة (Mutable)، ونستطيع التعديل عليها بشكل مباشر. في هذا المقال سوف نشرح عمليات إضافة وإزالة العناصر من المصفوفة، عكس المصفوفة، استبدال المصفوفة والتعديل على عناصرها. الوظيفة ()isArray قبل البدء بشرح توابع التعديل، سنتعرض للوظيفة ()isArray والتي تقوم بفحص هل الكائن عبارة عن مصفوفة أم لا. في حالة كان الكائن الذي نقوم بفحصه من نوع مصفوفة، فإن الوظيفة ستعيد القيمة المنطقية true، وفي حالة لم يكن مصفوفة، فإن الوظيفة ستعيد القيمة المنطقية false. let fish = [ "piranha", "barracuda", "koi", "eel" ]; // Test if fish variable is an array Array.isArray(fish); Output True الوظيفة ()isArray مفيدة بسبب أن العملية typeof التي تُستخدم عادة في فحص هذه الحالات ستعيد لنا القيمة object عند استخدامها مع المصفوفة، ومن المهم في بعض الأحيان معرفة هل الكائن مصفوفة أم لا. لاحظ أن الوظيفة ()isArray تُكتب بطريقة مختلفة عن أغلب وظائف المصفوفات، حيث يتم تمرير الكائن الذي يمثل المصفوفة كمُعطى للوظيفة. بعد أن تعرفنا على الوظيفة التي تساعدنا في فحص هل الكائن الذي نتعامل معه مصفوفة أم لا، سنبدأ بشرح وظائف توابع التعديل. الوظيفة ()pop أول وظيفة سنتناولها في الشرح هي وظيفة ()pop، التي تُزيل العنصر الأخير من نهاية مصفوفة. لنفترض أنه لدينا مصفوفة باسم fish تحتوي على أنواع بعض الأسماك: let fish = [ "piranha", "barracuda", "koi", "eel" ]; عند استدعاء الوظيفة ()pop سيتم إزالة اخر عنصر في المصفوفة، والذي سيكون العنصر الذي يساوي القيمة النصية “eel” : // Use pop method to remove an item from the end of an array fish.pop(); نقوم بطباعة محتوى المصفوفة لنتأكد من أنها أصبحت لا تحتوي القيمة المحذوفة: fish; Output [ 'piranha', 'barracuda', 'koi' ] بذلك نكون قد أزلنا العنصر “eel” من المصفوفة بنجاح. الوظيفة ()pop لا تأخذ أي معاملات أخرى. الوظيفة ()shift الوظيفة ()shift هي وظيفة أخرى من توابع التعديل وتقوم بإزالة العنصر الأول من المصفوفة. لنفترض وجود المصفوفة fish بالعناصر التالية: let fish = [ "piranha", "barracuda", "koi", "eel" ]; نستخدم الوظيفة ()shift لإزالة العنصر الأول “piranha” والموجود في الفهرس رقم 0، وكذلك سيتم إزاحة كافة عناصر المصفوفة بمقدار فهرس واحد للأسفل. // Use shift method to remove an item from the beginning of an array fish.shift(); fish; Output [ 'barracuda', 'koi', 'eel' ] في المثال السابق، تم إزالة العنصر الأول وإجراء عملية الإزاحة لكافة العناصر الباقية، لهذا السبب، وبشكل عام، فإنه يُفضل أن يتم استخدام الوظيفة ()pop قدر المستطاع لإزالة العناصر من المصفوفة لعدم وجود عملية إزاحة العناصر عند استخدامها. الوظيفة ()push الوظيفة ()push تعمل على إضافة عنصر جديد أو عدة عناصر جديدة في نهاية المصفوفة. let fish = [ "piranha", "barracuda", "koi", "eel" ]; لإضافة عنصر جديد، نمرر العنصر للوظيفة كمُعامل: // Use push method to add an item to the end of an array fish.push("swordfish"); fish; Output [ 'piranha', 'barracuda', 'koi', 'eel', 'swordfish' ] من الممكن أيضًا أن نضيف أكثر من عنصر دفعة واحدة لنهاية المصفوفة بالشكل التالي: fish.push("swordfish", "dragonfish") الوظيفة unshift() لإضافة عنصر جديد أو عدة عناصر في بداية المصفوفة، نستخدم الوظيفة ()unshift. let fish = [ "piranha", "barracuda", "koi", "eel" ]; // Use unshift method to add an item to the beginning of an array fish.unshift("shark"); fish; Output [ 'shark', 'piranha', 'barracuda', 'koi', 'eel' ] في المثال السابق، العنصر shark”” تم اضافته في الفهرس رقم 0، مع إزاحة كافة العناصر للإمام. نستطيع إضافة عدة عناصر دفعة واحدة بنفس الطريقة التي استخدمناها مع الوظيفة ()shift وذلك بتمرير العناصر بحيث تكون مفصولة عن بعضها بفاصلة عادية. الوظيفتان ()pop و ()push تؤثران على نهاية المصفوفة، والوظيفتان ()shift و ()unshift تؤثران على بداية المصفوفة. يوجد طريقة سهلة لتذكر هذه المعلومة وهي معرفة أن الوظيفتان ()shift و ()unshift تقومان بعمل إزاحة للعناصر في المصفوفة. الوظيفة ()splice تقوم الوظيفة ()splice بإضافة أو إزالة عنصر من أي موقع في المصفوفة. الوظيفة تقوم إما بالإضافة أو الحذف كلٌ على حدة، أو الإضافة والحذف بشكل متزامن. تأخذ الوظيفة ()splice ثلاث معاملات، الأول هو الفهرس الذي سنبدأ من عنده العملية، الثاني هو عدد العناصر التي نريد حذفها، الثالث هو العنصر الذي نريد اضافته (اختياري). splice(index number, number of items to remove, items to add) الأمثلة التالية توضح كيفية استخدام الوظيفة ()splice في إضافة وحذف العناصر في المصفوفة. الإضافة باستخدام ()splice إذا قمنا بإعداد المعامل الثاني في الوظيفة ()splice ليأخذ القيمة 0، فإنه لن يتم حذف أي عنصر. بهذه الطريقة، نستطيع أن نضيف عنصر لمصفوفة في أي موقع فيها، وهذه الطريقة تُعتبر أفضل وأقوى من استخدام الوظيفتين ()push و ()unshift واللتان تقومان بالإضافة في نهاية المصفوفة أو بدايتها فقط. let fish = [ "piranha", "barracuda", "koi", "eel" ]; // Splice a new item number into index position 1 fish.splice(1, 0, "manta ray"); fish; Output [ 'piranha', 'manta ray', 'barracuda', 'koi', 'eel' ] النص الجديد “manta ray” تم إضافته للمصفوفة في الفهرس رقم 1. الإزالة باستخدام ()splice إذا تجاهلنا المُعامل الثالث (الاختياري) في الوظيفة ()splice، نستطيع ببساطة إزالة أي عنصر في المصفوفة ومن أي موقع فيها: let fish = [ "piranha", "barracuda", "koi", "eel" ]; // Remove two items, starting at index position 1 fish.splice(1, 2); fish; Output [ 'piranha', 'eel' ] في المثال السابق، قمنا بحذف عنصرين من المصفوفة ابتداء من الفهرس 1. إذا لم نقم بإدخال المعامل الثاني الذي يُمثل عدد العناصر التي نريد حذفها، سيتم حذف جميع عناصر المصفوفة ابتداءً من الفهرس 1 وحتى نهاية المصفوفة. الإضافة والإزالة باستخدام ()splice باستخدام كافة المعاملات للوظيفة ()splice، نستطيع إجراء كلتا عمليتي الإضافة والحذف في نفس الوقت. لتوضيح هذا، لنقم بحذف نفس العناصر السابقة في المثال قبل السابق واضافة العنصر الجديد في الفهرس 1: let fish = [ "piranha", "barracuda", "koi", "eel" ]; // Remove two items and add one fish.splice(1, 2, "manta ray"); fish; Output [ 'piranha', 'manta ray', 'eel' ] الوظيفة ()splice تُعتبر وظيفة قوية في إجراء التعديلات على أي مصفوفة. انتبه أن لا يكون لديك لبس بين الوظيفة ()splice والوظيفة ()slice التي تُعتبر وظيفة من توابع الوصول وتقوم بإنشاء نسخة من جزء من مصفوفة. الوظيفة ()reverse تقوم هذه الوظيفة بعكس ترتيب العناصر في المصفوفة. let fish = [ "piranha", "barracuda", "koi", "eel" ]; باستخدام الوظيفة ()reverse، فإن العنصر الأخير سيكون الأول، والعنصر الأول سيكون اّخر العناصر، ولا تأخذ هذه الوظيفة أي معاملات. // Reverse the fish array fish.reverse(); fish; Output [ 'eel', 'koi', 'barracuda', 'piranha' ] الوظيفة ()fill تقوم الوظيفة ()fill باستبدال جميع عناصر المصفوفة بقيمة ثابتة. let fish = [ "piranha", "barracuda", "koi", "eel" ]; لنفترض وجود المصفوفة fish ولتحتوي على 4 عناصر، باستخدام الوظيفة ()fill سنستبدل جميع العناصر الأربعة بقيمة واحدة: // Replace all values in the array with "shark" fish.fill("shark"); fish; Output [ 'shark', 'shark', 'shark', 'shark' ] نستطيع أن نمرر للوظيفة ()fill معاملين إضافيين وكلاهما اختياري، ويمثلان فهرس البداية الذي نبدأ من عنده عملية الاستبدال وفهرس النهاية الذي ننهي عملية الاستبدال قبله. fish.fill("shark", 1) // > [ 'piranha', 'shark', 'shark', 'shark' ] fish.fill("shark", 1, 3); // > [ 'piranha', 'shark', 'shark', 'eel' ] الوظيفة ()sort الوظيفة ()sort تقوم بترتيب عناصر المصفوفة بناءً على الحرف الأول في العنصر. في حالة وجود أكثر من عنصر بنفس الحرف الأول، يتم الترتيب بناءً على الحرف الثاني وهكذا. بشكل تلقائي تقوم الوظيفة بالترتيب الأبجدي لمصفوفة نصية والتي يكون جميع عناصرها إما uppercase أو lowercase. let fish = [ "piranha", "barracuda", "koi", "eel" ]; // Sort items in array fish.sort(); fish; Output [ 'barracuda', 'eel', 'koi', 'piranha' ] بما أن الوظيفة ()sort تعتمد على الترميز النصي للحرف الأول، فإنها ستُرتب العناصر التي تكون حالتها uppercase قبل العناصر التي حالتها lowercase. انظر للمثال التالي: let fish = [ "piranha", "barracuda", "Koi", "eel" ]; fish.sort(); fish; Output [ 'Koi', 'barracuda', 'eel', 'piranha' ] الأرقام تأتي قبل الأحرف بغض النظر عن حالتها في ترتيب المصفوفة. انظر للمثال التالي: let fish = [ "piranha", "barracuda", "Koi", "1 eel" ]; fish.sort(); Output [ '1 eel', 'Koi', 'barracuda', 'piranha' ] لن تقوم الوظيفة بترتيب مصفوفة أعداد حسب قيمة الأعداد فيها (الأصغر ثم الأكبر)، ولكنها ستقوم بفحص الرقم الأول في كل عدد ويتم الترتيب بناءً عليه. let numbers = [ 42, 23, 16, 15, 4, 8 ]; numbers.sort(); Output [ 15, 16, 23, 4, 42, 8 ] لترتيب الأعداد في مصفوفة أعداد بطريقة سليمة وحسب قيمة الأعداد (من الأصغر إلى الأكبر)، نستطيع إنشاء وظيفة “مقارنة” ونمررها كمعامل للوظيفة ()sort: // Function to sort numbers by size const sortNumerically = (a, b) => { return a - b; } numbers.sort(sortNumerically); Output [ 4, 8, 15, 16, 23, 42 ] خاتمة في هذا الدرس، قمنا باستعراض أغلب وظائف توابع التعديل في لغة الجافا سكريبت. الوظائف من هذا النوع تقوم بالتعديل على المصفوفة الأصلية التي تستخدمها، على العكس من وظائف توابع الوصول، وتعلمنا خلال هذا الدرس كيفية إضافة وإزالة العناصر من المصفوفة في بدايتها أو نهايتها، بالإضافة لكيفية ترتيب المصفوفة، عكسها واستبدال عناصرها. لمراجعة أساسيات المصفوفات، راجع درس فهم المصفوفات في الجافا سكريبت. ترجمة -وبتصرّف- للمقال How To Use Array Methods in JavaScript Mutator Methods لصاحبه Tania Rascia حقوق الصورة البارزة محفوظة لـ Freepik
  14. مقدمة تُعتبر المصفوفة في الجافا سكريبت كائن عمومي (global) الغرض منه هو تخزين البيانات، وتحتوي المصفوفة إما على مجموعة من العناصر بنوع بيانات واحد أو أكثر، وقد تكون فارغة. نستخدم الفهارس العددية التي تبدأ من القيمة 0 للوصول إلى عناصر المصفوفة. المصفوفات مفيدة جدًا بما أنها تُخزن عدة قيم في متغير واحد، وهذا الأمر يقلل وينظم الشيفرة البرمجية التي نكتبها ويجعلها أكثر ملائمة للقراءة والصيانة. تستطيع المصفوفة أن تحتوي على أي نوع بيانات، ابتداءً من الأرقام ومرورا بالنصوص والكائنات وغيرها من أنواع البيانات. لتوضيح كيف من الممكن أن تكون المصفوفات مهمة، لنفترض أننا نريد أن نحفظ أسماء المحيطات في متغيرات عدة، بحيث يكون لكل محيط المتغير الخاص به: oceans.js // Assign the five oceans to five variables const ocean1 = "Pacific"; const ocean2 = "Atlantic"; const ocean3 = "Indian"; const ocean4 = "Arctic"; const ocean5 = "Antarctic"; هذه الطريقة مُضجرة جدا، وتُصبح أكثر صعوبة بشكل متسارع في المتابعة والصيانة. باستخدام المصفوفات، نستطيع تبسيط الأمر. oceans.js // Assign the five oceans let oceans = [ "Pacific", "Atlantic", "Indian", "Arctic", "Antarctic", ]; بدلًا من استخدام خمسة متغيرات منفصلة، نستطيع الان أن يكون لدينا متغير واحد يحتوي على جميع العناصر الخمسة. لإنشاء المصفوفة، نستخدم الأقواس المربعة [ ] كما هو واضح في الشيفرة البرمجية السابقة، وللوصول إلى عنصر معين في المصفوفة، نستخدم الفهرس مع المصفوفة بالطريقة التالية: // Print out the first item of the oceans array oceans[0]; Output Pacific في هذا الدرس سنتعلم كيفية بناء المصفوفة، وكيفية الوصول إلى عناصرها، والاضافة إليها وتعديلها والحذف منها، كما سنتعلم كيفية المرور خلال عناصرها باستخدام حلقة التكرار. إنشاء مصفوفة يوجد طريقتان لإنشاء المصفوفة في جافا سكريبت: التعريف اللفظي باستخدام الأقواس المعكوفة. التعريف بواسطة الباني (constructor) باستخدام كلمة new. لنوضح كيفية إنشاء مصفوفة تحتوي على أنواع سمك القرش، وذلك باستخدام التعريف اللفظي بواسطة الأقواس المربعة: sharks.js // Initialize array of shark species with array literal let sharks = [ "Hammerhead", "Great White", "Tiger", ]; الان نُعرف نفس المصفوفة باستخدام الباني وذلك بواسطة الجملة new Array() : sharks.js // Initialize array of shark species with array constructor let sharks = new Array( "Hammerhead", "Great White", "Tiger", ); كلا الطريقتين سوف يُنشئ لنا المصفوفة، ولكن طريقة التعريف اللفظي هي المشهورة والأكثر تفضيلا بما أن التعريف باستخدام الباني قد يؤدي إلى نتائج غير مستقرة وغير متوقعة وعليك الانتباه في حال صادفتك تلك الطريقة في التعريف أو في حال استخدامك لها. نستطيع طباعة محتويات المصفوفة بكتابة المتغير الخاص بها مباشرة: // Print out the entire sharks array sharks; Output [ 'Hammerhead', 'Great White', 'Tiger' ] تُستخدم المصفوفات عادة في تجميع العناصر أو القوائم من نفس نوع البيانات، ولكن من الناحية التقنية، فإن المصفوفات تستطيع أن تحتوي على عناصر من أنواع مختلفة بالإضافة إلى إمكانية أن تحتوي على مصفوفات أخرى: // Initialize array of mixed datatypes let mixedData = [ "String", null, 7, [ "another", "array", ], ]; بعد أن تعلمنا كيفية إنشاء المصفوفة، نستطيع الان التعامل معا بأكثر من طريقة، ولكننا في البداية نحتاج الى فهم كيفية فهرسة المصفوفات (Arrays Indexing). ملاحظة: قد تجد اخر عنصر في المصفوفة ينتهي بفاصلة وأحيانا قد لا تجد هذه الفاصلة. تُعرف هذه الفاصلة بالفاصلة التابعة (Trailing comma)، ومن الشائع ان تكون غير موجودة، ولكن بشكل عام أصبح من الأفضل أن يتم استخدامها في الشيفرة البرمجية بسبب أنها تجعل الاختلافات بين الإصدارات (في عملية إدارة الإصدارات Versions Control) أكثر وضوحا وتسهل من إضافة وإزالة عناصر المصفوفة دون أخطاء. لاحظ أن الفاصلة التابعة غير مسموح بها في ملفات JSON. فهرسة المصفوفات إذا تعاملت مسبقاً مع النصوص والفهرسة في الجافا سكريبت، ستكون مُلمًا بمفهوم فهرسة المصفوفات، حيث أن النص يُعتبر شبيهًا بالمصفوفة. لا تحتوي المصفوفات على عناصر مزدوجة على شكل اسم/قيمة، وبدلا من ذلك، فإن المصفوفات تُفهرس بقيم عددية تبدأ من القيمة 0. المثال التالي ينشئ مصفوفة باسم seaCreatures: seacreatures.js let seaCreatures = [ "octopus", "squid", "shark", "seahorse", "starfish", ]; الجدول التالي يُفصل كيف يتم فهرسة كل عنصر في المصفوفة بقيمة عددية ابتداءً من 0: octopus squid shark seahorse starfish 0 1 2 3 4 العنصر الأول في المصفوفة هو octopus ومُفهرس في الموقع 0 من المصفوفة، والعنصر الأخير هو starfish ومُفهرس في الموقع 4. تبدأ الفهرسة من 0، وهذا يتضارب مع طبيعتنا الفطرية ببدء العد من القيمة 1، لذلك نحتاج لأخذ الاحتياط وأن نتذكر هذه النقطة دائما حتى تصبح طبيعية. نستطيع أن نحصل على عدد العناصر في المصفوفة باستخدام الخاصية length: seaCreatures.length; Output 5 على الرغم من أن الفهارس الخاصة بالمصفوفة seaCreatuers تبدأ من 0 إلى 4، فإن الخاصية length سوف تُرجع العدد الفعلي للعناصر الموجودة في المصفوفة. إذا أردنا معرفة رقم الفهرس لعنصر معين في المصفوفة، وليكن مثلا seahorse، نستطيع أن نستخدم لذلك الوظيفة indexOf() : seaCreatures.indexOf("seahorse"); Output 3 إذا لم تحتوي المصفوفة على العنصر الذي نريده، فلن نحصل على رقم فهرس لعنصر غير موجود، وفي هذه الحالة، فإن الوظيفة سترجع لنا القيمة -1 كما في المثال التالي: seaCreatures.indexOf("cuttlefish"); Output -1 بواسطة أرقام الفهارس المرتبطة بعناصر المصفوفة، فإنه لدينا القدرة على الوصول لكل عنصر بشكل منفرد بهدف العمل على هذا العنصر والتعامل معه. الوصول لعناصر المصفوفة يتم الوصول لعنصر في مصفوفة جافا سكريبت بواسطة الإشارة لرقم الفهرس للعنصر بين قوسين معكوفين: seaCreatures[1]; Output squid نعلم أن الرقم 0 سيعيد لنا دائما العنصر الأول في المصفوفة. كذلك نستطيع إيجاد العنصر الأخير في المصفوفة بواسطة إجراء عملية طرح قيمة 1 من قيمة الخاصية length للمصفوفة، والإشارة لناتج هذه العملية كرقم فهرس للعنصر الأخير كما هو موضح في المثال التالي: const lastIndex = seaCreatures.length - 1; seaCreatures[lastIndex]; Output starfish محاولة الوصول لعنصر غير موجود سيعيد لنا undefined: seaCreatures[10]; Output undefined للوصول لعنصر مصفوفة متداخلة (مصفوفة داخل مصفوفة)، فعلينا إضافة فهرس اخر يعود للمصفوفة الداخلية: let nestedArray = [ [ "salmon", "halibut", ], [ "coral", "reef", ] ]; nestedArray[1][0]; Output coral في المثال السابق، قمنا بالوصول للعنصر coral بالإشارة لرقم الفهرس الذي يحتوي المصفوفة الداخلية وهو 1، ثم أشرنا للفهرس الذي يحتوي على العنصر في المصفوفة الداخلية وهو 0. إضافة عنصر لمصفوفة في المتغير seaCreatuers يوجد لدينا 5 عناصر بأرقام فهراس تبدأ من 0 الى 4. إذا أردنا أن نُضيف عنصر جديد لهذه المصفوفة، فيمكننا أن نقوم بذلك بإعطاء قيمة للفهرس التالي الذي يلي اخر فهرس: seaCreatures[5] = "whale"; seaCreatures; Output [ 'octopus', 'squid', 'shark', 'seahorse', 'starfish', 'whale' ] إذا قمنا بإضافة عنصر وتجاهلنا قيمة الفهرس التالي ووضعنا بدلا منه فهرس بقيمة 7 مثلا، فإن ذلك يؤدي لإضافة عنصر غير مُعرف (undefined) للمصفوفة كما في المثال التالي: seaCreatures[7] = "pufferfish"; seaCreatures; Output [ 'octopus', 'squid', 'shark', 'seahorse', 'starfish', 'whale', , 'pufferfish' ] هذه المشكلة نستطيع تجنبها باستخدام الوظيفة push() والتي تقوم بإضافة العنصر الجديد في نهاية المصفوفة: // Append lobster to the end of the seaCreatures array seaCreatures.push("lobster"); seaCreatures; Output [ 'octopus', 'squid', 'shark', 'seahorse', 'starfish', , 'whale', 'pufferfish', 'lobster' ] على العكس تماما من الوظيفة push()، فإن الوظيفة unshift() تقوم بإضافة العنصر في بداية المصفوفة: // Append dragonfish to the beginning of the seaCreatures array seaCreatures.unshift("dragonfish"); seaCreatures; Output [ 'dragonfish', 'octopus', 'squid', 'shark', 'seahorse', 'starfish', 'whale', , 'pufferfish', 'lobster' ] باستخدام الوظيفتين السابقتين، ستكون لديك المقدرة على إضافة عناصر جديدة للمصفوفة إما في بدايتها، أو نهايتها. إزالة عنصر من مصفوفة لإزالة عنصر معين من مصفوفة، نستخدم الوظيفة splice(). في المصفوفة seaCreatuers قمنا بإضافة عنصر غير مُعرف وليس له قيمة، ولإزالته نقوم بالتالي: seaCreatures.splice(7, 1); seaCreatures; Output [ 'dragonfish', 'octopus', 'squid', 'shark', 'seahorse', 'starfish', 'whale', 'pufferfish', 'lobster' ] في الوظيفة splice()، المُعامل الأول يشير لرقم الفهرس الذي سنبدأ بالإزالة من عنده (في هذه الحالة 7)، والمُعامل الثاني يشير لعدد العناصر التي نرغب بإزالتها (في حالتنا سيكون 1 حيث أننا نرغب بإزالة عنصر واحد). الوظيفة splice() ستؤثر على المتغير الأصلي. لذلك، إذا أردنا أن نحافظ على المصفوفة الأصلية دون تغيير، نستخدم الوظيفة slice() ونعطي القيمة الناتجة عنها لمتغير جديد. let newArray = slice(7, 1); الوظيفة pop() ستزيل العنصر الأخير من المصفوفة: // Remove the last item from the seaCreatures array seaCreatures.pop(); seaCreatures; Output [ 'dragonfish', 'octopus', 'squid', 'shark', 'seahorse', 'starfish', 'whale', 'pufferfish' ] العنصر lobster أُزيل من المصفوفة لأنه العنصر الأخير، ولإزالة العنصر الأول في المصفوفة، نستخدم الوظيفة shift(): // Remove the first item from the seaCreatures array seaCreatures.shift(); seaCreatures; Output [ 'octopus', 'squid', 'shark', 'seahorse', 'starfish', 'whale', 'pufferfish' ] باستخدام الوظيفتين pop() و shift() نستطيع إزالة العناصر من بداية المصفوفة أو نهايتها. يُفضل استخدام الوظيفة pop() قدر الإمكان، حيث أن باقي العناصر في المصفوفة تبقى في مواقعها دون تغيير. تعديل العناصر في المصفوفة نستطيع تغيير أي قيمة عنصر في المصفوفة وذلك بإعطاء القيمة الجديدة للعنصر باستخدام عملية المساواة، كما نفعل تماما عند التعامل مع المتغيرات العادية: // Assign manatee to the first item in the seaCreatures array seaCreatures[0] = "manatee"; seaCreatures; Output [ 'manatee', 'squid', 'shark', 'seahorse', 'starfish', 'whale', 'pufferfish' ] طريقة أخرى لتغيير قيمة العنصر باستخدام الوظيفة splice() وذلك بإضافة مُعامل جديد. فمثلا، إذا أردنا تغيير قيمة العنصر seahorse والذي يقع في الفهرس 3، نستطيع إزالته وإضافة قيمة جديدة بدلا منه: // Replace seahorse with sea lion using splice method seaCreatures.splice(3, 1, "sea lion"); seaCreatures(); Output [ 'manatee', 'squid', 'shark', 'sea lion', 'starfish', 'whale', 'pufferfish' ] في المثال السابق، قمنا بإزالة العنصر seahorse من المصفوفة، ووضعنا بدلا منه القيمة sea lion في نفس الفهرس 3. حلقة التكرار خلال المصفوفة نستطيع المرور على عناصر المصفوفة من خلال حلقة تكرار for وذلك بالاستفادة من خاصية length. في المثال التالي، نُنشئ مصفوفة باسم shellfish ونطبع رقم كل فهرس فيها بالإضافة إلى قيمة العنصر المرتبط بالفهرس: // Create an array of shellfish species let shellfish = [ "oyster", "shrimp", "clam", "mussel", ]; // Loop through the length of the array for (let i = 0; i < shellfish.length; i++) { console.log(i, shellfish[i]); } Output 0 'oyster' 1 'shrimp' 2 'clam' 3 'mussel' نستطيع أيضا استخدام حلقة التكرار for…of وهي خاصية جديدة في الجافا سكريبت: // Create an array of aquatic mammals let mammals = [ "dolphin", "whale", "manatee", ]; // Loop through each mammal for (let mammal of mammals) { console.log(mammal); } Output dolphin whale manatee حلقة التكرار for…of لا تقوم باستخدام رقم الفهرس للعناصر في المصفوفة، ولكنها بشكل عام طريقة أبسط وأكثر اختصارا للمرور على المصفوفة من خلال حلقة التكرار. استخدام حلقات التكرار مفيد بشكل كبير في طباعة قيم عناصر المصفوفة وهو يشبه عرض العناصر من قاعدة بيانات خلال موقع الكتروني. خاتمة تُعتبر المصفوفات جزء أساسي ومهم في برمجة الجافا سكريبت. في هذا الدرس تعلمنا كيفية إنشاء المصفوفة، وكيفية فهرستها، وتعلمنا إجراء بعض العمليات المهمة على المصفوفات مثل إزالة العناصر والتعديل عليها. وكذلك تعلمنا طريقتين للمرور على عناصر المصفوفة من خلال حلقات التكرار والتي تهدف لإجراء عمليات على عناصر المصفوفة مثل طباعة محتوياتها وطباعة أرقام الفهارس. ترجمة -وبتصرّف- للمقال Understanding Arrays in JavaScript لصاحبه Tania Rascia حقوق الصورة البارزة محفوظة لـ Freepik
  15. تعتمد صفحات الويب على HTML التي تُحدد محتوى الصفحة.تعدّ CSS لغة منفصلة عن HTML ودورها هو تحديد الشكل والمظهر الخاص بصفحة الويب. الشفرة الخاصّة بـ CSS عبارة عن قواعد ساكنة. كل قاعدة تأخذ مُحدِدًا Selector أو أكثر، وتُرجع قيم لمجموعة من الخصائص الشكلية. تُطبَّق هذه الخصائص بعد ذلك على عناصر صفحة الويب المشار إليها بواسطة المحددات. ملاحظة: لتعلم CSS بطريقة صحيحة، ولأن مخرجات هذه اللغة عبارة عن نتائج مرئية، يجب عليك ممارسة كل ما تتعلمه وننصحك بتطبيقه على موقع dabblet. الهدف الرئيسي من هذا المقال التركيز على كيفية الكتابة الصحيحة مع بعض النصائح. سنتناول في هذا المقال العناوين التالية: التعليقات. المحددات. الخصائص. طريقة استخدام CSS في الصفحة. الأولوية أم التتالي. استعلامات الميديا. التوافقية. التعليقات توجد طريقة واحدة لكتابة التعليقات في ملف CSS وهي كتابة التعليقات بين الرمزين /* */. /* التعليقات تُتكتب هنا ولا يوجد نمط لكتابة التعليق في سطر واحد سوى هذه الطريقة */ المحددات يُستخدم المحدد في استهداف عنصر في صفحة، ويُكتَب بالطريقة التالية: selector { property: value; /* خصائص أخرى*/ } لنفترض وجود العنصر التالي من نوع div في صحة ويب: <div class='class1 class2' id='anID' attr='value' otherAttr='en-us foo bar' /> تستطيع تطبيق قاعدة CSS على هذا العنصر باستخدام أحد أسماء الأصناف Classes التي ينتمي إليها في الصفحة: .class1 { } أو باستخدام جميع أسماء الأصناف المطبَّقة عليه: .class1.class2 { } أو باستخدام نوع العنصر: div { } أو باستخدام الرقم الخاص بالعنصر: #anID { } نستطيع تحدد العنصر من الصفحة في حال وجود صفة Attribute باسم معين: [attr] { font-size:smaller; } أو في حالة وجود صفة بقيمة معينة: [attr='value'] { font-size:smaller; } في حالة وجود صفة معينة في عنصر، ونريد تطبيق قاعدة على هذا العنصر بشرط وجود قيمة تبدأ منها الصفة نستخدم الطريقة التالية: [attr^='val'] { font-size:smaller; } أما إن كان الشرط يتعلّق بقيمة معينة تنتهي بها الصفة: [attr$='ue'] { font-size:smaller; } نستطيع تحديد العنصر في حالة احتواء الصفة على قيمة معينة ضمن قائمة قيم منفصلة عن بعضها بمسافة فارغة (يوافق الشرط أدناه العناصر التي لديها صفة otherAttr تساوي “foo” أو “foo bar” أو “foo bar far” …إلخ): [otherAttr~='foo'] { } أو تحديد العنصر في حالة احتواء الصفة على قيمة معينة ضمن قائمة قيم منفصلة عن بعضها برمز – كما في المثال التالي: [otherAttr|='en'] { font-size:smaller; } نستطيع جمع أكثر من مُحدد ببعضها للحصول على مُحدد مُركز كما في المثال التالي: div.some-class[attr$='ue'] { } من الممكن أيضا أن تقوم بتحديد عنصر يكون تابعا (ابن) لعنصر آخر: div.some-parent > .class-name { } في المثال السابق، يكون العنصر الابن على مستوى واحد أسفل من العنصر الأب. تستطيع أن تُحدد عنصر من سلالة عنصرآأخر (الأب)، وهذا يعني من أي مستوى أسفل من مستوى العنصر الأب (ليس شرطا أن يكون مستوى واحد أقل): div.some-parent .class-name { } يختلف المُحدد التالي عن المُحدد السابق لوجود مسافة فاصلة بين الأسماء: div.some-parent.class-name { } نستطيع تحديد العنصر بناءً على عنصر آخر مجاور له باستخدام الطريقة التالية: .i-am-just-before + .this-element { } أو بناءً على أي عنصر يسبق العنصر الذي نريده: .i-am-any-element-before ~ .this-element { } توجد بعض المُحددات تسمى الأصناف الزائفة pseudo classes تطبَّق على العنصر عندما يكون بحالة محددة، فمثلا، نستطيع تحديد عنصر عندما يمر عليه المؤشر: selector:hover { } أو رابط تمت زيارته: selector:visited { } أو لم تتم زيارته: selected:link { } أو عنصر في حالة التركيز: selected:focus { } لتحديد أول عنصر تابع لعنصر: selector:first-child {} لتحديد آخر عنصر تابع لعنصر: selector:last-child {} نستطيع تنسيق أجزاء محددة من العنصر باستخدام العناصر الزائدة Pseudo elements، فمثلا، نستخدم before لإضافة محتوى قبل محتوى عنصر معين: selector::before {} وafter لإضافة محتوى بعد محتوى عنصر معين: selector::after {} في أماكن معينة، يُستخدم رمز * كحرف “بدل” لاختيار كافة العناصر. * { } /* كل العناصر */ .parent * { } /* كل التابعين */ .parent > * { } /* كل الأبناء */ الخصائص selector { وحدات الطول إما مطلقة أو نسبية. الوحدات النسبية: width: 50%; /* نسبة من عرض العنصر الأب */ font-size: 2em; /* مضاعفة حجم الخط الخاص بالعنصر نفسه*/ font-size: 2rem; /* مضاعفة حجم الخط حسب حجم الخط الخاص بالعنصر الأب */ font-size: 2vw; /* مضاعفة حجم الخط بالنسبة ل 1% من عرض المساحة المرئية للمستخدم*/ font-size: 2vh; /* من الارتفاع*/ font-size: 2vmin; /* مضاعفة حجم الخط لأصغر قيمة من الارتفاع أو العرض*/ font-size: 2vmax; /* لاكبر قيمة */ القيم المطلقة: width: 200px; /* بكسل */ font-size: 20pt; /* نقطة */ width: 5cm; /* سنتميتر */ min-width: 50mm; /* مليميتر */ max-width: 5in; /* إنش */ الألوان: color: #F6E; /* صيغة سداسية قصيرة */ color: #FF66EE; /* صيغة سداسية طويلة */ color: tomato; /* حسب الاسم */ color: rgb(255, 255, 255); /* كقيم rgb */ color: rgb(10%, 20%, 50%); /* كنسبة rgb */ color: rgba(255, 0, 0, 0.3); /* كقيم rgba */ color: transparent; /* الشفافية صفر*/ color: hsl(0, 100%, 50%); /* كنسب hsl */ color: hsla(0, 100%, 50%, 0.3); /* كنسب hsla */ الحدود: border-width:5px; border-style:solid; border-color:red; border: 5px solid red; /* اختصار القواعد السابقة في قاعدة واحدة */ border-radius:20px; /* خاصية ابتداء من css3 */ الصور: background-image: url(/img-path/img.jpg); الخطوط: font-family: Arial; إذا كان اسم الخط به مسافات فيجب وضع الاسم بين علامتي تنصيص: font-family: "Courier New"; في نسرُد لائحة من الخطوط وإن لم يجد المتصفح الخط، يستخدم نوع الخط التالي: font-family: "Courier New", Trebuchet, Arial, sans-serif; } دورة تطوير واجهات المستخدم ابدأ عملك الحر بتطوير واجهات المواقع والمتاجر الإلكترونية فور انتهائك من الدورة اشترك الآن طريقة استخدام CSS في الصفحة توجد ثلاث طرق لإجراء عملية تنسيق صفحة الويب وهي كالتالي: 1- تضمين اسم الملف بالامتداد .css داخل العنصر head في بداية صفحة html كالتالي (وهي الطريقة المُوصى بها): <link rel='stylesheet' type='text/css' href='path/to/style.css'> 2- كتابة قواعد css مباشرة في ملف الصفحة: <style> a { color: purple; } </style> 3- تنسيق العنصر بطريقة مباشرة: <div style="border: 1px solid red;"> </div> الأولوية أم التتالي العنصر الواحد في صفحة الويب قد يكون مُستهدفا (محددا) من قبل مجموعة متعددة من المحددات، وقد يكون هناك تعديل أو تحديد لقيمة خاصية تابعة لهذا العنصر من قبل أكثر من مُحدد. في مثل هذه الحالات، فإن قاعدة واحدة سيكون لها الأولوية في التطبيق على هذا العنصر. القواعد التي تمتلك محددات مُفصلة لها أولوية على المحددات ذات التفاصيل الأقل، والقواعد التي تأتي في النهاية تقوم بالتعديل على القواعد التي قبلها (وهذا يعني أنه في حالة تضمين ملفي css وكلاهما يقومان بتحديد عنصر والتعديل على خصائصه، فإن ترتيب ربط الملفات يحكم تنسيق العنصر حسب قاعدة الترتيب المذكورة والتي تُسمى التتالي أو التتابع). استعلامات الوسائط Media queris استعلامات الوسائط هي خاصية بدأت من CSS 3 وتسمح لك بتحديد متى تُطبَّق قواعد CSS، مثلا عند الطباعة، أو عند كثافة وأبعاد شاشة معينين. أمثلة: هذه قاعدة CSS تُطبَّق على كل الأجهزة. h1 { font-size: 2em; color: white; background-color: black; } يعدّل استعلام الوسيط التالي القاعدة السابقة عند الطباعة: @media print { h1 { color: black; background-color: white; } } يجعل استعلام الوسيط التالي حجم الخط أكبر في شاشة بعرض 480 بكسل على الأقل: @media screen and (min-width: 480px) { h1 { font-size: 3em; font-weight: normal; } } تحتوي استعلامات الوسائط على الخصائص التالية: width, height, device-width, device-height, orientation, aspect-ratio, device-aspect-ratio, color,color-index, monochrome, resolution, scan, grid. من الممكن أن يسبق أغلب الخصائص السابقة خاصيتي min- و max-. خاصية الدقة Resolution غير مدعومة على الأجهزة القديمة، وبدلا من ذلك استخدم خاصية device-pixel-ratio. تحاول كثير من أجهزة الجوال والأجهزة اللوحية عرض الصفحة كما لو كانت على سطح المكتب العادي إلا إذا قمت بإضافة الصفة viewport بالشكل التالي: <head> <meta name="viewport" content="width=device-width; initial-scale=1.0"> </head> التوافقية أغلب المميزات والخصائص الموجودة في CSS2 وكثيرا من CSS3 موجودة في كل المتصفحات والأجهزة، ولكن ينصح من باب الممارسة الأفضل أن يتم الفحص قبل استخدام الخاصية الجديدة. ترجمة – بتصرّف – للمقال Learn X in Y minutes Where X=CSS.
  16. وضع Netscape’s Brendan Eich أسس جافا سكريبت سنة 1995. وكان الغرض منها أن تكون لغة ترميز سهلة خاصة بالمواقع ومكملة للجافا في تطبيقات الويب المعقدة، ولكن سهولة دمج جافا سكريبت والدعم الذاتي لها مع المتصفحات جعلها أكثر شيوعًا من لغة الجافا الأصلية في واجهات الويب. لا يقتصر استخدام جافا سكريبت مقتصرة على المتصفحات، ف Node.js مشروع قائم بذاته ويقدم إمكانية بناء تطبيقات إنترنت قائمة بذاتها. صيغة الشفرة البرمجية الخاصة بجافا سكريبت شبيهة بطريقة كتابة لغة C، فإذا كنت قد تعاملت مع لغة البرمجة C قبل ذلك أو جافا، ستكون الكثير من الأساسيات مألوفة لك. على الرغم من ذلك، وعلى الرغم من سهولة الاسم، إلا أن النموذج الكائني في جافا سكريبت مختلف تماماً عن الموجود في الجافا. سنتناول في هذا المقال المواضيع التالية: التعليقات. الأرقام، النصوص والعمليات. المتغيرات، المصفوفات والكائنات. جمل التحكم والمنطق. الدوال، نطاق الوصول و Closures. المشيّدات Constructors والنماذج الأولية Prototypes التعليقات لكتابة تعليق من سطر واحد نبدأ السطر بعلامتي / كما في السطر التالي: // Single-line comments start with two slashes. لكتابة تعليق من أكثر من سطر، نستخدم /* و */ في إحاطة الأسطر التي نريدها كما في الأسطر التالية: /* Multiline comments start with slash-star، and end with star-slash */ تنتهي الجمل في جافا سكريبت بفاصلة منقوطة، ولكن هذا الأمر غير ضروري، حيث يتم إضافة الفاصلة المنقوطة تلقائيا عند وجود سطر جديد وعدم وجود الفاصلة، وهذا الأمر مستثنى في بعض الحالات: doStuff(); بدون فاصلة: doStuff() سنعتمد في هذا الدرس استخدام الفاصلة المنقوطة. دورة تطوير التطبيقات باستخدام لغة JavaScript تعلم البرمجة بلغة جافا سكريبت انطلاقًا من أبسط المفاهيم وحتى بناء تطبيقات حقيقية. اشترك الآن الأرقام، النصوص والعمليات تحتوي جافا سكريبت على نوع رقمي واحد (64-bit IEEE 754 double). الأرقام من نوع Double (الأعداد الحقيقة) تحتوي على 52 بت من الأساس العشري، بما يكفي لتخزين الأعداد الصحيحة Integers حتى 9✕10¹⁵ بدقة. 3; // = 3 1.5; // = 1.5 بعض العمليات الحسابية الأساسية: 1 + 1; // = 2 0.1 + 0.2; // = 0.30000000000000004 8 - 1; // = 7 10 * 2; // = 20 35 / 5; // = 7 5 / 2; // = 2.5 10 % 2; // = 0 30 % 4; // = 2 18.5 % 7; // = 4.5 العمليات الثنائية متاحة أيضا، فعند إجراءك لعملية ثنائية، فإن الأعداد العشرية Float يتم تحويله إلى أعداد طبيعية Int حتى 32 بت: 1 << 2; // = 4 ترتيب العمليات يتم بواسطة استخدام الأقواس: (1 + 3) * 2; // = 8 توجد ثلاثة قيم أرقام غير حقيقية كالتالي: Infinity; // ناتجة عن قسمة رقم موجب على صفر -Infinity; // ناتجة عن قسمة رقم سالب على صفر NaN; //تشير إلى قيمة "غير رقم" القيم المنطقية: true; false; يتم استخدام علامة التنصيص المنفردة أو المزدوجة لبناء النصوص: 'abc'; "Hello، world"; لعكس القيمة نستخدم علامة التعجب: !true; // = false !false; // = true لفحص المساواة: 1 === 1; // = true 2 === 1; // = false لفحص عدم المساواة: 1 !== 1; // = false 2 !== 1; // = true عمليات المقارنة: 1 < 10; // = true 1 > 10; // = false 2 <= 2; // = true 2 >= 2; // = true دمج النصوص يتم بواسطة عملية + : "Hello " + "world!"; // = "Hello world!" وعملية الدمج + لا تعمل فقط مع النصوص، بل مع الأرقام أيضا والتراكيب مثل المصفوفات: "1، 2، " + 3; // = "1، 2، 3" "Hello " + ["world"، "!"] // = "Hello world،!" من الممكن مقارنة النصوص: "a" < "b"; // = true لإجراء عملية فحص المساواة باعتبار تحويل أنواع البيانات (في حالة اختلافها) نستخدم عملية = مرتين: "5" == 5; // = true null == undefined; // = true في حالة استخدام = ثلاثة مرات، لا تتم عملية التحويل: "5" === 5; // = false null === undefined; // = false لابد من الانتباه من التحويل التلقائي للنوع تجنبا لبعض الحالات غير المرغوبة: 13 + !0; // 14 "13" + !0; // '13true' نستخدم charAt للوصول لمحرف Character معين في النصوص بتعيين مكانها في سلسلة المحارف: "This is a string".charAt(0); // = 'T' أو نستخدم substring للحصول على أجزاء أكبر من النصوص: "Hello world".substring(0, 5); // = "Hello" length تعتبر خاصية، لذلك لا تستخدم الأقواس في النهاية: "Hello".length; // = 5 نستخدم null للإشارة للفارغ أو غير الموجود، بينما نستخدم undefined للإشارة لقيمة غير منشأة أو غير موجودة حاليا (مثل تعريف متغير وعدم إعطائه قيمة). القيم false، null، undefined، NaN، 0 ،”” كلها قيم خاطئة (تستخدم كقيمة منطقية خطأ) والباقي صحيح. المتغيرات، المصفوفات، والكائنات يتم تعريف المتغيرات باستخدام كلمة var. جافا سكريبت ديناميكية النوع، حيث لا يجب عليك تحديد نوع المتغير عند تعريفه، ولإعطاء قيمة للمتغير نستخدم = كما في المثال التالي: var someVar = 5; عند عدم استخدام كلمة var لن يظهر لك خطأ، ولكن في هذه الحالة فإن المتغير يكون مستواه على نطاق الوصول العام Global scope ولن يكون على المستوى الذي تم تعريفه فقط. someOtherVar = 10; المتغيرات التي لا تأخذ قيمة عند تعريفها تكون بقيمة undefined تلقائيا: var someThirdVar; // = undefined لتعريف أكثر من متغير في نفس السطر نفصل بين المتغيرات بفاصلة عادية: var someFourthVar = 2، someFifthVar = 4; نستطيع اختصار كتابة العمليات الحسابية بالشكل التالي: someVar += 5; // هذا يعادل someVar = someVar + 5; someVar is 10 now someVar *= 10; // someVar = 100 someVar++; // someVar = 101 someVar--; // back = 100 المصفوفات عبارة عن قائمة من القيم المرتبة من أي نوع: var myArray = ["Hello"، 45، true]; نستطيع الوصول لمحتويات المصفوفة باستخدام الأقواس المعكوفة والفهرس. فهرس المصفوفات يبدأ من صفر: myArray[1]; // = 45 المصفوفات غير ثابتة وذات حجم متغير: myArray.push("World"); myArray.length; // = 4 للتعديل أو الإضافة في موقع معين في المصفوفة: myArray[3] = "Hello"; لتعريف قاموس (Hash): var myObj = {key1: "Hello"، key2: "World"}; المفاتيح في القاموس عبارة عن نص، أو مُعرف صحيح، والقيم تأخذ أي نوع: var myObj = {myKey: "myValue"، "my other key": 4}; للوصول إلى قيمة باستخدام مفتاح والأقواس المعكوفة: myObj["my other key"]; // = 4 أو باستخدام صيغة النقطة والمُعِرف الذي يمثل المفتاح: myObj.myKey; // = "myValue" الكائنات في جافا سكريبت غير ثابتة وقابلة للتعديل: myObj.myThirdKey = true; إذا حاولت الوصول لقيمة غير موجودة في القاموس، ستكون النتيجة المرجعة undefined: myObj.myFourthKey; // = undefined جمل التحكم والمنطق جملة if: var count = 1; if (count == 3){ // ستُنفَّذ هذه الشفرةإذا كانت قيمة المتغير تساوي 3 } else if (count == 4){ // ستُنفَّذ هذه الشفرةإذا كانت قيمة المتغير تساوي 4 } else { // ستُنفَّذ هذه الشفرة في حالة عدم تحقق أي شرط سابق } جملة while: while (true){ // جملة تكرار غير منتهية } جملة do تشبه جملة while إلا أنها تُكرَّر مرة واحدة على الأقل: var input; do { input = getInput(); } while (!isValid(input)) جملة For تشبه الموجودة في لغة سي وجافا: for (var i = 0; i < 5; i++){ // ستُنفَّذ هذه الشفرة خمس مرات } توقف جملة التكرار باستخدام break مع تحديد اسم جملة التكرار التي نريد وقفها: outer: for (var i = 0; i < 10; i++) { for (var j = 0; j < 10; j++) { if (i == 5 && j ==5) { break outer; } } } تسمح لنا جملة for/in بالمرور على خصائص ومحتويات الكائن. في المثال التالي نقوم بالمرور على محتوى قاموس وحفظ النتيجة في متغير: var description = ""; var person = {fname:"Paul"، lname:"Ken"، age:18}; for (var x in person){ description += person[x] + " "; } // description = 'Paul Ken 18 ' العملية المنطقية and تُمَثَلْ ب && والعملية or تُمَثَلْ ب ||: if (house.size == "big" && house.colour == "blue"){ house.contains = "bear"; } if (colour == "red" || colour == "blue"){ // colour is either red or blue } نستفيد من && و || في تحديد القيم التلقائية كما في المثال التالي: var name = otherName || "default"; جملة switch تفحص المساواة باستخدام ===، استخدم break بعد كل حالة فحص وإلا سيتم تنفيذ حالة case الصحيحة التالية أيضا: grade = 'B'; switch (grade) { case 'A': console.log("Great job"); break; case 'B': console.log("OK job"); break; case 'C': console.log("You can do better"); break; default: console.log("Oy vey"); break; } الدوال، نطاق الوصول وClosures تُعرَّف الدوال في جافا سكريبت باستخدام كلمة function: function myFunction(thing){ return thing.toUpperCase(); } myFunction("foo"); // = "FOO" لابد الانتباه أن تضع القيمة المرجعة في نفس السطر الموجودة به كلمة return، إذا لم يكن كذلك، ستكون النتيجة المرجعة undefined بسبب الإضافة التلقائية للفاصلة المنقوطة عند كل سطر جديد (وقد نوهنا لهذه النقطة في البداية). function myFunction(){ return // الفاصلة المنقوطة مضافة تلقائيا هنا {thisIsAn: 'object literal'} } myFunction(); // = undefined يُتعامل مع الدوال في جافا سكريبت بوصفها كائنات، وهذا يعني أنك تستطيع تمرير الدالة معاملا لدالة أخرى، أو قيمة لمتغير. تُستخدَم الدالة myFunction في معالجة حدث في المثال التالي، حيث سيتم تنفيذها بعد فترة زمنية محددة: function myFunction(){ } // ينتُج عن السطر التالي تنفيذ الدالة أعلاه بعد 5 ثوان setTimeout(myFunction، 5000); ملاحظة: الدالة setTimeout ليست جزءًا من جافا سكريبت، ولكنها مقدمة من قبل المتصفحات و Node.js. وظيفة setInterval أيضا مقدمة من قبل المتصفحات. function myFunction(){ } setInterval(myFunction، 5000); ليس من الشرط تحديد اسم الدالة، ونستطيع كتابة الدالة دون اسم في المكان الذي يتم تمرير قيمتها المُرجعة فيه بالطريقة التالية: setTimeout(function(){ }, 5000); تمتلك جافا سكريبت نطاقاً وظيفياً، حيث لكل دالة نطاقها الخاص، بينما الكتل الأخرى لا تشاركها هذا النطاق. if (true){ var i = 5; } i; // = 5 إن كتبنا الشفرة في المثال السابق داخل دالة فإن قيمة المتغير i تساوي 5 على عكس ما تتوقعه في النطاق الكتلي، بمعنى أن المتغيرات مُشاهدَة ونستطيع الوصول إليها على مستوى الدالة بغض النظر عن مكان تعريفها داخل هذه الدالة. وهذا يشير إلى نمط متعارف عليه يمنع المتغيرات المؤقتة من الظهور في نطاق الوصول العام. وللتوضيح على ما سبق، في المثال التالي، يبقى المتغير temporary داخل نطاق الدالة المُعرف فيها، أما المتغيرات في النطاق العام مثل permanent فنستطيع الوصول إليه باستخدام الكائن العام والمسمى في كل المتصفحات ب window وتكون صيغة الوصول للمتغير هكذا window.permanent. الكائن ذو النطاق العام يختلف اسمه في البيئات التي لا علاقة لها بالمتصفحات مثل Node.js. (function(){ var temporary = 5; window.permanent = 10; })(); temporary; // raises ReferenceError permanent; // = 10 من أقوى خصائص لغة جافا سكريبت وجود ما يسمى بclosures ، حيث إذا كانت دالة مُعرفة داخل دالة أخرى، فإن الدالة الداخلية تمتلك الوصول لكافة المتغيرات الخاصة بالدالة الخارجية حتى بعد خروجها وانتهائها. في المثال التالي، فإن استدعاء الدالة setTimeout سيتم تنفيذها مباشرة بعد استدعاء الدالة الخارجية sayHelloInFiveSeconds والتي ستنتهي مباشرة. ومن ثم سوف يبدأ العد حتى 5 ثوان لاستدعاء الوظيفة الداخلية، وعند انتهاء المدة، وعلى الرغم من خروج وانتهاء الدالة الخارجية، إلا أنه سيتم تنفيذ الداخلية بنجاح وسيتم الوصول للمتغير prompt دون مشاكل. function sayHelloInFiveSeconds(name){ var prompt = "Hello، " + name + "!"; function inner(){ alert(prompt); } setTimeout(inner، 5000); } // سيتم طباعة "مرحبا أدم" بعد 5 ثواني sayHelloInFiveSeconds("Adam"); المشيّدات Constructors والنماذج الأولية Prototypes يمكن للكائنات أن تحتوي على دوال، كما في المثال التالي: var myObj = { myFunc: function(){ return "Hello world!"; } }; myObj.myFunc(); // = "Hello world!" عندما يتم استدعاء دوال معرَّفة في كائن، فإن هذه الدوال تستطيع الوصول للكائن التي عُرِّفت فيه باستخدام كلمة this كما في المثال التالي: myObj = { myString: "Hello world!"، myFunc: function(){ return this.myString; } }; myObj.myFunc(); // = "Hello world!" الدالة myFunc لا تعمل إذا لم يتم استدعاؤها في سياق الكائن الذي تتصل به، لاحظ في المثال التالي: var myFunc = myObj.myFunc; myFunc(); // = undefined نستطيع ربط دالة بكائن والوصول لمتغيرات هذا الكائن بواسطة this على الرغم من أن هذه الدالة لم تُعرَّف مع تعريف بالكائن. var myOtherFunc = function(){ return this.myString.toUpperCase(); } myObj.myOtherFunc = myOtherFunc; myObj.myOtherFunc(); // = "HELLO WORLD!" نستطيع أيضا تحديد سياق الدالة لتنفيذها من خلاله وذلك عن طريق استدعاء الوظيفة باستخدام call او apply. var anotherFunc = function(s){ return this.myString + s; } anotherFunc.call(myObj، " And Hello Moon!"); // = "Hello World! And Hello Moon!" استخدمنا في المثال السابق الدالة call. تؤدّي الدالة apply نفس الغرض ولكننا نمرر لها مصفوفة معاملات، وهذا يفيدنا في حالة التعامل مع دالة تقبل مجموعة من المعاملات. anotherFunc.apply(myObj، [" And Hello Sun!"]); // = "Hello World! And Hello Sun!" Math.min(42، 6، 27); // = 6 Math.min([42، 6، 27]); // = NaN (uh-oh!) Math.min.apply(Math، [42، 6، 27]); // = 6 لاحظ أننا عند استخدام apply و call قمنا بتمرير السياق الذي نريده من خلال myObj. إذا أردنا أن نُثبت السياق الذي نريد تنفيذ الدالة من خلاله، فإننا نستخدم bind عوضا عن ذلك. var boundFunc = anotherFunc.bind(myObj); boundFunc(" And Hello Saturn!"); // = "Hello World! And Hello Saturn!" نستطيع استخدام bind لتطبيق دالة جزئيا، انظر المثال التالي: var product = function(a، b){ return a * b; } var doubler = product.bind(this، 2); doubler(8); // = 16 عند استدعاء دالة بواسطة الكلمة new فإن كائناً جديداً يتم إنشاؤه وسوف يكون متاحا للدالة بواسطة كلمة this. الدوال التي صُممت للاستدعاء بهذه الطريقة تسمى المشيّدات constructors. var MyConstructor = function(){ this.myNumber = 5; } myNewObj = new MyConstructor(); // = {myNumber: 5} myNewObj.myNumber; // = 5 على خلاف لغات البرمجة الكائنية الأخرى، جافا سكريبت لا تحتوي على مفهوم العيّنة Instance أو “الكائن المتولد من الفئة عند التشغيل”. تُقدم جافا سكريبت المفاهيم الكائنية مثل التوليد والوراثة من خلال مفهوم واحد يسمى النموذج الأولي Prototype. كل كائن في الجافا سكريبت يحتوي على نموذج أولي. عندما تقوم بمحاولة استخدام لخاصية غير موجودة في كائن معين، فإن مفسر جافا سكريبت سوف ينظر في النموذج الأولي للكائن. تجعلك بعض تطبيقات الجافا سكريبت تصل لنموذج الكائن بواسطة الخاصية “proto“. على الرغم من أن هذه الطريقة مفيدة في شرح مفهوم النموذج الأولي، إلا أنها ليست الطريقة المعيارية لذلك، وسوف نشرح الطريقة الصحيحة لهذا الأمر لاحقا. var myObj = { myString: "Hello world!" }; var myPrototype = { meaningOfLife: 42، myFunc: function(){ return this.myString.toLowerCase() } }; myObj.__proto__ = myPrototype; myObj.meaningOfLife; // = 42 myObj.myFunc(); // = "hello world!" في حال لم تكن الخاصية موجودة في النموذج الأولي، فإن المفسر يبحث في نموذج النموذج وهكذا. myPrototype.__proto__ = { myBoolean: true }; myObj.myBoolean; // = true لا يوجد نُسَخْ عند استخدام النموذج، حيث إن كل كائن يقوم بالتأشير للنموذج الخاص به، وهذا يعني أن أي تغيير على النموذج سوف ينعكس في كل مكان آخر. myPrototype.meaningOfLife = 43; myObj.meaningOfLife; // = 43 جملة for/in تسمح بالمرور على خصائص كائن، مرورا بسلسلة النماذج الأولية حتى الوصول إلى نموذج فارغ. for (var x in myObj){ console.log(myObj[x]); } ///prints: // Hello world! // 42 // [Function: myFunc] للمرور على خصائص الكائن دون النموذج، نستخدم وظيفة hasOwnProperty كما في المثال التالي: for (var x in myObj){ if (myObj.hasOwnProperty(x)){ console.log(myObj[x]); } } ///prints: // Hello world! كما ذكرنا سابقا، فإن استخدام “proto” في تعريف نموذج كائن هي طريقة غير معيارية، ولا يوجد طريقة لتغيير نموذج أولي لكائن موجود. على الرغم من ذلك، توجد طريقتان لإنشاء كائن مع نموذج مُعطى. الأولى هي استخدام Object.create: var myObj = Object.create(myPrototype); myObj.meaningOfLife; // = 43 الطريقة الثانية – مضمونة أكثر - باستخدام المشيّدات. تمتلك المشيّدات خاصية تسمى prototype تُحدَّد عند إنشاء كائن جدي باستخدام كلمة new، المثال التالي يشرح هذا الأمر: MyConstructor.prototype = { myNumber: 5، getMyNumber: function(){ return this.myNumber; } }; var myNewObj2 = new MyConstructor(); myNewObj2.getMyNumber(); // = 5 myNewObj2.myNumber = 6 myNewObj2.getMyNumber(); // = 6 توجد لدى أنواع البيانات مثل النصوص والأرقام مشيّدات تقوم بإنشاء كائنات تعادل الكائنات المنشأة بطريقة عادية. عدا أنها ليست متماثلة تماما! var myNumber = 12; var myNumberObj = new Number(12); myNumber == myNumberObj; // = true typeof myNumber; // = 'number' typeof myNumberObj; // = 'object' myNumber === myNumberObj; // = false if (0){ //لن تُنفَّذ هذه الشفرة لأن قيمة الصفر خاطئة } if (new Number(0)){ //سوف تُنفَّذ هذه الشفرة لأن الرقم في الشرط عبارة عن كائن وليس نوع رقم، والكائنات دائما ذات قيمة منطقية صحيحة } الكائنات المغلفة أو العادية تتشارك في النموذج الأولي الخاص بنوعها، فمثلا، نستطيع إضافة خاصية على النموذج الخاص بنوع string بهدف الحصول على الحرف الأول من النص، كما في المثال التالي: String.prototype.firstCharacter = function(){ return this.charAt(0); } "abc".firstCharacter(); // = "a" تُستخدَم الخاصية السابقة غالباً في ما يُعرَف بالملْء المتعدّد Polyfilling والتي تُطَبِقْ مميزات أحدث من جافا سكريبت في مجموعة قديمة من نُسخ جافا سكريبت بهدف استخدام هذه المميزات الحديثة في بيئات قديمة مثل المتصفحات المنتهية تاريخا. ملاحظة: تنفيذ Object.create قد يكون غير متاح في بعض التطبيقات، ولكننا نستطيع استخدام الملْء المتعدّد لتعويض هذا الغياب كالتالي: if (Object.create === undefined){ //في حالة كانت موجودة لا تعدل عليها Object.create = function(proto){ // أنشئ مشيّدًا مؤقتا باستخدام النموذج الأولي المناسب var Constructor = function(){}; Constructor.prototype = proto; return new Constructor(); } } ترجمة – بتصرّف – للمقال Learn X in Y minutes Where X=javascript.
  17. هذا يعود لطريقتك في التعلم. من الممكن ان تعتمد على فيديوهات تشرح استخدام المكتبة ولكن لن تحصل على كل شيئ. إذا أردت معرفة تفاصيل المكتبة يُحبذ أن تقوم بمراجعة التوثيق الخاص بها. تحياتي العطرة.
  18. تساعدك الواجهة البرمجية (API) الخاصة بموقع التواصل الاجتماعي تويتر في إدارة الحسابات الموجودة فيه، كما وتسمح لك بالتنقيب عما يحتويه من بيانات. هذا الأمر يفيدك – مثلاً - في عملية ترويج هوية المؤسسة أو المنظمة التي تعمل فيها، وكذلك يُعدّ ممتعًا ومسليًا للمستخدمين الأفراد وهواة البرمجة. في هذا الدرس سوف نشرح لكم الخطوات اللازمة لإنشاء تطبيق توتير، وبعد ذلك سنبني سكربت بايثون من خلال استخدام مكتبة Tweepy للاستفادة من التطبيق و نشر تغريدات من خلاله. المتطلبات قبل البدء، تأكد من وجود المتطلبات التالية: وجود حساب توتير مرتبط برقم هاتف محمول صحيح، وتستطيع إعداد ذلك بالذهاب إلى خيار الهاتف المحمول في قائمة الإعدادات والخصوصية. وجود بيئة بايثون مثبتة على جهاز العمل. الخطوة الأولى: إنشاء تطبيق تويتر لنبدأ في بناء تطبيق تويتر والحصول على مفاتيح ورموز الوصول الخاصة بالواجهة البرمجية للتطبيق. هذه الرموز هي التي تسمح لك بالاستيثاق من أي تطبيق تطوّره لكي يعمل مع تويتر. كما ذكرنا في المتطلبات، فأنت تحتاج لرقم هاتف محمول لكي تستطيع بناء التطبيق. افتح المتصفح وزر هذا الرابط وسجّل الدخول للصفحة باستخدام بيانات حسابك. اضغط بمجرد ولوجك على الزر المعنون ب (Create New App). سوف تُوجَّه إلى صفحة بناء التطبيق التالية: أدخل في هذه الصفحة الحقول المطلوبة كما في المثال التالي: الاسم: AcademyHsoubTest الوصف: Academy Hsoub Application الموقع: https://my.example.placeholder اقرأ اتفاقية مطور تويتر. إذا كنت موافقاً عليها، اضغطعلى خيار الموافقة واضغط على الزر المعنون ب Create your Twitter application والموجود في أسفل الصفحة، وسوف تتلقى صفحة تأكيد على ذلك. ستُوجَّه بعد أن نجاح إنشاء التطبيق إلى صفحة التطبيق التالية، والتي تُقدم لك بعض المعلومات العامة عن التطبيق. الخطوة الثانية: تعديل مستوى الإذن للتطبيق وتوليد رموز الوصول الخاصة به من صفحة التفاصيل (Details Page) السابقة، اذهب لخيار الأذونات (Permissions) وذلك للتأكد من أننا نمتلك مستوى الوصول المطلوب لتوليد مفاتيح التطبيق. يمتلك التطبيق تلقائيا أذونات القراءة والكتابة. إذا لم يكن كذلك، عدّل التطبيق وتأكد من أن خيار القراءة والكتابة هو المحدد من قائمة الأذونات. هذا الأمر يسمح للتطبيق بالنشر على حساب تويتر بالنيابة عنك. اذهب بعد التأكد من أذونات التطبيق التي تسمح له بالنشر إلى خيار المفاتيح ورموز الوصول (Keys and Access Tokens). ستُنقَل إلى صفحة تعرض لك مفتاح المستهلك (Consumer Key) والرمز السري للمستهلك (Consumer Secret)، وكذلك تُمكنك من توليد رمز الوصول ورمز الوصول السري. هذه الرموز والمفاتيح هي التي ستقوم بعملية الاستيثاق لتطبيقك مع تويتر. اضغط على الزر المعنون ب (Create my access token) لتوليد رمز الوصول ورمز الوصول السري. أصبح لديك الآن رمز الوصول ورمز الوصول السري. الخطوة الثالثة: تثبيت مكتبة Tweepy تستطيع أن تستخدم مجموعة متنوعة من لغات البرمجة للتخاطب مع الواجهة البرمجية لتويتر. سنختبر التطبيق الذي أنشأناه عن طريق تشغيل سكربت بايثون ينشُر نصًّا معيّنًا على حساب تويترللمستخدم. مكتبة Tweepy مفتوحة المصدر وسهلة الاستخدام وتسمح للمشاريع المكتوبة بلغة البايثون بالوصول للواجهة البرمجية لتويتر بكل سهولة. سنستخدم أداة pip لتثبيت مكتبة tweepy. أنشئ مجلدًا خاصًّا للمشروع باسم twitter. تأكد قبل تثبيت المكتبة أن أداة pip مُحدثة: >> pip install --upgrade pip بعد أن تُحدَّث الأداة بنجاح، نثبّت مكتبة Tweepy: >> pip install tweepy بعد تثبيت المكتبة تستطيع البدء بكتابة البرنامج. الخطوة الرابعة: بناء البرنامج الذي يتخاطب مع الواجهة البرمجية لتويتر أصبحنا، بعد أن بنينا التطبيق وولّدنا الرموز والمفاتيح اللازمة للوصول للتطبيق، على مشارف بناء البرنامج الذي سينشر بالنيابة عنك. أنشئ باستخدام محرر النصوص المفضل لديك ملف بايثون باسم helloworld.py في داخل المجلد twitter الذي أنشأناه في الخطوة السابقة. في بداية الملف، نحتاج أن نستورد المكتبة باستخدام جملة import: import tweepy سننشئ متغيرات لكل مفتاح ورمز ولّدناه. استبدل ما بين علامات التنصيص في المتغيرات التالية بالمفاتيح والرموز التي تم توليدها في تطبيقك الخاص. import tweepy consumer_key = 'your_consumer_key' consumer_secret = 'your_consumer_secret' access_token = 'your_access_token' access_token_secret = 'your_access_token_secret' ننشئ بعدها عنصرًا من الصنف OAuthHandler الموجود في مكتبة Tweepy وسنمرر لهذا العنصر المفاتيح والرموز الموجودة لدينا. يعمل هذا العنصر من خلال بروتوكول HTTP يعطي التصريح اللازم للأجهزة، الواجهات البرمجية، الخوادم والتطبيقات، ويعدّ هذا الصنف صنفا معياريا يقدّم ألية وصول آمن وذي تفويض كامل. نعدّ كذلك رموز الوصول وندمجها مع الواجهة البرمجية المنشأة من تعريف عنصر من نوع API. import tweepy consumer_key = 'your_consumer_key' consumer_secret = 'your_consumer_secret' access_token = 'your_access_token' access_token_secret = 'your_access_token_secret' auth = tweepy.OAuthHandler(consumer_key, consumer_secret) auth.set_access_token(access_token, access_token_secret) api = tweepy.API(auth) نحدّد في نهاية البرنامج النص الذي سننشره. عرف متغيرًا باسم tweet ومرر له النص الذي تريد نشره ومرّر هذا المتغير للدالة api.update_status. import tweepy # Create variables for each key, secret, token consumer_key = 'your_consumer_key' consumer_secret = 'your_consumer_secret' access_token = 'your_access_token' access_token_secret = 'your_access_token_secret' # Set up OAuth and integrate with API auth = tweepy.OAuthHandler(consumer_key, consumer_secret) auth.set_access_token(access_token, access_token_secret) api = tweepy.API(auth) # Write a tweet to push to our Twitter account tweet = 'أكاديمية حسوب، أهلا بالعالم' api.update_status(status=tweet) تستطيع الان حفظ الملف وتشغيله: >> python helloworld.py بعد انتهاء تنفيذ البرنامج، قم بمراجعة الحساب توتير الخاص بك. نشرت التغريدة على الخط الزمني لحسابك بنجاح وبذلك تكون قد هيّأت تطبيق تويتر واستخدمته من خلال مكتبة Tweepy! خاتمة باتباعك للشرح في هذا المقال، أنت أصبحت قادرا على تهيئة تطبيق تويتر وربطه بحسابك الخاص على الموقع. بمجرد بناء التطبيق وتوليد المفاتيح والرموز اللازمة، قمنا باستخدام التطبيق والرموز للاستيثاق من برنامج بايثون باستخدام المكتبة المفتوحة المصدر Tweepy. إذا لم تكن مبرمجًا للغة فابدأ بتعلم بايثون، أو هناك العديد من لغات البرمجة والمكتبات التي من الممكن استخدامها مع الواجهة البرمجية لتويتر. يحتوي الموقع الخاص بمطوري تويتر العديد من هذه المكتبات التي تدعم التخاطب مع الواجهة البرمجية لتويتر. ترجمة - بتصرّف - للمقال How To Create a Twitter App لصاحبته Lisa Tagliaferri. حقوق الصورة البارزة محفوظة لـ Freepik
  19. في بدايات التسعينات، قام Guido van Rossum بإنشاء لغة البايثون. تُعتبر البايثون من أشهر لغات البرمجة حاليا، ولها حضور واسع في العديد من المجالات التطبيقية والعلمية، وتتميز بسهولة شفرتها البرمجية وسرعة تعلمها مع متانة وقوة تضاهي اللغات الأخرى. سوف نتناول في هذا المقال المواضيع التالية: التعليقات. أنواع البيانات. المتغيرات والتراكيب. جمل التحكم. الدوال Functions. الوحدات. الفئات. ملاحظة: إصدار البايثون 3 هو المعتمد في شرح هذا المقال، وناتج العمليات والأوامر في هذا المقال سيتم كتابتها بعد الرمز # =>. التعليقات تبدأ التعليقات ذات السطر الواحد برمز #، أما التعليقات التي تحتوي أكثر من سطر فتجب إحاطتها بثلاث علامات تنصيص (منفردة أو مزدوجة) في البداية والنهاية. أنظر المثال التالي: # Single line comments start with a number symbol. """ Multiline strings can be written using three "s, and are often used as documentation. """ دورة تطوير التطبيقات باستخدام لغة Python احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة اشترك الآن أنواع البيانات والعمليات الأرقام: 3 # => 3 العمليات الرياضية: 1 + 1 # => 2 8 - 1 # => 7 10 * 2 # => 20 35 / 5 # => 7.0 يوجد نوعان من القسمة في بايثون 3، الأولى تُسمى القسمة بعدد فاصل عائم “floating point division” ونَستخدم رمز القسمة المعروف / ، وناتج العملية هو دائما عدد حقيقي من النوع float: 10.0 / 3 # => 3.3333333333333335 أما النوع الثاني من القسمة فيُسمى القسمة الصحيحة “integer division” ونَستخدم الرمز // لهذا النوع، ويكون ناتج العملية دون الفاصلة والأرقام التي بعدها: 5 // 3 # => 1 5.0 // 3.0 # => 1.0 # يعمل هذا النوع من القسمة على الأعداد الحقيقية أيضا -5 // 3 # => -2 -5.0 // 3.0 # => -2.0 عملية باقي القسمة: 7 % 3 # => 1 عملية الأس: 2**3 # => 8 قاعدة أولوية العمليات حسب الأقواس: (1 + 3) * 2 # => 8 القيم المنطقية (لاحظ الحرف الكبير في البداية): True False عكس القيمة المنطقية باستخدام not: not True # => False not False # => True العمليات المنطقية (العمليات المنطقية حساسة لحالة الأحرف): True and False # => False False or True # => True القيمة المنطقية False تساوي الرقم 0، والقيمة المنطقية True تساوي الرقم 1: 0 and 2 # => 0 -5 or 0 # => -5 0 == False # => True 2 == True # => False 1 == True # => True -5 != False != True #=> True عملية فحص المساواة باستخدام ==: 1 == 1 # => True 2 == 1 # => False فحص عدم المساواة: 1 != 1 # => False 2 != 1 # => True المقارنات: 1 < 10 # => True 1 > 10 # => False 2 <= 2 # => True 2 >= 2 # => True 1 < 2 < 3 # => True 2 < 3 < 2 # => False تفحص عملية is إذا كان متغيران يشيران لنفس الكائن أم لا، ولكن العملية == تفحص إذا كانا بنفس القيمة أم لا: a = [1, 2, 3, 4] b = a b is a # => True b == a # => True b = [1, 2, 3, 4] b is a # => False b == a # => True تُنشَأ النصوص باستخدام علامات التنصيص المزدوجة أو الفردية: "This is a string." 'This is also a string.' تستطيع جمع النصوص ببعضها، ولكن حاول تجنب هذه الطريقة: "Hello " + "world!" # => "Hello world!" تستطيع دمج النصوص ببعضها دون استخدام + : "Hello " "world!" # => "Hello world!" من الممكن التعامل مع النص وكأنه مصفوفة من الحروف: "This is a string"[0] # => 'T' للحصول على طول نص نستخدم الدالة المضمنة len : len("This is a string") # => 16 تستطيع استخدام الدالة format لإجراء عملية التنسيق على النص: "{} can be {}".format("Strings", "interpolated") # => "Strings can be interpolated" تستطيع عند استخدام الدالة format ترقيم المدخلات حسب ترتيبها واستخدامها في تنسيق النص أكثر من مرة: "{0} be nimble, {0} be quick, {0} jump over the {1}".format("Jack", "candle stick") # => "Jack be nimble, Jack be quick, Jack jump over the candle stick" أو باستخدام طريقة تسمية المدخلات: "{name} wants to eat {food}".format(name="Bob", food="lasagna") # => "Bob wants to eat lasagna" تستطيع في البايثون 3 استخدام الطريقة القديمة في بايثون 2 لعمل تنسيق للنصوص: "%s can be %s the %s way" % ("Strings", "interpolated", "old") # => "Strings can be interpolated the old way" None عبارة عن كائن: None # => None لا تستخدم فحص المساواة باستخدام رمز == للمقارنة مع None واستخدم عملية الفحص is بدلا منها: "etc" is None # => False None is None # => True None والرقم 0 والمتغيرات الفارغة من الأنواع strings، lists، dict، وtuples جميعها تُرادف القيمة المنطقية False، أما باقي القيم فهي True: # All other values are True bool(0) # => False bool("") # => False bool([]) # => False bool({}) # => False bool(()) # => False المتغيرات والتراكيب: تتوفّردالة خاصة للطباعة (الإخراج على الطرفية) وتسمى print: print("I'm Python. Nice to meet you!") # => I'm Python. Nice to meet you! يُطبَع سطر جديد تلقائيا عند استخدام الدالة print. تستطيع استخدام المعطى end لتغيير هذا الأمر وتحديد النص الذي تريده بدلا من السطر الجديد: print("Hello, World", end="!") # => Hello, World! للحصول على مدخلات من الطرفية نستخدم الدالة input: input_string_var = input("Enter some data: ") # Returns the data as a string ملاحظة/ في النسخ القديمة من البايثون، كانت الدالة input باسم raw_input. لا يوجد في البايثون تعريفات، ولكن يوجد إعطاء قيم مباشرة. الطريقة المتعارف عليها في تسمية المتغيرات هي الأحرف الصغيرة مع التسطير السفلي: some_var = 5 some_var # => 5 محاولة استخدام متغير لم يأخذ قيمة مسبقاً ينتج عنه خطأ، راجع كيفية معالجة الأخطاء تحت عنوان جمل التحكم. some_unknown_var # ينتُج خطأ من الصنف NameError تشبه القوائم المصفوفات في اللغات الأخرى: li = [] other_li = [4, 5, 6] نستخدم append لإضافة عناصر في نهاية القائمة: li.append(1) # li is now [1] li.append(2) # li is now [1, 2] li.append(4) # li is now [1, 2, 4] li.append(3) # li is now [1, 2, 4, 3] نستخدم الدالةpop لحذف العناصر من آخر القائمة. ترجع التعليمة أدناه القيمة 3 وتصبح مكونات القائمة [1, 2, 4]: li.pop() # => 3 and li is now [1, 2, 4] تعود القائمة إلى حالتها السابقة لتنفيذ الدالة pop بعد تنفيذ الدالة append على النحو التالي: li.append(3) # li is now [1, 2, 4, 3] again. تستطيع التعامل مع القائمة مثل المصفوفة من حيث الوصول لعناصرها: li[0] # => 1 li[-1] # => 3 في حال استخدام فهرس خارج حدود القائمة سينتج خطأ من نوع IndexError: li[4] # Raises an IndexError تستطيع استخدام مجال للحصول على جزء أكبر من القائمة بحيث نحدد فهرس البداية وفهرس النهاية. li[1:3] # => [2, 4] ملاحظة: فهرس النهاية غير مشمول في القيمة المرجعة، حيث يعدّ النمط المستخدم هو نمط نطاق مغلق-مفتوح. في حال عدم استخدام فهرس النهاية: li[2:] # => [4, 3] في حال عدم استخدام فهرس البداية: li[:3] # => [1, 2, 4] اختيار عنصر كل خطوتين ابتداء من العنصر الأول في القائمة: li[::2] # =>[1, 4] إرجاع كامل المصفوفة بطريقة عكسية: li[::-1] # => [3, 4, 2, 1] القاعدة العامة للاستعلامات السابقة في القوائم هي كالتالي(البداية start، النهاية end والخطوة step): # li[start:end:step] نسخ عميق (Deep Copy): li2 = li[:] # => li2 = [1, 2, 4, 3] عندما نفحص المساواة باستخدام عملية is كالتالي: (li2 is li) ستكون النتيجة False. لحذف عنصر من القائمة: del li[2] # li is now [1, 2, 3] لحذف أول عنصر في القائمة يساوي القيمة المدخلة في الدالة remove: li.remove(2) # li is now [1, 3] li.remove(2) # ValueError لأن القيمة غير موجودة إضافة عنصر في مكان معين في القائمة: li.insert(1, 2) # li is now [1, 2, 3] again الحصول على فهرس أول عنصر في القائمة يساوي القيمة المعطاة: li.index(2) # => 1 li.index(4) # ValueError لأن القيمة غير موجودة لإضافة قائمة لقائمة وإرجاع النتيجة كقائمة جديدة: li + other_li # => [1, 2, 3, 4, 5, 6] لتمديد قائمة وإضافة قائمة إليها: li.extend(other_li) # Now li is [1, 2, 3, 4, 5, 6] لفحص وجود قيمة في القائمة: 1 in li # => True للحصول على حجم القائمة (عدد العناصر التي بها): len(li) # => 6 نوع البيانات Tuple تشبه القائمة ولكنها غير قابلة للتعديل (ثابتة-immutable): tup = (1, 2, 3) tup[0] # => 1 tup[0] = 3 # Raises a TypeError لاحظ أنه في حالة وجود عنصر واحد في tuple لابد من وضع فاصلة عادية بعد العنصر، أما في حالة وجود أكثر من عنصر فتصبح الفاصلة إضافية: type((1)) # => <class 'int'> type((1,)) # => <class 'tuple'> type(()) # => <class 'tuple'> تستطيع تنفيذ أغلب عمليات القوائم على النوع Tuple: len(tup) # => 3 tup + (4, 5, 6) # => (1, 2, 3, 4, 5, 6) tup[:2] # => (1, 2) 2 in tup # => True تستطيع تفريغ (unpacking) محتويات Tuples وكذلك القوائم في متغيرات كما في الأمثلة التالية: a, b, c = (1, 2, 3) # a = 1, b = 2, c = 3 a, *b, c = (1, 2, 3, 4) # a = 1, b = [2, 3], c = 4 عند عدم استخدام الأقواس فإن نوع البيانات التلقائي الذي سيتم استخدامه هو Tuple: d, e, f = 4, 5, 6 تبديل قيم المتغيرات بطريقة سهلة: e, d = d, e # d = 5, e = 4 القواميس عبارة عن مؤشرات (مُخططات) من المفاتيح للقيم (كل مفتاح يؤشر على قيمة خاصة به). تعريف قاموس فارغ: empty_dict = {} تعريف قاموس بقيم مسبقة: filled_dict = {"one": 1, "two": 2, "three": 3} لاحظ أن المفاتيح في القواميس لابد أن يكون نوع بياناتها ثابتا (immutable) وذلك لضمان الحصول على مفتاح ثابت (لا تتغير قيمته). أنواع البيانات الثابتة والتي من الممكن استخدامها هي int , float, string, tuple. invalid_dict = {[1,2,3]: "123"} # => Raises a TypeError: unhashable type: 'list' valid_dict = {(1,2,3):[1,2,3]} # Values can be of any type, however. يمكن للقيم – عكس المفاتيح – أن تكون من أي نوع. للبحث عن قيم نستخدم الأقواس المعكوفة: filled_dict["one"] # => 1 للحصول على مفاتيح قاموس على شكل قائمة (الترتيب في القواميس غير ثابت): list(filled_dict.keys()) # => ["three", "two", "one"] للحصول على قيم قاموس على شكل قائمة: list(filled_dict.values()) # => [3, 2, 1] للتأكد من وجود مفتاح قاموس معين: "one" in filled_dict # => True 1 in filled_dict # => False في حالة استخدام مفتاح غير موجود للبحث في قاموس، فإن ذلك ينتج خطأ: filled_dict["four"] # KeyError استخدم الدالة get لتجنب الخطأ السابق: filled_dict.get("one") # => 1 filled_dict.get("four") # => None تدعم الدالة get إعادة قيمة تلقائية في حالة عدم وجود المفتاح: filled_dict.get("one", 4) # => 1 filled_dict.get("four", 4) # => 4 تضيف الدالة setdefault المفتاح المُمرر إلى القاموس في حالة عدم وجوده. تضيف التعليمة التالية مفتاحا باسم five وتعطيه قيمة 5، أما التعليمة الثانية فلا تحدت تغييرا على القاموس. filled_dict.setdefault("five", 5) # filled_dict["five"] is set to 5 filled_dict.setdefault("five", 6) # filled_dict["five"] is still 5 للإضافة إلى القاموس: filled_dict.update({"four":4}) # => {"one": 1, "two": 2, "three": 3, "four": 4} filled_dict["four"] = 4 # طريقة أخرى حذف المفتاح من القاموس: del filled_dict["one"] # Removes the key "one" from filled dict بعض طرق التفريغ في القواميس: {'a': 1, **{'b': 2}} # => {'a': 1, 'b': 2} {'a': 1, **{'a': 2}} # => {'a': 2} المجموعات: empty_set = set() some_set = {1, 1, 2, 2, 3, 4} # some_set is now {1, 2, 3, 4} نوع البيانات الخاص بعناصر المجموعات لابد أن يكون ثابتا: invalid_set = {[1], 1} # => Raises a TypeError: unhashable type: 'list' valid_set = {(1,), 1} للإضافة إلى المجموعة: filled_set.add(5) # filled_set is now {1, 2, 3, 4, 5} إجراء عملية التقاطع بين مجموعتين: other_set = {3, 4, 5, 6} filled_set & other_set # => {3, 4, 5} إجراء عملية الاتحاد بين مجموعتين: filled_set | other_set # => {1, 2, 3, 4, 5, 6} إجراء عملية الطرح بين مجموعتين: {1, 2, 3, 4} - {2, 3, 5} # => {1, 4} لإجراء عملية فرق التماثل بين مجموعتين: {1, 2, 3, 4} ^ {2, 3, 5} # => {1, 4, 5} لفحص إذا كانت المجموعة على الشمال هي مجموعة تحتوي المجموعة على اليمين أم لا: {1, 2} >= {1, 2, 3} # => False عكس المثال السابق: {1, 2} <= {1, 2, 3} # => True فحص وجود قيمة في مجموعة: 2 in filled_set # => True 10 in filled_set # => False جمل التحكم some_var = 5 جملة if: if some_var > 10: print("قيمة المتغيّر أكبر تماما من 10") elif some_var < 10: # هذه الجملة اختيارية print("قيمة المتغيّر أصغر من 10") else: # هذه الجملة اختيارية print("قيمة المتغيّر تساوي 10") جملة for: for animal in ["dog", "cat", "mouse"]: print("{} is a mammal".format(animal)) لاحظ استخدام الدالة format في جملة for السابقة. يمكن أيضا تطبيق الجملة على مجال عددي range: for i in range(4): print(i) for i in range(4, 8): print(i) for i in range(4, 8, 2): print(i) جملة while: x = 0 while x < 4: print(x) x += 1 # اختصارا ل x = x + 1 معالجة الأخطاء باستخدام try/except (استخدم raise لتوليد الخطأ): try: raise IndexError("This is an index error") except IndexError as e: pass except (TypeError, NameError): pass else: print("All good!") finally: print("We can clean up resources here") ملاحظات حول معالجة الأخطاء: Pass تعني عدم وجود عملية للتنفيذ. تستطيع سرد أكثر من نوع خطأ في جملة except. تستطيع استخدام جملة else مع try/except اختياريا (تنفذ في حالة كانت الشفرة البرمجية في try لم تُصدر أي خطأ). نستخدم جملة finally لتنفيذ شفرة برمجية بعد try/except بغض النظر عن وجود أخطاء أم لا، وعادةً يُعاد تحرير المصادر المستخدمة. بدلا من استخدام جملة finally لإعادة تحرير المصادر المستخدمة، تستطيع استخدام جملة with: with open("myfile.txt") as f: for line in f: print(line) تُقدم البايثون كائنًا متُعددًا (Iterable) وهو كائن مجرد (عام) يُتعامل معه مثل sequence. فمثلا الكائن المُرجع من الدالة range هو كائن مُتعدد: filled_dict = {"one": 1, "two": 2, "three": 3} our_iterable = filled_dict.keys() print(our_iterable) # => dict_keys(['one', 'two', 'three']). تستطيع المرور على عناصر الكائن المتعدد والتعامل معها: for i in our_iterable: print(i) # Prints one, two, three على الرغم من خاصية الكائن المتعدد، إلا أنه لا تستطيع استخدام الفهرس معه: our_iterable[1] # Raises a TypeError تستطيع الحصول من خلال الكائن المُتعدد على كائن iterator منه بحيث تستطيع المرور على عناصره: our_iterator = iter(our_iterable) يحتفظ الكائن iterator بحالته كلما تم استخدامه، فمثلا، باستخدام وظيفة next تستطيع الحصول على العنصر التالي في هذا الكائن: next(our_iterator) # => "one" next(our_iterator) # => "two" next(our_iterator) # => "three" بعد الحصول على كافة عناصر iterator فإن استخدام الدالة next سيعيد خطأ: next(our_iterator) # Raises StopIteration تستطيع الحصول على كافة عناصر iterator دفعة واحدة على شكل قائمة وذلك باستخدام الدالة list : list(filled_dict.keys()) # => Returns ["one", "two", "three"] الدوال نستخدم الكلمة def في تعريف الدالة، ونستخدم كلمة return في إرجاع النتيجة: def add(x, y): print("x is {} and y is {}".format(x, y)) return x + y تطبع الدالة السابقة قيمتيْ المعامليْن المُمرّرين لها وتعيد ناتج جمعهما: add(5, 6) # => prints out "x is 5 and y is 6" and returns 11 يمكن أيضا استدعاء الدالة بذكر أسماء المعاملات (شرط الترتيب غير مطلوب هنا للمعاملات): add(y=6, x=5) تستطيع تعريف دالة باستقبال عددًا غير محدد من المعاملات: def varargs(*args): return args varargs(1, 2, 3) # => (1, 2, 3) من الممكن استخدام المعاملات المُسماة لاستقبال عدد غير محدد من المعاملات أيضا: def keyword_args(**kwargs): return kwargs keyword_args(big="foot", loch="ness") # => {"big": "foot", "loch": "ness"} كما نستطيع دمج الطريقتين في نفس الدالة: def all_the_args(*args, **kwargs): print(args) print(kwargs) all_the_args(1, 2, a=3, b=4) # => (1, 2) {"a": 3, "b": 4} توجد طريقة أخرى لاستدعاء الدوال باستخدام args/kwargs وذلك عندما تكون المعطيات من النوع tuple أو قاموس: args = (1, 2, 3, 4) kwargs = {"a": 3, "b": 4} all_the_args(*args) # equivalent to foo(1, 2, 3, 4) all_the_args(**kwargs) # equivalent to foo(a=3, b=4) all_the_args(*args, **kwargs) # equivalent to foo(1, 2, 3, 4, a=3, b=4) يمكن أيضا إرجاع نتيجة من قيم متعددة على شكل tuple: def swap(x, y): return y, x x = 1 y = 2 x, y = swap(x, y) # => x = 2, y = 1 يختلف المتغيّر في نطاق scope الدالة عن المتغيّرات العامة Global: x = 5 def set_x(num): x = num # => 43 print(x) # => 43 تُستخدَم الكلمة المفتاحية global لتعريف متغيّر عام من داخل الدالة: def set_global_x(num): global x print(x) # => 5 x = num # هذا المتغير يمثل المتغير على النطاق العام وقيمته الان 6 print(x) # => 6 set_x(43) set_global_x(6) تعدّ الدوال في بايثون كائنات من الفئة الأولى: def create_adder(x): def adder(y): return x + y return adder add_10 = create_adder(10) add_10(3) # => 13 كما يمكنك تعريف دوال غير مسمّاة Anonymous functions: (lambda x: x > 2)(3) # => True (lambda x, y: x ** 2 + y ** 2)(2, 1) # => 5 ويمكنك تمرير الدالة معاملا لدالة أخرى: list(map(add_10, [1, 2, 3])) # => [11, 12, 13] list(map(max, [1, 2, 3], [4, 2, 1])) # => [4, 2, 3] list(filter(lambda x: x > 5, [3, 4, 5, 6, 7])) # => [6, 7] تستطيع استخدام مبدأ “تفهيم القائمة” للحصول على نفس نتيجة الدوال map و filter: [add_10(i) for i in [1, 2, 3]] # => [11, 12, 13] [x for x in [3, 4, 5, 6, 7] if x > 5] # => [6, 7] تستطيع استخدام مبدأ “تفيهم القاموس” و “تفهيم المجموعة” كذلك: {x for x in 'abcddeef' if x not in 'abc'} # => {'d', 'e', 'f'} {x: x**2 for x in range(5)} # => {0: 0, 1: 1, 2: 4, 3: 9, 4: 16} الوحدات Modules الوحدات في بايثون عبارة عن ملفات بايثون عادية. تستطيع أن تكتب الوحدة الخاصة بك وتستوردها في الشفرة البرمجة الخاصة بمشروعك. اسم الوحدة سيكون نفس اسم الملف الذي أنشأته لهذا الغرض. تُستورَد الوحدات بالطريقة التالية: import math print(math.sqrt(16)) # => 4.0 تستطيع الحصول على دوال محددة من الوحدات: from math import ceil, floor print(ceil(3.7)) # => 4.0 print(floor(3.7)) # => 3.0 تستطيع استيراد جميع الدوالّ من الوحدة دفعة واحدة ولكن هذا الأمر غير منصوح به: from math import * تستطيع اختصار أسماء الوحدات عند استيرادها: import math as m math.sqrt(16) == m.sqrt(16) # => True تُستخدَم الدالة المضمنة dir لمعرفة مكان ملف الوحدة. import math dir(math) إذا كان لديك ملف بايثون باسم math في نفس المجلد الذي يوجد به ملف العمل الخاص بك، فإن الملف math هو الذي سيُحمَّل ويُستورد بدلا من الوحدة التلقائية المضمنة في البايثون باسم math ذلك لأن الأولوية في حال تشابه الأسماء هي للملفات في مجلد العمل المحلي أو الحالي. دورة تطوير التطبيقات باستخدام لغة Python احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة اشترك الآن الأصناف Classes نستخدم كلمة class لتعريف صنف: class Human: لتعريف خاصية للصنف (هذه الخاصية تكون مُشاركة بين كل العناصر المتولدة من هذا الصنف): species = "H. sapiens" init هو المشيّدات Constructor الأساسي ويُستدعى عند توليد عنصر من الصنف. التسطير السفلي المكرر مرتين قبل كلمة init وبعدها يدل على أن هذا الكائن أو الخاصية يستخدمه بايثون ولا يجب علينا استخدامها مباشرة. def __init__(self, name): # إعطاء قيمة المعطى للخاصية الموجودة في الصنف self.name = name # قيمة مبدئية self._age = 0 الدالة say هي تابع عيّنة Instance method، أي أن لكل كائن نسخة خاصة به منها. تأخذ هذه التوابع أن self في أول معامل يُمرّر لها: def say(self, msg): print ("{name}: {message}".format(name=self.name, message=msg)) def sing(self): return 'yo... yo... microphone check... one two... one two...' يمكن أيضا تعريف تابع متشارك بين كل كائنات الصنف: @classmethod def get_species(cls): return cls.species نستطيع كذلك تعريف تابع ساكن يُستدعى دون الحاجة لإنشاء كائن من الصنف: @staticmethod def grunt(): return "*grunt*" يحوّل التعليمة property@ دالة إلى خاصيّة للقراءة فقط لها نفس اسم الدالة، لتؤدّي بالتالي وظيفة المسترجعات Getters. @property def age(self): return self._age يمكننا جعل الخاصية قابلة للتعيين لتصبح الدالة تعمل معدّلا Setter: @age.setter def age(self, age): self._age = age كما يمكننا السماح بحذفها: @age.deleter def age(self): del self._age يقوم مُفسر البايثون بتنفيذ كافة اشيفرة البرمجية في ملف الوحدة الذي يقرأه، ومن خلال الخاصية name نتأكد من أن كتلة الشفرة البرمجية التي في جملة الشرط ستُنفَّذ في حال كانت الوحدة هي البرنامج الرئيسي المُنفذ: if __name__ == '__main__': i = Human(name="Ian") i.say("hi") # "Ian: hi" j = Human("Joel") j.say("hello") # "Joel: hello" # استدعاء دالة الفئة i.say(i.get_species()) # "Ian: H. sapiens" # تغيير الخاصية المشتركة Human.species = "H. neanderthalensis" i.say(i.get_species()) # => "Ian: H. neanderthalensis" j.say(j.get_species()) # => "Joel: H. neanderthalensis" # استدعاء الدالة الساكنة print(Human.grunt()) # => "*grunt*" لا تستطيع استدعاء الدالة الساكنة من خلال العنصر المتولد i لأن استدعاءها بهذه الطريقة سيضيف self كمعامل لها مما سينتج عنه خطأ: print(i.grunt()) # => TypeError: grunt() takes 0 positional arguments but 1 was given i.age = 42 i.say(i.age) # => "Ian: 42" j.say(j.age) # => "Joel: 0" del i.age # i.age # => this would raise an AttributeError ترجمة -وبتصرف- للمقال Learn X in Y minutes Where X=Python3 اقرأ أيضا: تعلم لغة بايثون
  20. أُنشئت لغة البرمجة Go لإنجاز العمل بسهولة، وهي ليست ضمن الاتجاهات الحديثة في علم الحاسوب، ولكنها أحدث وسيلة برمجية لحل المشاكل في الواقع بسرعة. تمتلك لغة Go مفاهيم مُشابهة للغات البرمجة الإجبارية Imperative Languages بالإضافة لثبات أنواع البيانات Static typing، وتعدّ كذلك سريعة في البرمجة Compilation وسريعة في التشغيل والتنفيذ، ومتوافقة مع المعالجات ذات الأنوية المتعددة بهدف الاستفادة منها بسهولة، كما أنها تمتلك الكثير من المزايا التي تساعد في البرمجة للأنظمة الكبيرة والمعقَّدة. تتمتع لغة Go بمكتبة معيارية عظيمة ومجتمع برمجي متحمس ونشط. في هذا المقال، سوف نشرح أساسيات لغة Go بطريقة سهلة وبسيطة، ونُعرج على بعض المفاهيم المهمة. الشفرة البرمجية الموجودة في هذا المقال مترابطة، ولكننا قسّمناها إلى أجزاء ووضعنا عناوين لهذه الأجزاء، كما توجد الكثير من التعليقات المباشرة على الشفرة البرمجية. المواضيع الأساسية التي يغطيها هذا المقال كالتالي: كتابة التعليقات. المكتبات واستيرادها. الدوال. أنواع البيانات. القيم الراجعة المسماة. المتغيرات والذاكرة. جمل التحكم. توليد الدوال. التنفيذ المؤجل. الواجهات. المُدخلات المتعددة. معالجة الأخطاء. التنفيذ المتزامن. الويب كتابة التعليقات لكتابة تعليق من سطر واحد // single line comment لكتابة تعليق بأكثر من سطر /* Multi- line comment */ المكتبات واستيرادها يبدأ كل ملف مصدري بالكلمة المفتاحية packag. تُستخدَم الكلمة المفتاحية main لتعريف الملف كملف تشغيلي وليس مكتبة. package main لاستيراد حزمة مكتبية في الملف نستخدم التعليمة Import بالطريقة التالية: import ( "fmt" // حزمة في المكتبة المعيارية للغة "io/ioutil" // تطبق دوال إدخال وإخراج m "math" //نستخدم الحرف m لاختصار اسم مكتبة الدوال الرياضية "net/http" // خادوم ويب "os" // دوال على مستوى نظام التشغيل مثل التعامل مع الملفات "strconv" // تحويلات نصية ) الدوال Functions تُعرَّف الدوال باستخدام كلمة func متبوعة باسم الدالة. تعدّ الدالة main خاصة، وهي المدخل للملف التنفيذي للبرنامج (لغة Go تستخدم الأقواس المزخرفة {} لتحديد الأجزاء/الكتل البرمجية). func main() { // لإخراج نص على وحدة الإخراج (العرض) الرئيسية stdout نستخدم الدالة Println الموجودة في مكتبة fmt fmt.Println("Hello world!") // استدعاء دالة من نفس الحزمة الحالية beyondHello() } تحتاج الدوال لأقواس تستقبل المعاملات Parameters، وحتى في عدم وجود معاملات فإن الأقواس مطلوبة. func beyondHello() { // تعريف متغير (لا بد من تعريف المتغير قبل استخدامه) var x int // إعطاء قيمة للمتغير x = 3 // التعريف القصير باستخدام := ويشمل تعريف المتغير, وتحديد نوعه وإعطاءه قيمة y := 4 // دالة ترجع قيمتين منفصلتين sum, prod := learnMultiple(x, y) // طباعة وإخراج بشكل بسيط ومباشر fmt.Println("sum:", sum, "prod:", prod) learnTypes() } يمكن أن توجد في تعريف الدوال معاملات وقيم مرجعة متعددة، فمثلا تأخذ الدالة learnMultiple أدناه معاملين x و y وترجع قيمتين sum و prod من نوع عدد صحيح Int. func learnMultiple(x, y int) (sum, prod int) { // نفصل بين القيم المُرجعة بفاصلة عادية return x + y, x * y } أنواع البيانات Data Types func learnTypes() { //التعريفات القصيرة عادة تؤدي الغرض المطلوب // تعريف متغير نصي باستخدام علامة التنصيص المزدوجة str := "Learn Go!" // تعريف متغير نصي باستخدام علامة التنصيص المنفردة s2 := `A "raw" string literal can include line breaks.` // تعريف متغير من نوع rune وهو عبارة عن مسمى آخر لنوع int32 ويحتوي المتغير من هذا النوع على يونيكود g := 'Σ' // تعريف عدد عشري Float f := 3.14195 // تعريف عدد مركب (عقدي)Complex c := 3 + 4i // تعريف المتغيرات باستخدام var var u uint = 7 //عدد طبيعي (صحيح موجب) var pi float32 = 22. / 7 //عدد عشري من 32 بت // طريقة التحويل باستخدام التعريف القصير (byte تعتبر مسمى اخر لنوع uint8) n := byte('\n') // المصفوفات لها حجم محدد وثابت في وقت الترجمة // تعريف مصفوفة من نوع int بحجم 4 عناصر وبقيمة أولية تساوي صفر var a4 [4]int // تعريف مصفوفة بحجم 3 عناصر بالقيم 3 و 1 و 5 a3 := [...]int{3, 1, 5} يقدّم Go نوع بيانات يُسمّى الشرائح Slices. الشرائح (Slices) لها حجم ديناميكي. المصفوفات والشرائح لها مميزات ولكن حالات الاستخدام للشرائح شائعة أكثر. تعرّف التعليمة التالية شريحة من النوع int // لاحظ الفرق بين تعريف المصفوفة والشريحة، حيث عند تعريف الشريحة لا يوجد رقم يحدد حجمها s3 := []int{4, 5, 9} //تعريف شريحة من نوع int بأربعة عناصر بقيم صفرية s4 := make([]int, 4) // تعريف فقط، ولا يوجد تحديد var d2 [][]float64 // طريقة تحويل النوع من نص لشريحة bs := []byte("a slice") بحكم طبيعة الشرائح الديناميكية، فإنه من الممكن إضافة عناصر جديدة للشريحة وذلك يتم باستخدام الدالة المضمنة append. نمرر أولا الشريحة التي نريد الإضافة عليها ومن ثم العناصر التي نريد إضافتها، أنظر للمثال بالأسفل. s := []int{1, 2, 3} s = append(s, 4, 5, 6) // ستُطبَع شريحة بالمحتويات التالية [1 2 3 4 5 6] fmt.Println(s) لإضافة شريحة إلى شريحة أخرى نمرر الشريحتين للدالة بدلا من تمرير عناصر منفردة، ونتبع الشريحة الثانية بثلاث نقاط كما في المثال التالي. s = append(s, []int{7, 8, 9}...) // سيتم طباعة شريحة بالمحتويات التالية [1 2 3 4 5 6 7 8 9] fmt.Println(s) تعرّف التعليمة التالية متغيرين p وq ليكونا مؤشّريْن Pointers على متغيّرين من نوع int يحويان قيمتين مُرجعتيْن من الدالة learnMemory: p, q := learnMemory() عندما تسبق النجمة مؤشرا فإن ذلك يعني قيمة المتغير الذي يحيل إليه المؤشر، أي في المثال التالي قيمتا المتغيريْن اللذيْن ترجعهما الدالة learnMemory: fmt.Println(*p, *q) الخرائط Maps في Go هي مصفوفات ترابطية يمكن التعديل عليها ديناميكيا وهي تشابه نوع القاموس او الهاش في اللغات الأخرى. /* هنا نعرف خريطة يكون مفتاحها من نوع نصي، وقيم العناصر رقمية. */ m := map[string]int{"three": 3, "four": 4} m["one"] = 1 تعدّ لغة Go المتغيرات غير المستخدمة خطأ. التسطير السفلي بالطريقة التالية يجعلك تستخدم المتغير ولكن تتجاهل قيمته في نفس الوقت: _, _, _, _, _, _, _, _, _, _ = str, s2, g, f, u, pi, n, a3, s4, bs تُستخدَم هذه الطريقة عادة لتجاهل قيمة راجعة من دالة، فمثلا تستطيع تجاهل رقم الخطأ الراجع من دالة إنشاء الملف os.Create والذي يفيد بأن الملف موجود مسبقا، وتفترض دائما أن الملف شيُنشَأ: file, _ := os.Create("output.txt") fmt.Fprint(file, "بالمناسبة، هذه هي دالة الكتابة في ملف") file.Close() fmt.Println(s, c, a4, s3, d2, m) learnFlowControl() } القيم الراجعة المسماة Named return values خلافا للغات الأخرى، من الممكن أن تكون للدوال قيم راجعة مسماة. حيث يتم إعطاء اسم للقيمة الراجعة من الدالة وذلك في سطر تعريف الدالة، وهذه الميزة تتيح الرجوع بسهولة من أي نقطة في الدالة بالإضافة لاستخدام كلمة return فقط دون ذكر شيء بعدها: func learnNamedReturns(x, y int) (z int) { z = x * y //هنا كتبنا كلمة return فقط وضمنيا نعني إعادة قيمة المتغير z return } ملاحظة: تعتمد لغة Go كثيرا على جمع الموارد غير المستخدمة Garbage collection. توجد في Go مؤشّرات لكن بدون إجراء عمليات حسابية عليها (تستطيع الخطأ في استخدام مؤشر فارغ ولكن لا تستطيع الزيادة على المؤشر). المتغيرات والذاكرة المتغيّران p وq أدناه هما مؤشّران على النوع int ويمثّلان قيمتين راجعتين في الدالة. يكون المؤشّران عند تعريفهما فارغين؛ إلا أن استخدام الدالة المُضمّنة new يجعل قيمة المتغيّر العددي الذي يحيل إليه المؤشّرp مساوية للصفر، وبالتالي يأخذ حيزا من الذاكرة؛ أي أن المؤشر p لم يعد فارغا. func learnMemory() (p, q *int) { p = new(int) // تعريف شريحة من 20 عنصر كوحدة واحدة في الذاكرة s := make([]int, 20) // إعطاء قيمة لأحد العناصر s[3] = 7 // تعريف متغير جديد محلي على مستوى الدالة r := -2 // إرجاع قيمتين من الدالة هما عبارة عن عناوين الذاكرة للمتغيرات s و r على الترتيب. return &s[3], &r } func expensiveComputation() float64 { return m.Exp(10) } جمل التحكم تتطلّب الجمل الشرطية وجود أقواس مزخرفة ولا تتطلب وجود أقواس هلالية. func learnFlowControl() { if true { fmt.Println("told ya") } if false { // Pout. } else { // Gloat. } نستخدم جملة switch في حال حاجتنا لكتابة أكثر من جملة شرطية متتابعة. x := 42.0 switch x { case 0: case 1: case 42: case 43: default: } كما الجملة الشرطية، فإن جملة for لا تأخذ أقواس هلالية.المتغيرات المعرفة في جملة for تكون مرئية على مستوى الجملة. for x := 0; x < 3; x++ { fmt.Println("iteration", x) } جملة for هي جملة التكرار الوحيدة في لغة Go ولها شكل آخر بالطريقة التالية: for { // تكرار لا نهائي // نستطيع استخدام break لوقف التكرار break // نستطيع استخدام continue للذهاب للتكرار القادم continue } تستطيع استخدام range للمرور على عناصر مصفوفة، شريحة، نص، خريطة أو قناة Channel تـعيد range قيمة واحدة عند استخدام قناة، وقيمتين عند استخدام شريحة أو مصفوفة أو نص أو خريطة. // مثال: for key, value := range map[string]int{"one": 1, "two": 2, "three": 3} { // نطبع قيمة كل عنصر في الخريطة fmt.Printf("key=%s, value=%d\n", key, value) } استخدم علامة التسطير السفلي مقابل القيمة الراجعة إذا كنت تريد الحصول على القيمة فقط، كالتالي: for _, name := range []string{"Bob", "Bill", "Joe"} { fmt.Printf("Hello, %s\n", name) } نستطيع استخدام التعريف القصير مع الجملة الشرطية بحيث يُعرف متغير ومن ثم يُفحَص في جملة الشرط. نعرّف في ما يلي متغيرًا y ونقوم بإعطائه قيمة ومن ثم نقوم بوضع شرط الجملة بحيث يتم فصلهما ب فاصلة منقوطة. if y := expensiveComputation(); y > x { x = y } نستطيع تعريف دوال وهمية anonymous مباشرة في الشفرة البرمجية” xBig := func() bool { // عرّفنا المتغيّر x التالي قبل جملة switch السابقة return x > 10000 } x = 99999 // ترجع الدالة xBig الآن القيمة true fmt.Println("xBig:", xBig()) x = 1.3e3 // بعد تعديل قيمة x إلى 1.3e3 التي تساوي 1300 (أي أكبر من 1000) فإن الدالة xBig ترجع false fmt.Println("xBig:", xBig()) بالإضافة لما سبق، فإنه من الممكن تعريف الدالة الوهمية واستدعائها في نفس السطروتمريرها في معطى لدالة أخرى بشرط أن يتم استدعاؤها مباشرة وأن يكون نوع النتيجة متوافقا مع ما هو متوقع في معطى الدالة. fmt.Println("Add + double two numbers: ", func(a, b int) int { return (a + b) * 2 }(10, 2)) goto love love: learnFunctionFactory() // دالة ترجع دالة learnDefer() // التأجيل learnInterfaces() // التعامل مع الواجهات } توليد الدوال نستطيع التعامل مع الدوال ككائنات منفصلة، فمثلا من الممكن أن نقوم بإنشاء دالة وتكون القيمة الراجعة منها دالة أخرى. func learnFunctionFactory() { تعدّ الطريقتان التاليتان في طباعة الجملة متماثلتين، إلا أن الطريقة الثانية أوضح ومقروءة أكثر وهي الشائعة. fmt.Println(sentenceFactory("summer")("A beautiful", "day!")) d := sentenceFactory("summer") fmt.Println(d("A beautiful", "day!")) fmt.Println(d("A lazy", "afternoon!")) } المزخرفات Decorators موجودة في بعض لغات البرمجة، وموجودة بنفس المفهوم في لغة Go بحيث نستطيع تمرير معطيات إلى الدوال. func sentenceFactory(mystring string) func(before, after string) string { return func(before, after string) string { return fmt.Sprintf("%s %s %s", before, mystring, after) } } التنفيذ المؤجل نستطيع استخدام خاصية التأجيل في الدوال بحيث ننفّذ إجراءً قبل إعادة القيمة المرجعة، وفي حال كتابة أكثر من إجراء، فإن تنفيذ هذه الإجراءات يكون بطريقة عكسية، كما في وظيفة learnDefer: func learnDefer() (ok bool) { // تُنفَّذ التعليمات المؤجلة قبل أن ترجع الوظيفة النتيجة. defer fmt.Println("deferred statements execute in reverse (LIFO) order.") defer fmt.Println("\nThis line is being printed first because") // يُستخدَم تأجيل التنفيذ عادة لإغلاق ملف بعد فتحه. return true } الواجهات Interfaces نعرّف في ما يلي دالة باسم Stringer تحتوي على دالة واحدة باسم String ؛ ثم نعرّف هيكلا Struct من خانتين نوع int باسم x وy. type Stringer interface { String() string } type pair struct { x, y int } نعرّف في ما يلي دالة String على النوع pair، ليصبح pair تطبيقا Implementation للواجهة Stringer. يُسمى المتغيّرp أدناه بالمُستقبل. لاحظ كيفية الوصول لحقول الهيكل pair وذلك باستخدام اسم الهيكل متبوعا بنقطة ثم اسم الحقل. func (p pair) String() string { return fmt.Sprintf("(%d, %d)", p.x, p.y) } تُستخدَم الأقواس الهلالية لإنشاء عنصر من الهياكل Structs. نستخدم التعريف القصير (باستخدام := ) في المثال أدناه لإنشاء متغير باسم p وتحديد نوعه بالهيكل pair . func learnInterfaces() { p := pair{3, 4} // نستدعي الدالة String الخاصة بالنوع pair fmt.Println(p.String()) // نُعرف متغيرًا باسم i من نوع الواجهة المعرفة سابقا Stringer var i Stringer // هذه المساواة صحيحة، لأن pair تُطبق Stringer i = p /* نستدعي الدالة String الخاصة بالمتغير i من نوع Stringer ونحصُل على نفس النتيجة السابقة */ fmt.Println(i.String()) /* عند تمرير المتغيرات السابقة مباشرةإلى دوال الحزمة fmt الخاصة بالطباعة والإخراج، فإن هذه الدوال تستدعي الدالة String لطباعة التمثيل الخاص بالمتغير. */ // يعطي السطران التاليان نفس النتيجة السابقة للطباعة fmt.Println(p) fmt.Println(i) learnVariadicParams("great", "learning", "here!") } المدخلات المتعددة من الممكن أن نمرر معطيات متغيرة العدد للدوال. func learnVariadicParams(myStrings ...interface{}) { /* تمرّ جملة التكرار التالية على عناصر المعطيات المدخلة للدالة. التسطير السفلي هنا نعني به تجاهل المؤشر الخاص بالعنصر الذي نمر عليه. */ for _, param := range myStrings { fmt.Println("param:", param) } /* هنا نمرّر مدخلات الدالة ذات العدد المتغير كمعامل لدالة أخرى (للدالة Sprintln) */ fmt.Println("params:", fmt.Sprintln(myStrings...)) learnErrorHandling() } معالجة الأخطاء Errors Handling تُستخدَم الكلمة المفتاحية “ok,” لمعرفة صحة عبارة من عدمها. في حال حدوث خطأ فيمكننا استخدام err لمعرفة تفاصيل أكثر عن الخطأ. func learnErrorHandling() { m := map[int]string{3: "three", 4: "four"} if x, ok := m[1]; !ok { // ok هنا ستكون false لأن رقم 1 غير موجود في الخريطة m fmt.Println("no one there") } else { // x ستكون القيمة الموجودة في map fmt.Print(x) } /* هنا نحاول أن نقوم بعمل تحويل لقيمة نصية إلى عدد مما سينتج عنه خطأ, ونقوم بطباعة تفاصيل الخطأ في حالة أن err ليست nil */ if _, err := strconv.Atoi("non-int"); err != nil { fmt.Println(err) } learnConcurrency() } // المعطى c هنا من نوع قناة، وهو كائن لتأمين الاتصالات المتزامنة func inc(i int, c chan int) { // عندما يظهر عنصر من نوع قناة على الشمال، فإن العملية <- تعني إرسال c <- i + 1 } التنفيذ المتزامن Concurrency نستخدم الدالة السابقة لعمل إضافة عددية على بعض الأرقام بالتزامن. نستخدم make كما فعلنا في بداية المقال لإنشاء متغير دون تحديد قيمة له. func learnConcurrency() { // هنا نقوم بإنشاء متغير من نوع قناة وباسم c c := make(chan int) /* نبدأ بإنشاء ثلاثة دوال متزامنة للغة Go. الأعداد سيتم الزيادة عليهابالتزامن (وبالتوازي في حال كان الجهاز مُهيئاً لذلك). */ // كافة الإرسالات ستتجه لنفس القناة // كلمة go هنا تعني بدء دالة أو وظيفة جديدة go inc(0, c) go inc(10, c) go inc(-805, c) // ثم نقوم بعمل ثلاثة قراءات من نفس القناة وطباعة النتائج. /* لاحظ أنه لا يوجد تحديد ترتيب لوصول القراءات من القناة، ولاحظ أيضا أنه عند ظهور القناة على يمين العملية <- فهذا يعني أننا نقوم بقراءة واستقبال من القناة. */ fmt.Println(<-c, <-c, <-c) // قناة جديدة تحتوي على نص cs := make(chan string) // قناة تحتوي على قنوات نصية ccs := make(chan chan string) // إرسال قيمة 84 إلى القناة c go func() { c <- 84 }() // إرسال كلمة wordy للقناة cs go func() { cs <- "wordy" }() /* جملة Select تشبه جملة switch ولكنها في كل حالة تحتوي على عملية خاصة بقناة جاهزة للتواصل معها. */ select { // القيمة المُستلمة من القناة من الممكن أن تُحفظ في متغير. case i := <-c: fmt.Printf("it's a %T", i) case <-cs: fmt.Println("it's a string") // قناة فارغة ولكنها جاهزة للتواصل case <-ccs: fmt.Println("didn't happen.") } // برمجة الويب learnWebProgramming() } الويب نستطيع بدء خادوم ويب باستخدام دالة واحدة من حزمة http. نمرّر في المعامل الأول للدالة ListenAndServe عنوان TCP للاستماع له، والمعامل الثاني واجهة عبارة عن معالج http. func learnWebProgramming() { go func() { err := http.ListenAndServe(":8080", pair{}) // نطبع الأخطاء في حال وجودها. fmt.Println(err) }() requestServer() } // اجعل pair معالج http وذلك بواسطة تطبيق دالته الوحيدة المسماة ServeHTTP func (p pair) ServeHTTP(w http.ResponseWriter, r *http.Request) { // تتبع الدالة Write للحزمة http.ResponseWriter ونستخدمها لإرجاع رد لطلب http w.Write([]byte("You learned Go in Y minutes!")) } func requestServer() { resp, err := http.Get("http://localhost:8080") fmt.Println(err) defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) fmt.Printf("\nWebserver said: `%s`", string(body)) } ترجمة – بتصرّف – للمقال Learn X in Y minutes Where X=Go.
  21. في هذا المقال، نقدم لكم لمحة سريعة عن أساسيات لغة البرمجة روبي، ويعتبر هذا المقال مُخصص بالدرجة الأولى للمبتدئين في تعلم اللغة ولديهم أرضية مسبقة عن مفاهيم البرمجة بشكل عام. سنتناول في هذا المقال المواضيع التالية: كتابة التعليقات. المصفوفات. جمل التحكم. معالجة الخطأ. بناء الوظائف. جملة yield. الفئات. ملاحظة: ناتج تنفيذ الأمثلة والأوامر الموجودة في هذا المقال تقع بعد علامة #=> من كل نتيجة أو أمر. كتابة التعليقات # هذا تعليق =begin تعليق من أكثر من سطر =end قبل البدء، لا بد من التنويه أن كل شيء عبارة عن كائن في لغة روبي. الأرقام عبارة عن كائنات، والوظيفة class في السطر التالي تعيد نوع الكائن: 3.class #=> Fixnum 3.to_s #=> "3" العمليات الحسابية والثنائية 1 + 1 #=> 2 8 - 1 #=> 7 10 * 2 #=> 20 35 / 5 #=> 7 2**5 #=> 32 5 % 3 #=> 2 3 & 5 #=> 1 3 | 5 #=> 7 3 ^ 5 #=> 6 العمليات الرياضية سهلة الاستدعاء على مستوى الكائن: 1.+(3) #=> 4 10.* 5 #=> 50 بعض القيم تُعتبر كائنات مثل: nil # تشبه القيمة الفارغة في اللغات الأخرى true # صحيح منطقي false # خطأ منطقي nil.class #=> NilClass true.class #=> TrueClass false.class #=> FalseClass المساواة: 1 == 1 #=> true 2 == 1 #=> false اللامساواة: 1 != 1 #=> false 2 != 1 #=> true تُعتبر القيمة الفارغة مرادفة للقيمة المنطقية الخاطئة: !nil #=> true !false #=> true !0 #=> false المقارنات: 1 < 10 #=> true 1 > 10 #=> false 2 <= 2 #=> true 2 >= 2 #=> true عمليات المقارنة المُجمعة: 1 <=> 10 #=> -1 10 <=> 1 #=> 1 1 <=> 1 #=> 0 العمليات المنطقية: true && false #=> false true || false #=> true !true #=> false يوجد نسخة أخرى من العمليات المنطقية ولكن بتطبيق مفهوم الأولوية المنخفضة، مما يعني استخدامها كبناء للتحكم في التدفقات (Flow Control) وربط الجمل ببعضها حتى تقوم أحدها بإرجاع قيمة منطقية صحيحة أو خاطئة. فمثلا، في السطر التالي تُستدعى do_something_else في حال كان استدعاء do_something ناجحاً. do_something() and do_something_else() وهنا يُستدعى log_error في حال فشل استدعاء: do_someting do_something() or log_error() النصوص النصوص عبارة عن كائنات: 'I am a string'.class #=> String "I am a string too".class #=> String placeholder = 'use string interpolation' "I can #{placeholder} when using double quoted strings" #=> "I can use string interpolation when using double quoted strings" يُفضل استخدام علامة التنصيص المنفردة على المزدوجة وذلك قدر الإمكان. علامة التنصيص المزدوجة تُضيف بعض الحسابات الداخلية الزائدة، ومن الممكن جمع النصوص ببعضها بشرط عدم استخدام الأرقام. 'hello ' + 'world' #=> "hello world" 'hello ' + 3 #=> TypeError: can't convert Fixnum into String 'hello ' + 3.to_s #=> "hello 3" دمج النصوص مع العمليات: 'hello ' * 3 #=> "hello hello hello " الإضافة لنص: 'hello' << ' world' #=> "hello world" لطباعة نص وسطر في النهاية نستخدم وظيفة puts: puts "I'm printing!" #=> I'm printing! #=> nil طباعة نص دون سطر في النهاية: print "I'm printing!" #=> I'm printing! => nil المتغيرات تعريف المتغيرات: x = 25 #=> 25 x #=> 25 استخدام عملية المساواة تُرجع القيمة المستخدمة وهذا يعني أنك تستطيع إجراء عمليات مساواة متعددة كما المثال التالي: x = y = 10 #=> 10 x #=> 10 y #=> 10 من المتعارف عليه استخدام طريقة snake_case في تسمية المتغيرات: snake_case = true حاول أن تستخدم أسماء متغيرات ذات دلالة: path_to_project_root = '/good/name/' path = '/bad/name/' الرموز (Symbols) في لغة روبي عبارة عن كائنات، وهي ثابتة. وتُمَثِّل الرموز ثوابت من الممكن إعادة استخدامها ويتم تمثيلها داخليا بأرقام. وغالبا يتم استخدامها بدلا من النصوص لتوصيل قيم ذات معنى ومحددة: :pending.class #=> Symbol status = :pending status == :pending #=> true status == 'pending' #=> false status == :approved #=> false المصفوفات لتعريف مصفوفة نقوم بالتالي: array = [1, 2, 3, 4, 5] #=> [1, 2, 3, 4, 5] من الممكن أن تحتوي المصفوفة على عناصر ذات أنواع مختلفة: [1, 'hello', false] #=> [1, "hello", false] يتم فهرسة المصفوفات بطريقة أمامية: array[0] #=> 1 array.first #=> 1 array[12] #=> nil ومن الممكن فهرستها بطريقة عكسية: array[-1] #=> 5 array.last #=> 5 ومن الممكن تحديد فهرس البداية والنهاية للحصول على جزء أو شريحة من المصفوفة: array[2, 3] #=> [3, 4, 5] نستخدم وظيفة reverse لإجراء عملية عكس المصفوفة: a=[1,2,3] a.reverse! #=> [3,2,1] من الممكن أيضا أن نستخدم نطاقًا لإجراء عملية الاقتطاع من المصفوفة بالطريقة التالية: array[1..3] #=> [2, 3, 4] لإجراء عملية الإضافة على المصفوفة نقوم بالتالي: array << 6 #=> [1, 2, 3, 4, 5, 6] أو : array.push(6) #=> [1, 2, 3, 4, 5, 6] للتأكد من وجود قيمة في المصفوفة نستخدم الوظيفة include : array.include?(1) #=> true هاش Hash الهاش Hash في لغة روبي هو القاموس الرئيسي باستخدام المفتاح والقيمة، ولتعريف الهاش نستخدم الأقواس المزخرفة: hash = { 'color' => 'green', 'number' => 5 } hash.keys #=> ['color', 'number'] يتم البحث في الهاش باستخدام المفتاح بالطريقة التالية: hash['color'] #=> 'green' hash['number'] #=> 5 في حالة البحث في الهاش باستخدام مفتاح غير موجود فإن النتيجة المرجعة هي nil : hash['nothing here'] #=> nil بعد نسخة روبي 1.9 يوجد طريقة خاصة لاستخدام الرموز كمفاتيح للهاش: new_hash = { defcon: 3, action: true } new_hash.keys #=> [:defcon, :action] لفحص وجود مفتاح أو قيمة في الهاش نستخدم الطريقة التالية: new_hash.key?(:defcon) #=> true new_hash.value?(3) #=> true ملاحظة/ المصفوفات والهاش في الروبي قابلة للعد (Enumerable) ، وكلاهما يحتوي على مجموعة من الوظائف المفيدة. جمل التحكم جملة الشرط: if true 'if statement' elsif false 'else if, optional' else 'else, also optional' end جملة التكرار for: for counter in 1..5 puts "iteration #{counter}" end #=> iteration 1 #=> iteration 2 #=> iteration 3 #=> iteration 4 #=> iteration 5 على الرغم من وجود جملة التكرار وشيوعها، إلا أنه لا يوجد من يستخدمها، وبدلا من ذلك يجب عليك استخدام جملة each وتمرير كتلة من الشفرة البرمجية لها. هذه الكتلة من الشفرة البرمجية تُرادف lambdas أو الوظائف الوهمية. عند استخدام وظيفة each مع نطاق من الأرقام، فإن كتلة الشفرة البرمجية المُمَرَرَة لها تُنفذ مرة واحدة مع كل عنصر من النطاق. يُمرَّر عداد كمعامل لكتلة الشفرة البرمجية،وتُكتَب جملة each بالطريقة التالية: (1..5).each do |counter| puts "iteration #{counter}" end #=> iteration 1 #=> iteration 2 #=> iteration 3 #=> iteration 4 #=> iteration 5 نستطيع إحاطة كتلة الشفرة البرمجية بأقواس مزخرفة: (1..5).each { |counter| puts "iteration #{counter}" } نستطيع استخدام each للمرور على محتويات التراكيب مثل المصفوفات والهاش: array.each do |element| puts "#{element} is part of the array" end hash.each do |key, value| puts "#{key} is #{value}" end إذا كنت تريد الحصول على فهرس العنصر الذي تمر عليه في جملة each تستطيع استخدام جملة each_with_index وتعريف متغير الفهرس من خلالها. انظر المثال التالي: array.each_with_index do |element, index| puts "#{element} is number #{index} in the array" end counter = 1 while counter <= 5 do puts "iteration #{counter}" counter += 1 end #=> iteration 1 #=> iteration 2 #=> iteration 3 #=> iteration 4 #=> iteration 5 توجد مجموعة من الوظائف الأخرى لتنفيذ الحلقات Loops في لغة الروبي، فمثلا توجد map، reduce ، inject والقائمة تطول. Map تأخذ مصفوفة كمعامل، وتقوم بالمرور على عناصرها وإجراء عمليات عليها وترجعها في مصفوفة جديدة، كما المثال التالي: array = [1,2,3,4,5] doubled = array.map do |element| element * 2 end puts doubled #=> [2,4,6,8,10] puts array #=> [1,2,3,4,5] جملة case : grade = 'B' case grade when 'A' puts 'Way to go kiddo' when 'B' puts 'Better luck next time' when 'C' puts 'You can do better' when 'D' puts 'Scraping through' when 'F' puts 'You failed!' else puts 'Alternative grading system, eh?' end #=> "Better luck next time" نستطيع استخدام نطاق مع جملة case بالطريقة التالية: grade = 82 case grade when 90..100 puts 'Hooray!' when 80...90 puts 'OK job' else puts 'You failed!' end #=> "OK job" معالجة الخطأ begin raise NoMemoryError, 'You ran out of memory.' rescue NoMemoryError => exception_variable puts 'NoMemoryError was raised', exception_variable rescue RuntimeError => other_exception_variable puts 'RuntimeError was raised now' else puts 'This runs if no exceptions were thrown at all' ensure puts 'This code always runs no matter what' end بناء الوظائف والدوال def double(x) x * 2 end الوظائف ضمنيا تعيد قيمة آخر جملة في الوظيفة: double(2) #=> 4 الأقواس تُعتبر إضافية، ومن الممكن استدعاء الوظيفة من دونهم: double 3 #=> 6 double double 3 #=> 12 def sum(x, y) x + y end معاملات الوظائف يتم الفصل بينها بواسطة الفاصلة. sum 3, 4 #=> 7 sum sum(3, 4), 5 #=> 12 جملة yield كل الوظائف تمتلك ضمنيا معامل كتلة إضافي خاص بها، وتُستدعى بواسطة كلمة yield : def surround puts '{' yield puts '}' end surround { puts 'hello world' } # { # hello world # } تستطيع تمرير كتلة من الشفرة البرمجية للوظيفة، ونستخدم رمز & لحفظ عنوان كتلة الشفرة البرمجية المُمَرَرَة. def guests(&block) block.call 'some_argument' end تستطيع تمرير أكثر من معامل للوظيفة بشكل غير محدد باستخدام رمز *، وهذه المجموعة من المعاملات تتحول إلى مصفوفة والتي بدورك تستطيع المرور عليها باستخدام جملة each: def guests(*array) array.each { |guest| puts guest } end إذا كانت الوظيفة تُرجع مصفوفة، فإنك تستطيع استخدام المساواة المتعددة لأكثر من متغير في نفس الوقت (unpacking): def foods ['pancake', 'sandwich', 'quesadilla'] end breakfast, lunch, dinner = foods breakfast #=> 'pancake' dinner #=> 'quesadilla' من المتفق عليه أن كل الوظائف التي تعيد قيمة منطقية لابد أن تنتهي بعلامة استفهام عند استدعائها: 5.even? # false 5.odd? # true إذا كانت الوظيفة تنتهي بعلامة تعجب، فهذا يعني أن التغيير الذي يتم على المتغير أو العنصر يكون مباشرا على قيمته، أما بدون علامة تعجب، فإن العملية لا تؤثر على العنصر، ويتم إعادة التغيير في عنصر جديد. انظر للمثال التالي: company_name = "Dunder Mifflin" company_name.upcase #=> "DUNDER MIFFLIN" company_name #=> "Dunder Mifflin" company_name.upcase! # we're mutating company_name this time! company_name #=> "DUNDER MIFFLIN" الأصناف Classes تُعرَّف الأصناف باستخدام الكلمة المحجوزة class: class Human نعرّف في ما يلي متغيرًا على مستوى الصنف، وهو مُشارَك بين الكائنات المتولدة الخاصة بهذا الصنف: @@species = 'H. sapiens' الطريقة الأساسية للاستهلال Initialization : def initialize(name, age = 0) إعطاء قيمة المعامل “الاسم” للمتغير الخاص بالكائن المتولد من الفئة بنفس الاسم @name = name في حالة عدم تمرير معامل باسم “العمر” فإن القيمة التلقائية هي التي ستمرر للمتغير الخاص بالكائن المتولد: @age = age end وظيفة التعديل الأساسية (Setter): def name=(name) @name = name end وظيفة الاسترجاع الأساسية (Getter): def name @name end نستطيع تنفيذ وظيفتي التعديل والاسترجاع بواسطة attr_accessor كالتالي: attr_accessor :name ويمكن فصل العمليتين عن بعضهما. المُسترجِع :getter attr_reader :name المعدِّل setter: attr_writer :name للتمييز بين الوظائف الخاصة بالصنف، والوظائف الخاصة بالكائن المتولد من الصنف، نستخدم كلمة self، وهي خاصة لتعريف الوظائف على مستوى الفئة. def self.say(msg) puts msg end def species @@species end end تعريف كائنين من الصنف Human: jim = Human.new('Jim Halpert') dwight = Human.new('Dwight K. Schrute') استدعاء بعض الوظائف: jim.species #=> "H. sapiens" jim.name #=> "Jim Halpert" jim.name = "Jim Halpert II" #=> "Jim Halpert II" jim.name #=> "Jim Halpert II" dwight.species #=> "H. sapiens" dwight.name #=> "Dwight K. Schrute" استدعاء الوظيفة على مستوى الصنف: Human.say('Hi') #=> "Hi" يُعرَّف مجال المتغيرات في المكان التي عُرِّف فيه المتغيّر، والمتغيرات التي تبدأ ب علامة $ تكون على مستوى النطاق الواسع Global Variable: $var = "I'm a global var" defined? $var #=> "global-variable" المتغيرات التي تبدأ بعلامة @ تكون على مستوى الكائن المتولد: @var = "I'm an instance var" defined? @var #=> "instance-variable" المتغيرات التي تبدأ ب @@ تكون على مستوى الصنف: @@var = "I'm a class var" defined? @@var #=> "class variable" المتغيرات التي تبدأ بحرف كبير تكون ثوابتا: Var = "I'm a constant" defined? Var #=> "constant" المتغير الخاص بالصنف يتشاركه الصنف وكل الأصناف التي تريث منه: # base class class Human @@foo = 0 def self.foo @@foo end def self.foo=(value) @@foo = value end end # derived class class Worker < Human end Human.foo # 0 Worker.foo # 0 Human.foo = 2 # 2 Worker.foo # 2 المتغير الخاص بالكائن المتولد من الصنف غير مشارك أو مرئي في الأصناف التي ترث من الصنف الرئيسي: class Human @bar = 0 def self.bar @bar end def self.bar=(value) @bar = value end end class Doctor < Human end Human.bar # 0 Doctor.bar # nil عند استخدام عملية include لتضمين وحدة Module داخل صنف، فإن الوظائف الخاصة بالوحدة تكون مضمنة في الكائنات المتولدة من الصنف. وعند استخدام عملية extend للوحدة داخل صنف، فإن الوظائف الخاصة بالوحدة تكون مضمنة في نفس الصنف. module ModuleExample def foo 'foo' end end class Person include ModuleExample end class Book extend ModuleExample end Person.foo # => NoMethodError: undefined method `foo' for Person:Class Person.new.foo # => 'foo' Book.foo # => 'foo' Book.new.foo # => NoMethodError: undefined method `foo' تُنفَّذ دوال استرداد Callbacks عندما تُطبَق include أو extend على الوحدة : module ConcernExample def self.included(base) base.extend(ClassMethods) base.send(:include, InstanceMethods) end module ClassMethods def bar 'bar' end end module InstanceMethods def qux 'qux' end end end class Something include ConcernExample end Something.bar # => 'bar' Something.qux # => NoMethodError: undefined method `qux' Something.new.bar # => NoMethodError: undefined method `bar' Something.new.qux # => 'qux' ترجمة – بتصرّف – للمقال Learn X in Y minutes Where X=ruby.
  22. وعليكم السلام ورحمة الله وبركاته،،، يوجد العديد من الطرق والتقنيات والخورازميات المستخدمة في هذا المجال، ويوجد حلول جاهزة لبعض مشاكل التتبع مثل تتبع الوجوه او السيارات. الروابط التالية قد تفيدك https://www.mathworks.com/help/vision/examples/motion-based-multiple-object-tracking.html https://www.mathworks.com/help/vision/examples/tracking-pedestrians-from-a-moving-car.html https://www.mathworks.com/help/vision/examples/using-kalman-filter-for-object-tracking.html
  23. عليك بإستخدام ما يسمى ب Restful API حسب اللغة التي تبني بها. أغلب لغات البرمجة الحالية تقدم مكتبات جاهزة لبناء وتطوير Restful API. هذا المقال ل @محمد أحمد العيليشرح لك كيفية بناء Restful API باستخدام Laravel: تحياتي
  24. سنتحدث في هذا المقال عن مفهوم العلاقات بين جداول قاعدة البيانات، وما أنواع هذه العلاقات وكيف تتمثل وما هو أثرها على العمل. ما هي العلاقات بين الجداول عند إنشاء جداول في قاعدة البيانات، فإن الظاهر لنا أننا نقوم ببناء جداول منفصلة وغير مترابطة، ولكننا في الواقع العملي نحتاج لربط هذه الكيانات المنفصلة بحيث تُبنَى علاقات تحكم البيانات الموجودة في هذه الجداول، وتحكم طريقة التعامل مع هذه البيانات. تنشَأ العلاقة بين جدوليْن عندما يُربط عمودان فيهما مع بعضهما عن طريق وجود قيود مطبقة على العمودين، بحيث يكون قيد المفتاح الرئيسي على عمود في الجدول “الأب” وقيد المفتاح الأجنبي على العمود في الجدول “الابن”، وعادة يكون اسم العمودين واحدًا في كلا الجدولين. مثلا، لحفظ عناوين الأشخاص نستطيع إنشاء جدول باسم Address ونربطه بجدول الأشخاص Persons بعلاقة تحكم البيانات الموجودة في الجدولين، بحيث يكون لكل شخص في الجدول Persons عنوان واحد مرتبط به في الجدول Address. يُربَط الجدولان عن طريق عمود باسم Person_Id في كلا الجدولين. مثال آخر، لو أردنا أن نتابع عملية استعارة الكتب في مكتبة، فإننا سننشئ جدولًا باسم Borrowed_Books (كُتُب مُعارة) ونربطها بالجدول Persons عن طريق العمود Person_Id. يستطيع الشخص الواحد - في هذا النوع من الربط - أن يستعير أكثر من كتاب. في هذا المثال، لو أننا حفظنا بيانات الأشخاص والكتب المستعارة في جدول واحد، ستظهر لنا مشكلة تكرار البيانات Data Redundancy لأننا سنكرّر بيانات الشخص لكل كتاب يستعيره. ماذا نستفيد من بناء العلاقات بين الجداول؟ التخلص من مشكلة تكرار البيانات عن طريق فصلها وحفظها في أكثر من جدول، فمشكلة تكرار البيانات هي عدو مستخدمي قواعد البيانات ومسؤوليها، لأنها تتسبب بزيادة حجم قاعدة البيانات بقدر كبير وبسرعة، وترفع السرعات المطلوبة لتنفيذ الاستعلامات، وتجعل من موضوع صيانة قاعدة البيانات كابوسا مقلقا. الحفاظ على دقة وسلامة البيانات في قاعدة البيانات، فمع وجود العلاقات بين الجداول، سوف تضمن مثلا عدم وجود كتاب مُعار ليس له شخص استعاره، أو عنوان وهمي ليس له صاحب، وقس على ذلك العديد من الأمثلة. استخراج البيانات من أكثر من جدول بكفاءة وسرعة عن طريق بناء جمل ربط استعلامية تطلب المعلومات من أعمدة مختلفة في جداول مختلفة، وإخراج النتيجة بطريقة مفيدة ومرتبة. أنواع العلاقات توجد أربعة أنواع من العلاقات بين الجداول كالتالي: علاقة واحد إلى واحد (One-to-One). علاقة واحد إلى كثير أو علاقة كثير إلى واحد (One-to-Many / Many-to-One). علاقة كثير إلى كثير (Many-to-Many). علاقة المرجعية الذاتية (Self Referencing). علاقة واحد إلى واحد لنفترض أن الجدول Persons لديه البنية والبيانات التالية: Person_ID First_Name Last_Name Age Address 101 Ibrahim Mohammed 31 12 Main St, Doha 102 Mohammed Khaled 25 Gaza, Middle Center نستطيع أن نضع بيانات العنوان في جدول منفصل ونسميه Address وتكون بنية الجدوليْن كالتالي. الجدول Persons: Person_ID First_Name Last_Name Age Address_Id 101 Ibrahim Mohammed 31 1 102 Mohammed Khaled 25 2 الجدول Address: Address_ID Address 1 12 Main St, Doha 2 Gaza, Middle Center لاحظ أنه أصبح لدينا عمود بنفس الاسم Address_Id في كلا الجدولين. لبناء العلاقة بين الجدولين، طبّقنا قيد المفتاح الأجنبي على العمود Address_Id في الجدول Persons بحيث يأخذ قيمه من العمود Address_Id في الجدول Address والمطبق عليه قيد المفتاح الرئيسي. أصبحت لدينا الآن علاقة بين الجدولين، وفي حال كان كل عنوان في الجدول Address يقترن فقط بشخص واحد في الجدول Persons فعندها نسمي هذه العلاقة واحدًا إلى واحد. يجب التنويه إلى أن هذا النوع من العلاقات غير مستخدم كثيرا، فالجدول الأول الذي يحتوي العنوان وبيانات الشخص يفي بالغرض في أغلب الأحيان. نستطيع تمثيل العلاقة بالشكل التالي: لاحظ أن وجود العلاقة اختياري، فمن الممكن أن يكون لدينا سجل في الجدول Persons دون عنوان له في الجدول Address وهذا مرتبط بعدم تطبيق قيد القيمة غير الفارغة على العمود Address_Id. في حال طُبِّق قيد غير القيمة غير الفارغة على العمود، فهنا تصبح العلاقة واجبة بين الجدولين، ولا يمكن أن نُنْشئ سجلًّا في الجدول Persons إلا بإدخال قيمة موجودة للعمود Address_Id وهو في مثالنا هذا غير منقطي نوعا ما. دورة علوم الحاسوب دورة تدريبية متكاملة تضعك على بوابة الاحتراف في تعلم أساسيات البرمجة وعلوم الحاسوب اشترك الآن علاقة واحد إلى كثير أو علاقة كثير إلى واحد هذا النوع من العلاقات هو الشائع بين أنواع العلاقات بين الجداول في قاعدة البيانات، لوجود تطبيقات كثيرة عليه، فمثلا: الطالب (واحد) يستطيع أن يدرس أكثر من مساق (كثير). الطبيب يعالج ويتابع حالة مريض واحد أو أكثر. طلبية الشراء تحتوي على أكثر من عنصر. الشخص يستعير أكثر من كتاب. وقس على ذلك العديد من الأمثلة. لنفترض وجود جدول للزبناء Customers بالهيكلية التالية: Customer_ID Customer_Name 1 Ibrahim Mohammed 2 Mohammed Ahmed نستطيع ربط جدول الزبناء السابق بجدولٍ للطلبيات Orders بعلاقة واحد إلى كثير، لتعبر العلاقة عن الطلبيات التي قام بها العملاء وقيمة كل طلبية وتاريخها. يمكن أن تكون هيكلية الجدول Orders كالتالي: Order_ID Customer_ID Order_Date Order_Value 997 101 1/5/2017 100 998 102 21/4/2016 150 999 101 21/4/2015 1500 تسمح هذه العلاقة للعميل بأن يطلُب طلبيةً أو أكثر، ويمكن ألا تكون له أية طلبية. ولكنّ كل طلبية في الجدول Orders ستكون تابعة لعميل واحد. ونستطيع تمثيل هذه العلاقة بالشكل التالي: علاقة كثير إلى كثير في علاقة كثير إلى واحد، تكون العلاقة مبنية على أن يكون أحد أطرافها “واحدًا”، مثل طالب واحد، عميل واحد، طلبية واحدة، وفي الطرف الثاني “كثير”. نحتاج أحيانا أن يكون طرفا العلاقة كثيرين. فمثلا، قد تكون لدينا طلبية تحتوي أكثر من عنصر، ونفس العنصر يكون متواجدًا في أكثر من طلبية. في هذه الحالة نحتاج لوجود جدول إضافي لبناء العلاقة، فمثلا تكون هيكلية جدول Orders كالتالي: Order_ID Customer_ID Order_Date Order_Value 997 101 1/5/2017 100 998 102 21/4/2016 150 999 101 21/4/2015 1500 وهيكلية جدول Items كالتالي: Item_Id Item_Name Item_Description 201 Hard Disk 1 1 Tera SSD Hard 202 Mouse Microsoft Optical Mouse 203 LCD 42 42” LCD نستطيع بناء علاقة كثير إلى كثير بين الجدولين السابقين بإضافة جدول ثالث يحلّ مكان الرابط وغرضه الوحيد هو بناء هذا النوع من العلاقات. نطلق عليه مثلا الاسم Orders_Items، ويكون بالهيكلية التالية: Order_Id Item_Id 997 201 997 202 999 201 999 202 999 203 998 203 يمثّل الشكل التالي علاقة كثير إلى كثير كما تظهر في الجدول Orders_Items: علاقة المرجعية الذاتية يُبنى هذا النوع من العلاقات عندما نريد أن نبني علاقة بين جدول ونفس الجدول، وأوضح مثال على هذا النوع من العلاقات هو جدول الموظفين الذي يحتوي على عمود رقم الموظف المسؤول، حيث يمكن ربط كل موظف بموظف آخر (مدير أو مسؤول) من نفس الجدول. فمثلا، لو كان لدينا جدول باسم Employees خاص بحفظ بيانات الموظفين، ستكون هيكليته على النحو التالي لتطبيق علاقة مرجعية ذاتية عليه: Employee_ID Employee_Name Manager_Id 100 Ibrahim Elbouhissi 101 Khaled Saber 100 102 Yasmeen Hadi 100 103 Duaa Yousef 101 104 Sami Saber بعلاقة المرجعية الذاتية، من الممكن أن يكون للموظف مسؤولًا أو لا يكون، ومن الممكن أن يكون الموظف مسؤولا عن موظف أو أكثر، ويمكن تمثيل العلاقة بالشكل التالي.
×
×
  • أضف...