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

عبد اللطيف ايمش

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

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

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

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

    63

كل منشورات العضو عبد اللطيف ايمش

  1. يبدو نوع البيانات tuple في بايثون كما يلي: coral = ('blue coral', 'staghorn coral', 'pillar coral', 'elkhorn coral') tuple (صف وتُجمَع إلى صفوف) هي بنية بيانات تُمثِّل سلسلة مرتبة من العناصر غير القابلة للتبديل، وبالتالي لا يمكن تعديل القيم الموجودة فيها. يستعمل نوع البيانات tuple لتجميع البيانات، فكل عنصر أو قيمة داخل tuple تُشكِّل جزءًا منه. توضع القيم داخل نوع البيانات tuple بين قوسين ( ) ويُفصَل بينها بفاصلة ,، وتبدو القيم الفارغة كما يلي coral = ()‎، لكن إذا احتوى نوع البيانات tuple على قيم –حتى لو كانت قيمةً واحدةً فقط– فيجب وضع فاصلة فيه مثل coral = ('blue coral',). إذا استخدمنا الدالة print()‎ على tuple، فسنحصل على الناتج الآتي الذي يُبيّن أنَّ القيمة الناتجة ستوضع بين قوسين: print(coral) ('blue coral', 'staghorn coral', 'pillar coral', 'elkhorn coral') عند التفكير بنوع tuple وغيره من بنى البيانات التي تُعبَر من أنوع «المجموعات» (collections)، فمن المفيد أن تضع ببالك مختلف المجموعات الموجودة في حاسوبك: تشكيلة الملفات الموجودة عندك، وقوائم التشغيل للموسيقى، والمفضلة الموجودة في متصفحك، ورسائل بريدك الإلكتروني، ومجموعة مقاطع الفيديو التي تستطيع الوصول إليها من التلفاز، والكثير. نوع tuple شبيه بالقوائم (lists)، لكن القيم الموجودة فيه لا يمكن تعديلها، وبسبب ذلك، فأنت تخبر الآخرين أنَّك لا تريد إجراء أيّة تعديلات على هذه السلسلة من القيم عندما تستعمل tuple في شيفرتك. إضافةً إلى ما سبق، ولعدم القدرة على تعديل القيم، فسيكون أداء برنامجك أفضل، حيث ستُنفَّذ الشيفرة بشكل أسرع إذا استعملتَ tuple بدلًا من القوائم (lists). فهرسة نوع البيانات tuple يمكن الوصول إلى كل عنصر من عناصر tuple بمفرده لأنَّه سلسلة مرتبة من العناصر، وذلك عبر الفهرسة. وكل عنصر يرتبط برقم فهرس، الذي هو عدد صحيح يبدأ من الفهرس 0. لمثال coral السابق، ستبدو الفهارس والقيم المرتبطة بها كالآتي: ‘blue coral’ ‘staghorn coral’ ‘pillar coral’ ‘elkhorn coral’ 0 1 2 3 العنصر الأول الذي يُمثِّل السلسلة النصية 'blue coral' تبدأ بالفهرس 0، وتنتهي القائمة بالفهرس رقم 3 المرتبط بالقيمة 'elkhorn coral'. ولأن كل عنصر من عناصر tuple له رقم فهرس مرتبط به، فسنتمكن من الوصول إلى عناصره فرادى. يمكننا الآن الوصول إلى عنصر معيّن في tuple عبر استخدام رقم الفهرس المرتبط به. print(coral[2]) pillar coral تتراوح قيم الفهارس في المتغير coral من 0 إلى 3 كما هو ظاهر في الجدول السابق، لذا يمكننا استدعاء العناصر الموجودة فيه فرادى كما يلي: coral[0] coral[1] coral[2] coral[3] إذا حاولنا استدعاء المتغير coral مع رقم فهرس أكبر من 3، فستظهر رسالة خطأ تشير إلى أنَّ الفهرس خارج المجال: print(coral[22]) IndexError: tuple index out of range إضافةً إلى أرقام الفهارس الموجبة، يمكننا أيضًا الوصول إلى الفهارس باستخدام رقم فهرس سالب، وذلك بالعد بدءًا من نهاية قائمة العناصر وسيرتبط آخر عنصر بالفهرس ‎-1، وهذا مفيدٌ جدًا إذا كان لديك متغير من النوع tuple وكان يحتوي عناصر كثيرة وأردتَ الوصول إلى أحد عناصره انطلاقًا من النهاية. ففي مثالنا السابق عن coral، إذا أردنا استخدام الفهارس السالبة فالناتج كالآتي: ‘elkhorn coral’ ‘pillar coral’ ‘staghorn coral’ ‘blue coral’ -1 -2 -3 -4 إذا أردنا طباعة العنصر 'blue coral' باستخدام الفهارس السالبة، فستبدو التعليمة كما يلي: print(coral[-4]) blue coral يمكننا إضافة العناصر النصية الموجودة في tuple إلى السلاسل النصية الأخرى باستخدام المعامل +: print('This reef is made up of ' + coral[1]) This reef is made up of staghorn coral استطعنا في المثال السابق إضافة عنصر موجود في الفهرس 1 مع السلسلة النصية 'This reef is made up of '، ويمكننا أيضًا استخدام المعامل + لإضافة بنيتَي tuple معًا. الخلاصة: يمكننا الوصول إلى كل عنصر من عناصر tuple على حدة باستخدام أرقام الفهارس (الموجبة أو السالبة) المرتبطة بها. تقطيع قيم tuple يمكننا استخدام الفهارس للوصول إلى عدِّة عناصر من tuple، أما التقطيع فيسمح لنا بالوصول إلى عدِّة قيم عبر إنشاء مجال من أرقام الفهارس المفصولة بنقطتين رأسيتين [x:y]. لنقل أننا نريد عرض العناصر الموجودة في وسط المتغير coral، يمكننا فعل ذلك بإنشاء قطعة جديدة: print(coral[1:3]) ('staghorn coral', 'pillar coral') عند إنشاء قطعة جديدة –كما في المثال السابق– فيمثِّل أوّل رقم مكان بدأ القطعة (متضمنةً هذا الفهرس)، ورقم الفهرس الثاني هو مكان نهاية القطعة (دون تضمين هذا الفهرس بالقطعة)، وهذا هو السبب وراء عرض المثال السابق للقيم المرتبطة بالعناصر الموجودة في الفهرسين 1 و 2. إذا أردتَ تضمين إحدى نهايتَي القائمة، فيمكنك حذف أحد الأرقام في التعبير tuple[x:y]، فمثلًا، لنقل أننا نريد عرض أوّل ثلاثة عناصر من coral، والتي هي 'blue coral' و 'staghorn coral' و 'pillar coral'، فيمكننا فعل ذلك كالآتي: print(coral[:3]) ('blue coral', 'staghorn coral', 'pillar coral') المثال السابق عرض العناصر من بداية القائمة وتوقف قبل العنصر ذي الفهرس 3. لتضمين كل العناصر الموجودة في نهاية tuple، فيمكننا عكس التعبير السابق: print(coral[1:]) ('staghorn coral', 'pillar coral', 'elkhorn coral') يمكننا استخدام الفهارس السالبة أيضًا عند التقطيع، كما فعلنا مع أرقام الفهارس الموجبة: print(coral[-3:-1]) print(coral[-2:]) ('staghorn coral', 'pillar coral') ('pillar coral', 'elkhorn coral') هنالك معاملٌ إضافيٌ يمكننا استعماله ويسمى «الخطوة»، ويُشير إلى عدد العناصر التي يجب تجاوزها بعد الحصول على أوّل عنصر من القائمة. حذفنا في جميع أمثلتنا السابقة معامل الخطوة، حيث القيمة الافتراضية له في بايثون هي 1، لذا سنحصل على جميع العناصر الموجودة بين الفهرسَين المذكورين. شكل هذا التعبير العام هو tuple[x:y:z]، إذ يُشير المعامل z إلى الخطوة. لنُنشِئ قائمةً أكبر، ثم نقسِّمها، ونعطيها القيمة 2 كخطوة: numbers = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) print(numbers[1:11:2]) (1, 3, 5, 7, 9) التعبير numbers[1:11:2] سيطبع القيم الموجودة بين رقمين الفهرسين 1 (بما في ذلك العنصر المرتبط بالفهرس 1) و 11 (دون تضمين ذلك العنصر)، ومن ثم ستخبر قيمةُ الخطوة 2 البرنامجَ أنَّ يتخطى عنصرًا بين كل عنصرين. يمكننا حذف أوّل معاملين واستخدام معامل الخطوة بمفرده بتعبيرٍ برمجيٍ من الشكل tuple[::z]: print(numbers[::3]) (0, 3, 6, 9, 12) طبعنا في المثال السابق عناصر numbers بعد ضبط قيمة الخطوة إلى 3، وبالتالي سيتم تخطي عنصرين. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 الخلاصة: تقطيع tuples باستخدام أرقام الفهارس الموجبة والسالبة واستعمال معامل الخطوة يسمح لنا بالتحكم بالناتج الذي نريد عرضه. إضافة بنى tuple إلى بعضها يمكن أن نُضيف بنى tuple إلى بعضها أو أن «نضربها» (multiply)، تتم عملية الإضافة باستخدام المعامل +، أما عملية الضرب فباستخدام المعامل *. يمكن أن يُستخدَم المعامل + لإضافة بنيتَي tuple أو أكثر إلى بعضها بعضًا. يمكننا إسناد القيم الموجودة في بنيتَي tuple إلى بنية جديدة: coral = ('blue coral', 'staghorn coral', 'pillar coral', 'elkhorn coral') kelp = ('wakame', 'alaria', 'deep-sea tangle', 'macrocystis') coral_kelp = (coral + kelp) print(coral_kelp) الناتج: ('blue coral', 'staghorn coral', 'pillar coral', 'elkhorn coral', 'wakame', 'alaria', 'deep-sea tangle', 'macrocystis') وصحيحٌ أنَّ المعامل + يمكنه إضافة بنى tuple إلى بعضها، لكن يمكن أن يستعمل لإنشاء بنية tuple جديدة ناتجة عن جمع بنى أخرى، لكن لا يمكنه تعديل بنية tuple موجودة مسبقًا. أما المعامل * فيمكن استخدامه لضرب بنى tuple، فربما تريد إنشاء نسخ من الملفات الموجودة في أحد المجلدات إلى الخادوم أو مشاركة قائمة بالمقطوعات الموسيقية التي تحبها مع أصدقائك، ففي هذه الحالات سترغب بمضاعفة مجموعات من البيانات (أو «ضربها»). لنضرب البنية coral بالرقم 2 والبنية kelp بالرقم 3، ثم نسندها إلى بنى tuple جديدة: multiplied_coral = coral * 2 multiplied_kelp = kelp * 3 print(multiplied_coral) print(multiplied_kelp) الناتج: ('blue coral', 'staghorn coral', 'pillar coral', 'elkhorn coral', 'blue coral', 'staghorn coral', 'pillar coral', 'elkhorn coral') ('wakame', 'alaria', 'deep-sea tangle', 'macrocystis', 'wakame', 'alaria', 'deep-sea tangle', 'macrocystis', 'wakame', 'alaria', 'deep-sea tangle', 'macrocystis') يمكننا باستخدام المعامل * أن نُكرِّر (أو نُضاعِف) بنى tuple بأي عدد من المرات نشاء، مما سينُشِئ بنى tuple جديدة اعتمادًا على محتوى البنى الأصلية. الخلاصة هي أنَّ بنى tuple يمكن إضافتها إلى بعضها أو ضربها لتشكيل بنى tuple جديدة عبر استخدام المعاملَين + و *. دوال التعامل مع tuple هنالك دوال مُضمَّنة في لغة بايثون للتعامل مع بنى tuple، لننظر إلى بعضها. len()‎ وكما في السلاسل النصية والقوائم، يمكننا حساب طول (أو عدد عناصر) بنية tuple باستخدام الدالة len()‎ حيث نُمرِّر إليها بنية tuple كمعامل (parameter)، كما يلي: len(coral) هذه الدالة مفيدة إذا أردنا أن نَضمَن أنَّ لبنية tuple عدد عناصر معيّن، فمثلًا يمكننا الاستفادة من ذلك بمقارنة بنيتين مع بعضهما. إذا أردنا طباعة عدد عناصر kelp و numbers، فسيظهر الناتج الآتي: print(len(kelp)) print(len(numbers)) الناتج: 4 13 الناتج أعلاه يشير إلى أنَّ للبنية kelp أربعة عناصر: kelp = ('wakame', 'alaria', 'deep-sea tangle', 'macrocystis') أما البنية numbers فتملك ثلاثة عشر عنصرًا: numbers = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) وصحيحٌ أنَّ هذه الأمثلة عناصرها قليلة نسبيًا، إلا أنَّ الدالة len()‎ تستطيع أن تخبرنا بعدد عناصر بنى tuple الكبيرة. الدالتان max()‎ و min()‎ عندما نتعامل مع بنى tuple مكوَّنة من عناصر رقمية (بما فيها الأعداد الصحيحة والأرقام ذات الفاصلة العشرية)، فيمكننا استخدام الدالتين max()‎ و min()‎ للعثور على أكبر وأصغر قيمة موجودة في بنية tuple معيّنة. تسمح لنا هاتان الدالتان باستخراج معلومات تخص البيانات القابلة للإحصاء، مثل نتائج الامتحانات أو درجات الحرارة أو أسعار المنتجات …إلخ. لننظر إلى بنية tuple مكونة من أعداد عشرية: more_numbers = (11.13, 34.87, 95.59, 82.49, 42.73, 11.12, 95.57) للحصول على القيمة العظمى من بين القيم الآتية فعلينا تمرير بنية tuple إلى الدالة max()‎ كما في max(more_numbers)، وسنستخدم الدالة print()‎ لعرض الناتج: print(max(more_numbers)) 95.59 أعادت الدالة max()‎ أعلى قيمة في بنية more_numbers. وبشكلٍ شبيهٍ بما سبق نستخدم الدالة min()‎: print(min(more_numbers)) 11.12 أُعيدَ هنا أصغر رقم عشري موجودة في البنية. يمكن الاستفادة من الدالتين max()‎ و min()‎ كثيرًا للتعامل مع بنى tuple التي تحتوي الكثير من القيم. كيف تختلف بنى tuple عن القوائم (list) الفرق الرئيسي بين tuple و list هو عدم القدرة على تعديلها، وهذا يعني أنَّنا لا نستطيع إضافة أو حذف أو استبدال العناصر داخل بنية tuple. لكن يمكننا إضافة بنيتَي tuple أو أكثر إلى بعضها بعضًا لتشكيل بنية جديدة كما رأينا في أحد الأقسام السابقة. لتكن لدينا البنية coral الآتية: coral = ('blue coral', 'staghorn coral', 'pillar coral', 'elkhorn coral') لنقل أننا نريد استبدال العنصر 'blue coral' ووضع العنصر 'black coral' بدلًا منه. فلو حاولنا تغيير بنية tuple بنفس الطريقة التي نُعدِّل فيها القوائم بكتابة: coral[0] = 'black coral' فستظهر رسالة خطأ كالآتية: TypeError: 'tuple' object does not support item assignment وذلك بسبب عدم إمكانية تعديل بنى tuple. إذا أنشأنا بنية tuple ثم قررنا أنَّ ما نحتاج له هو بنية list، فيمكننا تحويلها إلى قائمة list، وذلك بالدالة list()‎: list(coral) أصبحت بنية coral قائمةً الآن: coral = ['blue coral', 'staghorn coral', 'pillar coral'] يمكننا أن نلاحظ أنَّ بنية tuple تحوَّلتَ إلى قائمة list لأنَّ الأقواس المحيطة بالقيم أصبح مربعة الشكل. وبشكلٍ شبيهٍ بما سبق، نستطيع تحويل القوائم من النوع list إلى tuple باستخدام الدالة tuple()‎. الخلاصة نوع البيانات tuple هو مجموعةٌ من البيانات المتسلسلة التي لا يمكن تعديلها، ويوفِّر تحسينًا في أداء برامجك لأنه أسرع معالجةً من القوائم في بايثون. وعندما يراجع الآخرون شيفرتك فسيعلمون من استخدامك لبنى tuple أنك لا تريد تعديل هذه القيم. شرحنا في هذا الدرس الميزات الأساسية لبنى tuple بما في ذلك الفهارس وتقطيعها وتجميعها، وعرضنا بعض الدوال المُضمَّنة المتوافرة لهذا النوع من البيانات. هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة –وبتصرّف– للمقال Understanding Tuples in Python 3 لصاحبته Lisa Tagliaferri اقرأ أيضًا المقالة التالية: فهم القواميس في بايثون 3 المقالة السابقة: فهم كيفية استعمال List Comprehensions في بايثون 3 المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون دليلك الشامل إلى أنواع البيانات
  2. توفر List Comprehensions طريقةً مختصرةً لإنشاء القوائم بناءً على قوائم موجودة مسبقًا. فعند استخدام list comprehensions فيمكن بناء القوائم باستخدام أيّ نوع من البيانات المتسلسلة التي يمكن الدوران على عناصرها عبر حلقات التكرار، بما في ذلك السلاسل النصية و tuples. من ناحية التركيب اللغوي، تحتوي list comprehensions على عنصر يمكن المرور عليه ضمن تعبيرٍ متبوعٍ بحلقة for. ويمكن أن يُتبَع ما سبق بتعابير for أو if إضافية، لذا سيساعدك الفهم العميق لحلقات for والعبارات الشرطية في التعامل مع list comprehensions. توفِّر list comprehensions طريقةً مختلفةً لإنشاء القوائم وغيرها من أنواع البيانات المتسلسلة. وعلى الرغم من إمكانية استخدام الطرائق الأخرى للدوران، مثل حلقات for، لإنشاء القوائم، لكن من المفضَّل استعمال list comprehensions لأنها تقلِّل عدد الأسطر الموجودة في برنامجك. List Comprehensions يمكن بناء list comprehensions في بايثون كالآتي: list_variable = [x for x in iterable] ستُسنَد القائمة، أو أي نوع من البيانات يمكن المرور على عناصره، إلى متغير. المتغيرات الإضافية –التي تُشير إلى عناصر موجودة ضمن نوع البيانات الذي يمكن المرور على عناصره– تُبنى حول عبارة for. والكلمة المحجوزة in تستعمل بنفس استعمالها في حلقات for وذلك لمرور على عناصر iterable. لننظر إلى مثالٍ يُنشِئ قائمةً مبنيةً على سلسلةٍ نصية: shark_letters = [letter for letter in 'shark'] print(shark_letters) أسندنا في المثال السابق قائمةً جديدةً إلى المتغير shark_letters، واستعملنا المتغير letter للإشارة إلى العناصر الموجودة ضمن السلسلة النصية 'shark'. استعملنا بعد ذلك الدالة print()‎ لكي نتأكد من القائمة الناتجة والمُسنَدة إلى المتغير shark_letters، وحصلنا على الناتج الآتي: ['s', 'h', 'a', 'r', 'k'] القائمة التي أنشأناها باستخدام list comprehensions تتألف من العناصر التي تكوِّن السلسلة النصية 'shark'، وهي كل حرف في الكلمة shark. يمكن إعادة كتابة تعابير list comprehensions كحلقات for، لكن لاحظ أنَّك لا تستطيع إعادة كتابة كل حلقة for بصيغة list comprehensions. لنعد كتابة المثال السابق الذي أنشأنا فيه القائمة shark_letters باستخدام حلقة for، وهذا سيساعدنا في فهم كيف تعمل list comprehensions عملها: shark_letters = [] for letter in 'shark': shark_letters.append(letter) print(shark_letters) عند إنشائنا للقائمة عبر استخدام الحلقة for، فيجب تهيئة المتغير الذي سنُسنِد العناصر إليه كقائمة فارغة، وهذا ما فعلناه في أوّل سطر من الشيفرة السابقة. ثم بدأت حلقة for بالدوران على عناصر السلسلة النصية 'shark' مستعملةً المتغير letter للإشارة إلى قيمة العنصر الحالي. ومن ثم أضفنا كل عنصر في السلسلة النصية إلى القائمة ضمن حلقة for وذلك باستخدام الدالة list.append(x). الناتج من حلقة for السابقة يماثل ناتج list comprehension في المثال أعلاه: ['s', 'h', 'a', 'r', 'k'] الخلاصة: يمكن إعادة كتابة List comprehensions كحلقات for، لكن بعض حلقات for يمكن إعادة كتابتها لتصبح List comprehensions لتقليل كمية الشيفرات المكتوبة. استخدام التعابير الشرطية مع List Comprehensions يمكن استخدام التعابير الشرطية في list comprehension لتعديل القوائم أو أنواع البيانات المتسلسلة الأخرى عند إنشاء قوائم جديدة. لننظر إلى مثالٍ عن استخدام العبارة الشرطية if في تعبير list comprehension: fish_tuple = ('blowfish', 'clownfish', 'catfish', 'octopus') fish_list = [fish for fish in fish_tuple if fish != 'octopus'] print(fish_list) استعملنا المتغير fish_tuple الذي من نوع البيانات tuple كأساس للقائمة الجديدة التي سنُنشِئها التي تسمى fish_list. استعملنا for و in كما في القسم السابق، لكننا أضفنا هنا العبارة الشرطية if. ستؤدي العبارة الشرطية if إلى إضافة العناصر غير المساوية للسلسلة النصية 'octopus'، لذا ستحتوي القائمة الجديدة على العناصر الموجودة في بنية tuple والتي لا تُطابِق الكلمة 'octopus'. عند تشغيل البرنامج السابق فسنلاحظ أنَّ القائمة fish_list تحتوي على نفس العناصر التي كانت موجودة في fish_tuple لكن مع حذف العنصر 'octopus': ['blowfish', 'clownfish', 'catfish'] أي أصبحت القائمة الجديدة تحتوي على بنية tuple الأصلية لكن ما عدا السلسلة النصية التي استثنيناها عبر التعبير الشرطي. سنُنشِئ مثالًا آخر يستعمل المعاملات الرياضية والأرقام الصحيحة والدالة range()‎: number_list = [x ** 2 for x in range(10) if x % 2 == 0] print(number_list) القائمة التي ستُنشَأ باسم number_list ستحتوي على مربع جميع القيم الموجودة من المجال 0 إلى 9 لكن إذا كان الرقم قابلًا للقسمة على 2. وستبدو المخرجات كالآتية: [0, 4, 16, 36, 64] دعنا نُفصِّل ما الذي يفعله تعبير list comprehension السابق، ودعنا نفكِّر بالذي سيظهر إذا استعملنا التعبير x for x in range(10) فقط. يجب أن يبدو برنامجنا الصغير كالآتي: number_list = [x for x in range(10)] print(number_list) الناتج: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] لنضف العبارة الشرطية الآن: number_list = [x for x in range(10) if x % 2 == 0] print(number_list) الناتج: [0, 2, 4, 6, 8] أدّت العبارة الشرطية if إلى قبول العناصر القابلة للقسمة على 2 فقط وإضافتها إلى القائمة، مما يؤدي إلى حذف جميع الأرقام الفردية. يمكننا الآن استخدام معامل رياضي لتربيع قيمة المتغير x: number_list = [x ** 2 for x in range(10) if x % 2 == 0] print(number_list) أي ستُربَّع قيم القائمة السابقة ‎[0, 2, 4, 6, 8] وسيُخرَج الناتج الآتي: [0, 4, 16, 36, 64] يمكننا أيضًا استعمال ما يشبه عبارات if المتشعبة في تعابير list comprehension: number_list = [x for x in range(100) if x % 3 == 0 if x % 5 == 0] print(number_list) سيتم التحقق أولًا أنَّ المتغير x قابل للقسمة على الرقم 3، ثم سنتحقق إن كان المتغير x قابل للقسمة على الرقم 5، وإذا حقَّق المتغير x الشرطين السابقين فسيُضاف إلى القائمة، وسيُظهَر في الناتج: [0, 15, 30, 45, 60, 75, 90] الخلاصة: يمكن استخدام عبارات if الشرطية لتحديد ما هي العناصر التي نريد إضافتها إلى القائمة الجديدة. حلقات التكرار المتشعبة في تعابير List Comprehension يمكن استعمال حلقات التكرار المتشعبة لإجراء عدِّة عمليات دوران متداخلة في برامجنا. سننظر في هذا القسم إلى حلقة for متشعبة وسنحاول تحويلها إلى تعبير list comprehension. هذه الشيفرة ستُنشِئ قائمةً جديدةً بالدوران على قائمتين وبإجراء عمليات رياضية عليها: my_list = [] for x in [20, 40, 60]: for y in [2, 4, 6]: my_list.append(x * y) print(my_list) سنحصل على الناتج الآتي عند تشغيل البرنامج: [40, 80, 120, 80, 160, 240, 120, 240, 360] الشيفرة السابقة تضرب العناصر الموجودة في أوّل قائمة بالعناصر الموجودة في ثاني قائمة في كل دورة. لتحويل ما سبق إلى تعبير list comprehension، وذلك باختصار السطرين الموجودين في الشيفرة السابقة وتحويلهما إلى سطرٍ وحيدٍ، الذي يبدأ بإجراء العملية x*y، ثم ستلي هذه العملية حلقة for الخارجية، ثم يليها حلقة for الداخلية؛ وسنضيف تعبير print()‎ للتأكد أنَّ ناتج القائمة الجديدة يُطابِق ناتج البرنامج الذي فيه حلقتين متداخلتين: my_list = [x * y for x in [20, 40, 60] for y in [2, 4, 6]] print(my_list) الناتج: [40, 80, 120, 80, 160, 240, 120, 240, 360] أدى استعمال تعبير list comprehension في المثال السابق إلى تبسيط حلقتَي for لتصبحا سطرًا وحيدًا، لكن مع إنشاء نفس القائمة والتي ستُسنَد إلى المتغير my_list. توفِّر لنا تعابير list comprehension طريقةً بسيطةً لإنشاء القوائم، مما يسمح لنا باختصار عدِّة أسطر إلى سطرٍ وحيد. لكن من المهم أن تبقي في ذهنك أنَّ سهولة قراءة الشيفرة لها الأولوية دومًا، لذا إذا أصبحتَ تعابير list comprehension طويلةً جدًا ومعقدة، فمن الأفضل حينها تحويلها إلى حلقات تكرار عادية. الخلاصة تسمح تعابير list comprehension لنا بتحويل قائمة أو أي نوع من البيانات المتسلسلة إلى سلسلةٍ جديدة، ولها شكلٌ بسيطٌ يُقلِّل عدد الأسطر التي نكتبها. تتبع تعابير list comprehension شكلًا رياضيًا معيّنًا، لذا قد يجدها المبرمجون أولو الخلفية الرياضية سهلة الفهم. وصحيحٌ أنَّ تعابير list comprehension تختصر الشيفرةـ لكن من المهم جعل سهولة قراءة الشيفرة من أولوياتنا، وحاول تجنّب الأسطر الطويلة لتسهيل قراءة الشيفرة. هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة –وبتصرّف– للمقال Understanding List Comprehensions in Python 3 لصاحبته Lisa Tagliaferri اقرأ أيضًا المقالة التالية: فهم نوع البيانات Tuples في بايثون 3 المقالة السابقة: كيفية استخدام توابع القوائم في بايثون 3 المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون
  3. الدالة str.format()‎ المتوافرة للسلاسل النصية تسمح لك باستبدال المتغيرات وتنسيق القيم. مما يمنحك القدرة على تجميع العناصر مع بعضها عبر إدخالها في مواضع معينة. سيشرح لك هذا الدرس أشهر الاستخدامات لآلية تنسيق السلاسل النصية في بايثون، والتي ستساعدك في جعل شيفرتك وبرنامجك أسهل قراءةً واستخدامًا. استخدام «المُنسِّقات» تعمل المُنسِّقات (formatters) بوضع حقول قابلة للاستبدال تُعرَّف عبر وضع قوسين معقوفين {} في السلسلة النصية ثم استدعاء الدالة str.format()‎، إذ ستُمرَّر القيمة التي تريد وضعها ضمن السلسلة النصية إلى الدالة format()‎ وستوضع هذه القيمة في نفس مكان الحقل القابل للاستبدال الموجود في السلسلة الأصلية عندما تُشغِّل برنامجك. لنطبع سلسلةً نصيةً تستخدم «مُنسِّقًا» (formatter): print("Sammy has {} balloons.".format(5)) الناتج: Sammy has 5 balloons. أنشأنا في المثال السابق سلسلةً نصيةً تحتوي على قوسين معقوفين: "Sammy has {} balloons." ثم أضفنا الدالة str.format()‎ ومررنا إليها القيمة الرقمية 5 وهذا يعني أنَّ القيمة 5 ستوضع مكان القوسين المعقوفين: Sammy has 5 balloons. يمكننا أيضًا إسناد السلسلة النصية الأصلية التي تحتوي مُنسِّقًا إلى متغير: open_string = "Sammy loves {}." print(open_string.format("open source")) الناتج: Sammy loves open source. أضفنا في المثال السابق السلسلة النصية "open source" إلى سلسلةٍ نصيةٍ أكبر باستبدالها للقوسين المعقوفين الموجودَين في السلسلة الأصلية. تسمح لك المُنسِّقات في بايثون باستخدام الأقواس المعقوفة لحجز أماكن للقيم التي ستمررها مستقبلًا عبر الدالة str.format()‎. استخدام المُنسِّقات لحجز أكثر من مكان يمكنك استخدام أكثر من زوج من الأقواس المعقوفة عند استعمال المُنسِّقات؛ فيمكنك أن تضيف سلسلةً نصيةً أخرى إلى المثال السابق وذلك بإضافة زوج آخر من الأقواس المعقوفة وتمرير قيمة ثانية إلى الدالة كما يلي: new_open_string = "Sammy loves {} {}." # {} مكانين محجوزين عبر print(new_open_string.format("open-source", "software")) # تمرير قيمتين إلى الدالة مفصولٌ بينهما بفاصلة الناتج: Sammy loves open-source software. أضفنا زوجًا آخر من الأقواس المعقوفة إلى السلسلة النصية للسماح بوضع قيمة ثانية، ثم مررنا سلسلتين نصيتين إلى الدالة str.format()‎ مفصولٌ بينهما بفاصلة. سنضيف عمليات استبدال أخرى عبر اتباع نفس الآلية التي شرحناها أعلاه: sammy_string = "Sammy loves {} {}, and has {} {}." print(sammy_string.format("open-source", "software", 5, "balloons")) الناتج: Sammy loves open-source software, and has 5 balloons. إعادة ترتيب المنسقات عبر المعاملات الموضعية عندما نترك الأقواس المعقوفة دون معاملات (parameters) ممررة إليها، فستضع بايثون القيم المُمرَّرة إلى الدالة str.format()‎ بالترتيب. هذا تعبيرٌ فيه زوجين من الأقواس المعقوفة يوضع مكانهما سلسلتان نصيتان شبيهٌ بما رأيناه سابقًا في هذا الدرس: print("Sammy the {} has a pet {}!".format("shark", "pilot fish")) الناتج: Sammy the shark has a pet pilot fish! اُستبدِل أوّل زوجٍ من الأقواس المعقوفة ووضعت مكانه القيمة "shark"، ووضعت القيمة "pilot fish" مكان الزوج الثاني من الأقواس. القيم التي مررناها إلى الدالة str.format()‎ كانت بهذا الترتيب: ("shark", "pilot fish") إن سبق لك دراسة أنواع البيانات المُختلفة على بايثون فقد تلاحظ أنَّ القيمة السابقة هي من النوع tuple، ويمكن الوصول إلى كل قيمة موجودة فيها عبر فهرسٍ رقميٍ تابعٍ لها، والذي يبدأ من الفهرس 0. يمكننا تمرير أرقام الفهارس إلى داخل القوسين المعقوفين: print("Sammy the {0} has a pet {1}!".format("shark", "pilot fish")) سنحصل بعد تنفيذ المثال السابق على نفس الناتج التي ظهر دون تحديد أرقام الفهارس يدويًا، وذلك لأننا استدعينا القيم بالترتيب: Sammy the shark has a pet pilot fish! لكن إن عكسنا أرقام الفهارس في معاملات الأقواس المعقوفة فسنتمكن من عكس ترتيب القيم المُمرَّرة إلى السلسلة النصية الأصلية: print("Sammy the {1} has a pet {0}!".format("shark", "pilot fish")) الناتج: Sammy the pilot fish has a pet shark! لكن إن حاولت استخدام الفهرس ذي الرقم 2 ولم تكن لديك إلا قيمتين موجودتين في الفهرسين 0 و 1، فأنت تستدعي قيمةً خارج المجال المسموح، ولهذا السبب ستظهر رسالة خطأ: print("Sammy the {2} has a pet {1}!".format("shark", "pilot fish")) الناتج: IndexError: tuple index out of range تُشير رسالة الخطأ إلى وجود قيمتين فقط ومكانهما هو 0 و1، لذا كان الفهرس 2 غير مرتبطٍ بقيمةٍ وكان خارج المجال المسموح. لنضف الآن مكانين محجوزين إلى السلسلة النصية ولنمرر بضع قيم إلى الدالة str.format()‎ لكي نفهم آلية إعادة الترتيب فهمًا تامًا. هذه هي السلسلة النصية الجديدة التي فيها أربعة أزواج من الأقواس المعقوفة: print("Sammy is a {}, {}, and {} {}!".format("happy", "smiling", "blue", "shark")) الناتج: Sammy is a happy, smiling and blue shark! ستوضع القيم المُمرَّرة إلى الدالة str.format()‎ بنفس ترتيب ورودها في حال لم نستعمل المعاملات داخل الأقواس المعقوفة. تملك السلاسل النصية المُمرَّرة إلى الدالة str.format()‎ الفهارس الآتية المرتبطة بها: “happy” “smiling” “blue” “shark” 0 1 2 3 لنستخدم الآن أرقام الفهارس لتغيير ترتيب ظهور القيم المرتبطة بها في السلسلة النصية: print("Sammy is a {3}, {2}, and {1} {0}!".format("happy", "smiling", "blue", "shark")) الناتج: Sammy is a shark, blue, and smiling happy! ولمّا كنّا قد بدأنا بالفهرس ذي الرقم 3، فستظهر القيمة "shark" أولًا. أي أنَّ وضع رقم الفهرس بين القوسين كمعامل سيؤدي إلى تغيير ترتيب ظهور القيم في السلسلة النصية الأصلية. نستطيع -بالإضافة إلى المعاملات الموضعية الرقمية- أن نربط بين القيم وبين كلمات محجوزة مخصصة ومن ثم نستدعيها عبر وضع الكلمة المحجوزة بين القوسين المعقوفين كما يلي: print("Sammy the {0} {1} a {pr}.".format("shark", "made", pr = "pull request")) الناتج: Sammy the shark made a pull request. أظهر المثال السابق استخدام كلمة محجوزة كوسيط بالإضافة إلى المعاملات الموضعية، يمكنك استخدام الكلمة المحجوزة pr كوسيط بالإضافة إلى أرقام الفهارس، وتستطيع أيضًا إعادة ترتيب تلك الوسائط كيفما شئت: print("Sammy the {pr} {1} a {0}.".format("shark", "made", pr = "pull request")) الناتج: Sammy the pull request made a shark. استخدام المعاملات الموضعية والكلمات المحجوزة سيمنحنا تحكمًا أكبر بكيفية معالجة السلسلة النصية الأصلية عبر إعادة ترتيب القيم المُمرَّرة إليها. تحديد نوع القيمة يمكنك وضع معاملات أخرى ضمن القوسين المعقوفين، سنستخدم الصيغة الآتية {field_name:conversion} حيث field_name هو الفهرس الرقمي للوسيط الممرَّر إلى الدالة str.format()‎ والذي شرحناه تفصيليًا في القسم السابق، و conversion هو الرمز المستعمل للتحويل إلى نوع البيانات الذي تريده. «رمز التحويل» يعني رمزًا من حرفٍ وحيد الذي تستخدمه بايثون لمعرفة نوع القيمة المُراد «تنسيقها». الرموز التي سنستخدمها في أمثلتنا هي s للسلاسل النصية و d لإظهار الأرقام بنظام العد العشري (ذي الأساس 10) و f لإظهار الأعداد ذات الفاصلة. يمكنك قراءة المزيد من التفاصيل عن رموز التنسيق في بايثون 3 (وغير ذلك من المواضيع المرتبطة بهذا المجال) في التوثيق الرسمي. لننظر إلى مثالٍ نُمرِّر فيه رقمًا صحيحًا عبر الدالة format()‎ لكننا نريد إظهاره كعددٍ ذي فاصلة عبر رمز التحويل f: print("Sammy ate {0:f} percent of a {1}!".format(75, "pizza")) الناتج: Sammy ate 75.000000 percent of a pizza! وضعت القيمة -مكان أوّل ورود للصيغة {field_name:conversion}- كعددٍ ذي فاصلة، أما ثاني ورود للقوسين المعقوفين فكان بالصيغة {field_name}. لاحظ في المثال السابق وجود عدد كبير من الأرقام الظاهرة بعد الفاصلة العشرية، لكنك تستطيع تقليل عددها. فعندما نستخدم الرمز f للقيم ذات الفاصلة نستطيع أيضًا تحديد دقة القيمة الناتجة بتضمين رمز النقطة . متبوعًا بعدد الأرقام بعد الفاصلة التي نود عرضها. حتى لو أكل سامي 75.765367% من قطعة البيتزا فلن نحتاج إلى هذا القدر الكبير من الدقة، إذ يمكننا مثلًا أن نجعل عدد المنازل العشرية ثلاث منازل بعد الفاصلة بوضعنا ‎.3 قبل رمز التحويل f: print("Sammy ate {0:.3f} percent of a pizza!".format(75.765367)) الناتج: Sammy ate 75.765 percent of a pizza! أما إذا أردنا عرض منزلة عشرية وحيدة، فيمكننا إعادة كتابة السلسلة السابقة كالآتي: print("Sammy ate {0:.1f} percent of a pizza!".format(75.765367)) الناتج: Sammy ate 75.8 percent of a pizza! لاحظ كيف أدى تعديل دقة الأرقام العشرية إلى تقريب الرقم (وفق قواعد التقريب الاعتيادية). وصحيحٌ أننا عرضنا رقمًا دون منازل عشرية كعددٍ ذي فاصلة، إلا أننا إذا حاولنا تحويل عدد عشري إلى عدد صحيح باستخدام رمز التحويل d فسنحصل على خطأ: print("Sammy ate {0:.d} percent of a pizza!".format(75.765367)) الناتج: ValueError: Unknown format code 'd' for object of type 'float' إذا لم ترغب بعرض أيّة منازل عشرية، فيمكنك كتابة تعبير كالآتي: print("Sammy ate {0:.0f} percent of a pizza!".format(75.765367)) الناتج: Sammy ate 76 percent of a pizza! لن يؤدي ما سبق إلى تحويل العدد العشري إلى عددٍ صحيح، وإنما سيؤدي إلى تقليل عدد المنازل العشرية الظاهرة بعد الفاصلة. إضافة حواشي لمّا كانت الأماكن المحجوزة عبر القوسين المعقوفين هي حقول قابلة للاستبدال (أي ليست قيمًا فعليةً) فيمكنك إضافة حاشية (padding) أو إضافة فراغ حول العنصر بزيادة حجم الحقل عبر معاملات إضافية، قد تستفيد من هذا الأمر عندما تحتاج إلى تنظيم البيانات بصريًا. يمكننا إضافة حقلٍ بحجمٍ معيّن (مُقاسًا بعدد المحارف) بتحديد ذاك الحجم بعد النقطتين الرأسيتين : كما في المثال الآتي: print("Sammy has {0:4} red {1:16}!".format(5, "balloons")) الناتج: Sammy has 5 red balloons ! أعطينا في المثال السابق حقلًا بحجم 4 محارف للعدد 5، وأعطينا حقلًا بحجم 16 محرفًا للسلسلة النصية balloons (لأنها سلسلة طويلة نسبيًا). وكما رأينا من ناتج المثال السابق، يتم محاذاة السلاسل النصية افتراضيًا إلى اليسار والأعداد إلى اليمين، يمكنك أن تُغيّر من هذا بوضع رمز خاص للمحاذاة بعد النقطتين الرأسيتين مباشرةً. إذ سيؤدي الرمز > إلى محاذاة النص إلى يسار الحقل، أما الرمز ^ فسيوسِّط النص في الحقل، والرمز < سيؤدي إلى محاذاته إلى اليمين. لنجعل محاذاة العدد إلى اليسار ونوسِّط السلسلة النصية: print("Sammy has {0:<4} red {1:^16}!".format(5, "balloons")) الناتج: Sammy has 5 red balloons ! نلاحظ الآن أنَّ محاذاة العدد 5 إلى اليسار، مما يعطي مساحة فارغةً في الحقل قبل الكلمة red، وستظهر السلسلة النصية balloons في منتصف الحقل وتوجد مسافة فارغة على يمينها ويسارها. عندما نُنسِّق الحقل لنجعله أكبر من حجمه الطبيعي فستملأ بايثون الحقل افتراضيًا بالفراغات، إلا أننا نستطيع تغيير محرف الملء إلى محرفٍ آخر بوضعه مباشرةً بعد النقطتين الرأسيتين: print("{:*^20s}".format("Sammy")) الناتج: *******Sammy******** ستضع بايثون السلسلة النصية المُمرَّرة إلى الدالة str.format()‎ ذات الفهرس 0 مكان القوسين المعقوفين لأننا لم نطلب منها عكس ذلك، ومن ثم سنضع النقطتين الرأسيتين ثم سنُحدِّد أننا سنستعمل المحرف * بدلًا من الفراغات لملء الحقل، ثم سنوسِّط السلسلة النصية عبر استعمال الرمز ^ مُحدِّدين أنَّ حجم الحقل هو 20 محرف، ومُشيرين في نفس الوقت إلى أنَّ الحقل هو حقلٌ نصيٌ عبر وضع الرمز s. يمكننا استخدام هذه المعاملات مع المعاملات التي استخدمناها وشرحناها في الأقسام السابقة: print("Sammy ate {0:5.0f} percent of a pizza!".format(75.765367)) الناتج: Sammy ate 76 percent of a pizza! حدِّدنا داخل القوسين المعقوفين رقم فهرس القيمة العددية ثم وضعنا النقطتين الرأسيتين ثم اخترنا حجم الحقل ثم وضعنا نقطةً . لنضبط عدد المنازل العشرية الظاهرة، ثم اخترنا نوع الحقل عبر رمز التحويل f. استخدام المتغيرات مرَّرنا منذ بداية هذا الدرس وإلى الآن الأعداد والسلاسل النصية إلى الدالة str.format()‎ مباشرةً، لكننا نستطيع تمرير المتغيرات أيضًا، وذلك بنفس الآلية المعتادة: nBalloons = 8 print("Sammy has {} balloons today!".format(nBalloons)) الناتج: Sammy has 8 balloons today! يمكننا أيضًا استخدام المتغيرات لتخزين السلسلة النصية الأصلية بالإضافة إلى القيم التي ستُمرَّر إلى الدالة: sammy = "Sammy has {} balloons today!" nBalloons = 8 print(sammy.format(nBalloons)) الناتج: Sammy has 8 balloons today! ستُسهِّل المتغيرات من التعامل مع تعبيرات التنسيق وتُبسِّط عملية إسناد مدخلات المستخدم وإظهارها مُنسَّقةً في السلسلة النصية النهائية. استخدام المُنسِّقات لتنظيم البيانات يسطع نجم آلية التنسيق التي نشرحها في هذا الدرس عندما تُستخدَم لتنظيم البيانات بصريًا، فلو أردنا إظهار نتائج قاعدة البيانات إلى المستخدمين، فيمكننا استعمال المُنسِّقات لزيادة حجم الحقل وتعديل المحاذاة لجعل الناتج أسهل قراءةً. لننظر إلى حلقة تكرار تقليدية في بايثون التي تطبع i و i*i و i*i*i لمجالٍ من الأعداد من 3 إلى 13: for i in range(3,13): print(i, i*i, i*i*i) الناتج: 3 9 27 4 16 64 5 25 125 6 36 216 7 49 343 8 64 512 9 81 729 10 100 1000 11 121 1331 12 144 1728 صحيحٌ أنَّ الناتج مُنظَّمٌ قليلًا، إلا أنَّ الأعداد تتداخل مع بعضها بصريًا مما يُصعِّب قراءة الأسطر الأخيرة من الناتج، وإذا كنتَ تتعامل مع مجموعة أكبر من البيانات التي يتواجد فيها أعداد أكبر (أو أصغر) مما عرضناه في مثالنا، فقد تبدو لك المشكلة جليةً حينها. لنحاول تنسيق الناتج السابق لإعطاء مساحة أكبر لإظهار الأعداد: for i in range(3,13): print("{:3d} {:4d} {:5d}".format(i, i*i, i*i*i)) لم نُحدِّد في المثال السابق ترتيب الحقل وبدأنا مباشرةً بكتابة النقطتين الرأسيتين متبوعةً بحجم الحقل ورمز التحويل d (لأننا نتعامل مع أعداد صحيحة). أعطينا في المثال السابق حجمًا للحقل مساويًا لعدد أرقام العدد الذي نتوقع طباعته في الحقل المعني مضافًا إليه 2، لذا سيبدو الناتج كالآتي: 3 9 27 4 16 64 5 25 125 6 36 216 7 49 343 8 64 512 9 81 729 10 100 1000 11 121 1331 12 144 1728 يمكننا أيضًا تحديد حجم ثابت للحقل لنحصل على أعمدة متساوية العرض، مما يضمن إظهار الأعداد الكبيرة بصورة صحيحة: for i in range(3,13): print("{:6d} {:6d} {:6d}".format(i, i*i, i*i*i)) الناتج: 3 9 27 4 16 64 5 25 125 6 36 216 7 49 343 8 64 512 9 81 729 10 100 1000 11 121 1331 12 144 1728 يمكننا أيضًا تعديل محاذاة النص الموجود في الأعمدة باستخدام الرموز > و ^ و <، وتبديل d إلى f لإظهار منازل عشرية، وغير ذلك مما تعلمناه في هذا الدرس لإظهار البيانات الناتجة كما نرغب. الخلاصة قد يكون استخدام المنسقات لوضع القيم في سلسلةٍ نصيةٍ أمرًا مفيدًا لتسهيل تنظيم البيانات والقيم، فالمنسقات هي آليةٌ تساعد في جعل الناتج مقروءًا للمستخدم. هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة -وبتصرّف- للمقال How To Use String Formatters in Python 3 لصاحبته Lisa Tagliaferri. اقرأ أيضًا المقالة التالية: كيفية إجراء العمليات الحسابية المقالة السابقة: كيفية استخدام المتغيرات المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون
  4. السلاسل النصية في بايثون هي مجموعةٌ مُنشأةٌ من المحارف المنفصلة والتي تُمثِّل الأحرف والأرقام والفراغات والرموز. ولأنَّ السلسلة النصية هي «سلسلة»، فيمكن الوصول إليها كغيرها من أنواع البيانات المتسلسلة، عبر الفهارس والتقسيم. سيشرح لك هذا الدرس كيفية الوصول إلى السلاسل النصية بفهارسها، وتقسيمها إلى مجموعات من المحارف، وسيُغطي طرائق إحصاء المحارف وتحديد مواضعها. آلية فهرسة السلاسل النصية وكما في نوع البيانات list الذي فيه عناصر مرتبطة بأرقام، فإنَّ كل محرف في السلسلة النصية يرتبط بفهرس معيّن، بدءًا من الفهرس 0. فللسلسلة النصية Sammy Shark!‎ ستكون الفهارس والمحارف المرتبطة بها كالآتي: وكما لاحظت، سيرتبط المحرف S بالفهرس 0، وستنتهي السلسلة النصية بالفهرس 11 مع الرمز !. لاحظ أيضًا أنَّ الفراغ بين كلمتَي Sammy و Shark له فهرسٌ خاصٌ به، وفي مثالنا سيكون له الفهرس 5. علامة التعجب (!) لها فهرسٌ خاصٌ بها أيضًا، وأيّة رموز أو علامات ترقيم أخرى مثل *#$&.;? سترتبط بفهرسٍ مخصصٍ لها. وحقيقة أنَّ المحارف في بايثون لها فهرسٌ خاصٌ بها ستعني أنَّ بإمكاننا الوصول إلى السلاسل النصية وتعديلها كما نفعل مع أنواع البيانات المتسلسلة الأخرى. الوصول إلى المحارف عبر أرقام الفهارس الموجبة يمكننا الحصول على محرف من سلسلة نصية بالإشارة إليه عبر فهرسه. يمكننا فعل ذلك بوضع رقم الفهرس بين قوسين مربعين. سنُعرِّف في المثال الآتي سلسلةً نصيةً ونطبع المحرف المرتبط بالفهرس المذكور بين قوسين مربعين: ss = "Sammy Shark!" print(ss[4]) الناتج: y عندما نُشير إلى فهرسٍ معيّنٍ في سلسلةٍ نصيةٍ، فستُعيد بايثون المحرف الموجود في ذاك الموضع، ولمّا كان المحرف y موجودًا في الفهرس الرابع في السلسلة النصية ss = "Sammy Shark!"‎ فعندما طبعنا ss[4] فظهر الحرف y. الخلاصة: أرقام الفهارس ستسمح لنا بالوصول إلى محارف معيّنة ضمن سلسلة نصية. الوصول إلى المحارف عبر أرقام الفهارس السالبة إذا كانت لديك سلسلةٌ طويلةٌ وأردنا تحديد أحد محارفها لكن انطلاقًا من نهايتها، فعندئذٍ نستطيع استخدام الأرقام السالبة للفهارس، بدءًا من الفهرس ‎-1. لو أردنا أن نستخدم الفهارس السالبة مع السلسلة النصية Sammy Shark!‎، فستبدو كما يلي: يمكننا أن نطبع المحرف r في السلسلة السابقة في حال استخدمنا الفهارس السالبة بالإشارة إلى المحرف الموجود في الفهرس ‎-3 كما يلي: print(ss[-3]) الخلاصة: يمكننا الاستفادة من الفهارس السالبة لو أردنا الوصول إلى محرف في آخر سلسلة نصية طويلة. تقسيم السلاسل النصية يمكننا أن نحصل على مجال من المحارف من سلسلة نصية، فلنقل مثلًا أننا نريد أن نطبع الكلمة Shark فقط، يمكننا فعل ذلك بإنشائنا «لقسم» من السلسلة النصية، والذي هو سلسلةٌ من المحارف الموجودة ضمن السلسلة الأصلية. فالأقسام تسمح لنا بالوصول إلى عدِّة محارف دفعةً واحدة باستعمال مجال من أرقام الفهارس مفصولة فيما بينها بنقطتين رأسيتين [x:y]: print(ss[6:11]) الناتج: Shark عند إنشائنا لقسم مثل [6:11] فسيُمثِّل أوّل رقم مكان بدء القسم (متضمنًا المحرف الموجود عند ذاك الفهرس)، والرقم الثاني هو مكان نهاية القسم (دون تضمين ذاك المحرف)، وهذا هو السبب وراء استخدمنا لرقم فهرس يقع بعد نهاية القسم الذي نريد اقتطاعه في المثال السابق. نحن نُنشِئ «سلسلةً نصيةً فرعيةً» (substring) عندما نُقسِّم السلاسل النصية، والتي هي سلسلةٌ موجودةٌ ضمن سلسلةٍ أخرى. وعندما نستخدم التعبير ss[6:11] فنحن نستدعي السلسلة النصية Shark التي تتواجد ضمن السلسلة النصية Sammy Shark!‎. إذا أردت تضمين نهاية السلسلة (أو بدايتها) في القسم الذي ستُنشِئه، فيمكننا ألّا نضع أحد أرقام الفهارس في string[n:n]. فمثلًا، نستطيع أن نطبع أوّل كلمة من السلسلة ss –أي Sammy– بكتابة ما يلي: print(ss[:5]) فعلنا ذلك بحذف رقم الفهرس قبل النقطتين الرأسيتين، ووضعنا رقم فهرس النهاية فقط، الذي يُشير إلى مكان إيقاف اقتطاع السلسلة النصية الفرعية. لطباعة منتصف السلسلة النصية إلى آخرها، فسنضع فهرس البداية فقط قبل النقطتين الرأسيتين، كما يلي: print(ss[7:]) الناتج: hark! بكتابة فهرس البداية فقط قبل النقطتين الرأسيتين وترك تحديد الفهرس الثاني، فإنَّ السلسلة الفرعية ستبدأ من الفهرس الأول إلى نهاية السلسلة النصية كلها. يمكنك أيضًا استخدام الفهارس السالبة في تقسيم سلسلة نصية، فكما ذكرنا سابقًا، تبدأ أرقام الفهارس السلبية من الرقم ‎-1، ويستمر العد إلى أن نصل إلى بداية السلسلة النصية. وعند استخدام الفهارس السالبة فسنبدأ من الرقم الأصغر لأنه يقع أولًا في السلسلة. لنستخدم فهرسين ذوي رقمين سالبين لاقتطاع جزء من السلسلة النصية ss: print(ss[-4:-1]) الناتج: ark السلسلة النصية «ark» مأخوذة من السلسلة النصية «Sammy Shark!‎» لأنَّ الحرف «a» يقع في الموضع ‎-4 والمحرف k يقع قبل الفهرس ‎-1 مباشرةً. تحديد الخطوة عند تقسيم السلاسل النصية يمكن تمرير معامل ثالث عند تقسيم السلاسل النصية إضافةً إلى فهرسَي البداية والنهاية، وهو الخطوة، التي تُشير إلى عدد المحارف التي يجب تجاوزها بعد الحصول على المحرف من السلسلة النصية. لم نُحدِّد إلى الآن الخطوة في أمثلتنا، إلا أنَّ قيمته الافتراضية هي 1، لذا سنحصل على كل محرف يقع بين الفهرسين. لننظر مرةً أخرى إلى المثال السابق الذي يطبع السلسلة النصية الفرعية «Shark»: print(ss[6:11]) Shark سنحصل على نفس النتائج بتضمين معامل ثالث هو الخطوة وقيمته 1: print(ss[6:11:1]) Shark إذًا، إذا كانت الخطوة 1 فهذا يعني أنَّ بايثون ستُضمِّن جميع المحارف بين فهرسين، وإذا حذفتَ الخطوة فستعتبرها بايثون مساويةً للواحد. أما لو زدنا الخطوة، فسنرى أنَّ بعض المحارف ستهمل: print(ss[0:12:2]) SmySak تحديد الخطوة بقيمة 2 كما في ss[0:12:2] سيؤدي إلى تجاوز حرف بين كل حرفين، ألقِ نظرةً على المحارف المكتوبة بخطٍ عريضٍ: Sammy Shark! لاحظ أنَّ الفراغ الموجود في الفهرس 5 قد أُهمِل أيضًا عندما كانت الخطوة 2. إذا وضعنا قيمةً أكبر للخطوة، فسنحصل على سلسلةٍ نصيةٍ فرعيةٍ أصغر بكثير: print(ss[0:12:4]) Sya حذف الفهرسين وترك النقطتين الرأسيتين سيؤدي إلى إبقاء كامل السلسلة ضمن المجال، لكن إضافة معامل ثالث وهو الخطوة سيُحدِّد عدد المحارف التي سيتم تخطيها. إضافةً إلى ذلك، يمكنك تحديد رقم سالب كخطوة، مما يمكِّنك من كتابة السلسلة النصية بترتيبٍ معكوس إذا استعملتَ القيمة ‎-1: print(ss[::-1]) الناتج: !krahS ymmaS لنجرِّب ذلك مرةً أخرى لكن إذا كانت الخطوة ‎-2: print(ss[::-2]) الناتج: !rh ma في المثال السابق (ss[::-2])، سنتعامل مع كامل السلسلة النصية لعدم وجود أرقام لفهارس البداية والنهاية، وسيتم قلب اتجاه السلسلة النصية لاستخدامنا لخطوةٍ سالبة. بالإضافة إلى أنَّ الخطوة ‎-2 ستؤدي إلى تخطي حرف بين كل حرفين بترتيبٍ معكوس: !krahS[whitespace]ymmaS سيُطبَع الفراغ في المثال السابق. الخلاصة: تحديد المعامل الثالث عند تقسيم السلاسل النصية سيؤدي إلى تحديد الخطوة التي تُمثِّل عدد المحارف التي سيتم تخطيها عند الحصول على السلسلة الفرعية من السلسلة الأصلية. دوال الإحصاء بعد أن تعرفنا على آلية فهرسة المحارف في السلاسل النصية، حان الوقت لمعاينة بعض الدوال التي تُحصي السلاسل النصية أو تعيد أرقام الفهارس. يمكننا أن نستفيد من ذلك بتحديد عدد المحارف التي نريد استقبالها من مدخلات المستخدم، أو لمقارنة السلاسل النصية. ولدى السلاسل النصية –كغيرها من أنواع البيانات– عدِّة دوال تُستخدم للإحصاء. لننظر أولًا إلى الدالة len()‎ التي تُعيد طول أيّ نوع متسلسل من البيانات، بما في ذلك string و lists و tuples و dictionaries. لنطبع طول السلسلة النصية ss: print(len(ss)) 12 طول السلسلة النصية «Sammy Shark!‎» هو 12 محرفًا، بما في ذلك الفراغ وعلامة التعجب. بدلًا من استخدام متغير، فلنحاول مباشرةً تمرير سلسلة نصية إلى الدالة len()‎: print(len("Let's print the length of this string.")) 38 الدالة len()‎ تُحصي العدد الإجمالي من المحارف في سلسلة نصية. إذا أردنا إحصاء عدد مرات تكرار محرف أو مجموعة من المحارف في سلسلة نصية، فيمكننا استخدام الدالة str.count()‎، لنحاول إحصاء الحرف «a» في السلسلة النصية ss = "Sammy Shark!"‎: print(ss.count("a")) 2 يمكننا البحث عن محرفٍ آخر: print(ss.count("s")) 0 صحيحٌ أنَّ الحرف «S» قد ورد في السلسلة النصية، إلا أنَّه من الضروري أن تبقي بذهنك أنَّ بايثون حساسةٌ لحالة الأحرف، فلو أردنا البحث عن حروفٍ معيّنة بغض النظر عن حالتها، فعلينا حينها استخدام الدالة str.lower()‎ لتحويل حروف السلسلة النصية إلى حروفٍ صغيرة أولًا. يمكنك قراءة المزيد من المعلومات عن دالة str.lower()‎ في درس «مقدمة إلى دوال التعامل مع السلاسل النصية في بايثون 3» لنحاول استخدام الدالة str.count()‎ مع سلسلة من المحارف: likes = "Sammy likes to swim in the ocean, likes to spin up servers, and likes to smile." print(likes.count("likes")) الناتج هو 3، حيث تتواجد مجموعة المحارف «likes» ثلاث مرات في السلسلة النصية الأصلية. يمكننا أيضًا معرفة موقع الحرف أو مجموعة الحروف في السلسلة النصية، وذلك عبر الدالة str.find()‎، وسيُعاد موضع المحرف بناءً على رقم فهرسه. يمكننا أن نعرف متى يقع أوّل حرف «m» في السلسلة النصية ss كالآتي: print(ss.find("m")) 2 أوّل مرة يقع فيها الحرف «m» في الفهرس 2 من السلسلة «Sammy Shark!‎»، يمكنك أن تراجع بداية هذا الدرس لرؤية جدول يبيّن ارتباطات المحارف مع فهارسها في السلسلة السابقة. لنرى الآن مكان أوّل ظهور لمجموعة المحارف «likes» في السلسلة likes: print(likes.find("likes")) 6 أوّل مرة تظهر فيها السلسلة «likes» هي في الفهرس 6، أي مكان وجود الحرف l من likes. ماذا لو أردنا أن نعرف موضع ثاني تكرار للكلمة «likes»؟ نستطيع فعل ذلك بتمرير معاملٍ ثانٍ إلى الدالة str.find()‎ الذي سيجعلها تبدأ بحثها من ذاك الفهرس، فبدلًا من البحث من أوّل السلسلة النصية سنبحث انطلاقًا من الفهرس 9: print(likes.find("likes", 9)) 34 بدأ البحث في هذا المثال من الفهرس 9، وكانت أوّل مطابقة للسلسلة النصية «likes» عند الفهرس 34. إضافةً إلى ذلك، يمكننا تحديد نهاية إلى مجال البحث بتمرير معامل ثالث. وكما عند تقسيم السلاسل النصية، يمكننا استخدام أرقام الفهارس السالبة للعد عكسيًا: print(likes.find("likes", 40, -6)) 64 يبحث آخر مثال عن موضع السلسلة النصية «likes» بين الفهرس 40 و ‎-6، ولمّا كان المعامل الأخير هو رقم سالب، فسيبدأ العد من نهاية السلسلة الأصلية. دوال الإحصاء مثل len()‎ و str.count()‎ و str.find()‎ مفيدةٌ في تحديد طول السلسلة النصية وعدد حروفها وفهارس ورود محارف معيّنة فيها. الخلاصة ستمنحنا القدرة على تحديد محارف بفهارسها أو تقسيم سلسلة نصية أو البحث فيها مرونةً عاليةً عند التعامل مع السلاسل النصية. ولأنَّ السلاسل النصية هي نوعٌ من أنواع البيانات المتسلسلة (كنوع البيانات list)، فيمكننا الوصول إلى عناصرها عبر فهارس خاصة. هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة -وبتصرّف- للمقال How To Index and Slice Strings in Python 3 لصاحبته Lisa Tagliaferri. اقرأ أيضًا المقال التالي: كيفية التحويل بين أنواع البيانات المقال السابق: مقدمة إلى دوال التعامل مع السلاسل النصية المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون
  5. لدى بايثون عدِّة دوال مبنية فيها للتعامل مع السلاسل النصية. تسمح هذه الدوال لنا بتعديل وإجراء عمليات على السلاسل النصية بسهولة. يمكنك أن تتخيل الدوال على أنها «أفعال» يمكننا تنفيذها على عناصر موجودة في الشيفرة. الدوال المبنية في اللغة هي الدوال المُعرَّفة داخل لغة بايثون وهي جاهزة مباشرةً للاستخدام. سنشرح في هذا الدرس مختلف الدوال التي نستطيع استخدامها للتعامل مع السلاسل النصية في بايثون 3. جعل السلاسل النصية بأحرف كبيرة أو صغيرة الدالتان str.upper()‎ و str.lower()‎ ستُعيدان السلسلة النصية بعد تحويل حالة جميع أحرفها الأصلية إلى الأحرف الكبيرة أو الصغيرة (على التوالي وبالترتيب). ولعدم قدرتنا على تعديل السلسلة النصية بعد إنشائها، فستُعاد سلسلةٌ نصيةٌ جديدةٌ. لن تُعدَّل أيّة محارف غير لاتينية في السلسلة النصية الأصلية وستبقى على حالها. لنحوِّل السلسلة النصية Sammy Shark إلى أحرفٍ كبيرة: ss = "Sammy Shark" print(ss.upper()) الناتج: SAMMY SHARK لنحوِّلها الآن إلى أحرفٍ صغيرة: print(ss.lower()) وسينتج: sammy shark ستُسهِّل الدالتان str.upper()‎ و str.lower()‎ عملية التحقق من مساواة سلسلتين نصيتين لبعضهما أو لمقارنتهما وذلك عبر توحيد حالة الأحرف. فلو كتب المستخدم اسمه بأحرفٍ صغيرةٍ فسنستطيع أن نتأكد إن كان مُسجَّلًا في قاعدة البيانات بمقارنته بعد تحويل حالة أحرفه. الدوال المنطقية تتوفر في بايثون عدِّة دوال التي ستتحقق من القيم المنطقية (Boolean). هذه الدوال مفيدةٌ عند إنشائنا للنماذج التي يجب على المستخدمين ملأها؛ فمثلًا، إذا سألنا المستخدم عن الرمز البريدي وأردنا أن نقبل السلاسل النصية التي تحتوي أرقامًا فقط، أو عندما نسأله عن اسمه فسنقبل سلسلةً نصيةً تحتوي حروفًا فقط. هنالك عددٌ من الدوال التي تُعيد قيمًا منطقيةً: str.isalnum()‎: تُعيد true إذا احتوت السلسلة النصية على أرقام وأحرف فقط (دون رموز). str.isalpha()‎: تُعيد true إذا احتوت السلسلة النصية على أحرف فقط (دون أرقام أو رموز). str.islower()‎: تُعيد true إذا كانت جميع أحرف السلسلة النصية صغيرة. str.isnumeric()‎: تُعيد true إذا احتوت السلسلة النصية على أرقام فقط. str.isspace()‎: تُعيد true إذا لم تحتوي السلسلة النصية إلا على الفراغات. str.istitle()‎: تُعيد true إذا كانت حالة أحرف السلسلة النصية كما لو أنها عنوان (أي أنَّ أوّل حرف من كل كلمة كبير، والبقية صغيرة). str.isupper()‎: تُعيد true إذا كانت جميع أحرف السلسلة النصية كبيرة. لننظر إلى بعضها عمليًا: number = "5" letters = "abcdef" print(number.isnumeric()) print(letters.isnumeric()) الناتج: True False استخدام الدالة str.isnumeric()‎ على السلسلة النصية 5 سيُعيد القيمة True، بينما استخدام نفس الدالة على السلسلة النصية abcdef سيُعيد False. وبشكلٍ مماثل، يمكننا معرفة إن كانت حالة الأحرف في سلسلةٍ نصيةٍ كما لو أنها عنوان، أو أنها كبيرة أو صغيرة. لنُنشِئ بدايةً بعض السلاسل النصية: movie = "2001: A SAMMY ODYSSEY" book = "A Thousand Splendid Sharks" poem = "sammy lived in a pretty how town" لنجرّب الدوال المنطقية لمعرفة الناتج (سنعرض كل دالتين وناتجهما تحتهما): print(movie.islower()) print(movie.isupper()) False True print(book.istitle()) print(book.isupper()) True False print(poem.istitle()) print(poem.islower()) False True ستساعدنا معرفة إن كانت أحرف السلسلة النصية بحالة صغيرة أو كبيرة أو كأنها عنوان في تصنيف البيانات تصنيفًا سليمًا، وتوفِّر لنا الفرصة لتوحيد طريقة تخزين البيانات بالتحقق من حالة أحرفها ثم تعديلها وفقًا لذلك. الدوال المنطقية التي تعمل على السلاسل النصية مفيدةٌ أيضًا عندما نريد التحقق إن حقَّقَت مدخلات المستخدم شروطًا معيّنة. الدوال join()‎ و split()‎ و replace()‎ توفِّر الدوال str.join()‎ و str.split()‎ و str.replace()‎ إمكانياتٍ إضافيةً لتعديل السلاسل النصية في بايثون. الدالة str.join()‎ تجمع سلسلتين نصيتين مع بعضهما، لكنها تفعل ذلك بتمرير إحداها إلى الأخرى. لنُنشِئ سلسلةً نصيةً: balloon = "Sammy has a balloon." لنستخدم الآن الدالة str.join()‎ لإضافة فراغات إلى تلك السلسلة النصية كالآتي: " ".join(balloon) إذا طبعنا الناتج: print(" ".join(balloon)) فسنجد أنَّ السلسلة النصية الجديدة هي السلسلة الأولى لكن بين كل حرفين فراغ: S a m m y h a s a b a l l o o n . يمكننا أيضًا استخدام الدالة str.join()‎ لإنشاء مقلوب سلسلة نصية: print("".join(reversed(balloon))) .noollab a sah ymmaS لم نرغب في إضافة أيّة سلسلة نصية إلى أخرى، لذا أبقينا على السلسلة النصية فارغةً دون محتوى داخلها. الدالة str.join()‎ مفيدةٌ أيضًا لجمع قائمة (list) من السلاسل النصية وإخراجها إلى سلسلةٍ وحيدة. لنُنشِئ سلسلة نصية يُفصَل بين كلماتها بفاصلة من القائمة الآتية: print(",".join(["sharks", "crustaceans", "plankton"])) sharks,crustaceans,plankton إذا أردتَ وضع فاصلة ثم فراغ بين القيم في المثال السابق، فيمكنك أن تُعيد كتابة التعبير البرمجي السابق لإضافة فراغ بعد الفاصلة كما يلي: ", ".join(["sharks", "crustaceans", "plankton"]). وكما نستطيع جمع السلاسل النصية مع بعضها بعضًا، نستطيع أيضًا تجزئتها، وذلك عبر الدالة str.split()‎: print(balloon.split()) ['Sammy', 'has', 'a', 'balloon.'] الدالة str.split()‎ ستُعيد قائمة (list) تحتوي سلاسل نصية كانت مفصولةً بالفراغات في السلسلة النصية الأصلية إذا لم يُمرَّر معاملٌ لتحديد محرف الفصل. يمكنك أيضًا استخدام الدالة str.split()‎ لحذف أجزاء معيّنة من السلسلة النصية الأصلية، فلنحاول مثلًا حذف الحرف a: print(balloon.split("a")) ['S', 'mmy h', 's ', ' b', 'lloon.'] حُذِفَ الحرف a من السلسلة النصية وأصبح الناتج مقسومًا عند كل ورود للحرف a مع الإبقاء على الفراغات. تأخذ الدالة str.replace()‎ سلسلةً نصيةً وتُعيد نسخةً محدَّثةً منها بعد إجراء بعض عمليات الاستبدال عليها. لنفترض أنَّ البالون الذي يملكه سامي قد ضاع، ولعدم امتلاك سامي للبالون في الوقت الراهن، فسنُبدِّل الكلمة "has" إلى "had": print(balloon.replace("has","had")) أوّل سلسلة نصية داخل أقواس الدالة replace()‎ هي السلسلة النصية التي نريد استبدالها، والسلسلة النصية الثانية هي السلسلة التي نريد وضعها بدلًا من الأولى. ناتج تنفيذ السطر السابق هو: Sammy had a balloon. استخدام دوال تعديل السلاسل النصية مثل str.join()‎ و str.split()‎ و str.replace سيمنحك تحكمًا كبيرًا بمعالجة السلاسل النصية في بايثون. الخلاصة لقد تعلمنا في هذا الدرس بعض الدوال المُضمَّنة في لغة بايثون لمعالجة النصوص والتعامل معها ومعرفة بعض خصائصها. هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة -وبتصرّف- للمقال An Introduction to String Methods in Python 3 لصاحبته Lisa Tagliaferri اقرأ أيضًا المقال التالي: آلية فهرسة السلاسل النصية وطريقة تقسيمها المقال السابق: كيفية تنسيق النصوص المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون
  6. تتألف السلاسل النصية عادةً من النص المكتوب، وهنالك عدِّة حالات نحتاج فيها إلى تحكمٍ أكبر بكيفية إظهار النص وجعلها أسهل قراءةً للبشر عبر وضع علامات الترقيم والسطور الجديدة والمحاذاة. سنشرح في هذا الدرس كيفية التعامل مع السلاسل النصية في بايثون لكي يظهر النص الناتج بتنسيقٍ صحيح. القيم الحرفية لنفرِّق أولًا بين القيم الحرفية للسلاسل النصية (string literal) والسلاسل النصية نفسها (string value)، فالقيم الحرفية هي ما نراه في الشيفرة المصدرية للبرنامج، بما في ذلك علامتَي الاقتباس. أما السلسلة النصية نفسها فهي ما نراها عندما نستدعي الدالة print()‎ عند تشغيل البرنامج. ففي برنامج «Hello, World!‎» التقليدي، تكون القيمة الحرفية هي "Hello, World!‎" بينما السلسلة النصية هي Hello, World!‎ دون علامتَي الاقتباس. أي أنَّ السلسلة النصية هي ما نراه في نافذة الطرفية عندما نُشغِّل برنامج بايثون. لكن بعض السلاسل النصية قد تحتوي على علامات اقتباس، مثل اقتباسنا لمقولةٍ ما. ولأنَّ القيم الحرفية والقيم الفعلية للسلاسل النصية غير متساوية، فمن الضروري في أغلب الحالات إضافة تنسيق إلى القيم الحرفية لعرض السلاسل النصية كما ينبغي. علامات الاقتباس بسبب إمكانيتنا استخدام علامات الاقتباس المفردة أو المزدوجة في بايثون، فمن السهل تضمين الاقتباسات بوضعها بين علامتَي اقتباس مزدوجتين في سلسلةٍ نصيةٍ محاطةٍ بعلامتَي اقتباس مفردتين كما في السلسلة الآتية: 'Sammy says, "Hello!"' أو يمكننا استخدام علامة اقتباس فردية (أو كما يسمونها «فاصلة عليا» [apostrophe]) في سلسلةٍ نصيةٍ محاطةٍ بعلامتَي اقتباس مزدوجتين: "Sammy's balloon is red." إذًا، يمكننا التحكم بطريقة عرض علامات الاقتباس والفواصل العليا في سلاسلنا النصية عبر استخدام النوع الصحيح من علامات الاقتباس لإحاطة كامل السلسلة النصية. دورة تطوير التطبيقات باستخدام لغة Python احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة اشترك الآن كتابة النص على أكثر من سطر طباعة السلاسل النصية على أكثر من سطر ستجعل منها واضحةً وسهلة القراءة. إذ يمكن تجميع النصوص المكتوبة بعدِّة أسطر لزيادة وضوحها، أو لتنسيقها كرسالة، أو للحفاظ على تعدد الأسطر في الأشعار. نستخدم ثلاث علامات اقتباس فردية ''' أو ثلاث علامات اقتباس مزدوجة """ للإحاطة بالسلسلة النصية التي تمتد على أكثر من سطر: ''' This string is on multiple lines within three single quotes on either side. ''' """ This string is on multiple lines within three double quotes on either side. """ يمكنك الآن طباعة السلاسل النصية في عدِّة أسطر لجعل النصوص سهلة القراءة –خصوصًا الطويلة منها– وذلك عبر استخدام ثلاث علامات اقتباس متتالية. «تهريب» المحارف طريقة أخرى لتنسيق السلاسل النصية هي استخدام «محرف التهريب» (escape character). فجميع عمليات تهريب المحارف تبدأ بالخط المائل الخلفي (backslash، أي \) متبوعًا بمحرفٍ آخر الذي له معنى خاص يفيد في تنسيق السلسلة النصية. هذه قائمة بأكثر محارف التهريب شيوعًا: \: سطرٌ جديدٌ في سلسلةٍ نصيةٍ متألفةٍ من عدِّة أسطر. \\: طباعة رمز الخط المائل الخلفي. ‎\'‎: طباعة علامة اقتباس فردية. ‎\"‎: طباعة علامة اقتباس مزدوجة. ‎\‎n‎: طباعة محرف الانتقال إلى سطرٍ جديد. ‎\t: طباعة محرف الجدولة (Tab). لنستخدم محرف التهريب لإضافة علامات الاقتباس إلى سلسلتنا النصية السابقة، لكن هذه المرة سنُحيط السلسلة النصية بعلامتَي اقتباس مزدوجتين: print("Sammy says, \"Hello!\"") Sammy says, "Hello!" تمكننا عبر محرف التهريب ‎\"‎ من استخدام علامات الاقتباس المزدوجة للإحاطة بالسلسلة النصية التي تحتوي على نصٍ مقتبسٍ ومحاطٍ بعلامتَي اقتباس مزدوجتين. نستطيع أيضًا استخدام محرف التهريب ‎\'‎ لإضافة علامة اقتباس مفردة ضمن السلسلة النصية المحاطة بعلامتَي اقتباس مفردتين: print('Sammy\'s balloon is red.') Sammy's balloon is red. ولأننا نستخدم الآن محرف التهريب فنستطيع وضع علامات الاقتباس المفردة حتى لو كانت السلسلة النصية كلها موجودة بين علامتَي اقتباس مفردتين. عندما نستخدم علامات الاقتباس الثلاثية –كما فعلنا أعلاه– فسنجد فراغًا في أعلى وأسفل النص عند طباعته. نستطيع حذف تلك الفراغات عبر استخدام محرف التهريب \ في بداية ونهاية السلسلة النصية مع الإبقاء على النص مقروءًا بسهولة في الشيفرة. """\ This multi-line string has no space at the top or the bottom when it prints.\ """ وبشكلٍ شبيهٍ بما سبق، يمكننا استخدام محرف التهريب ‎\n لوضع أسطر جديدة دون الحاجة إلى الضغط على زر Enter أو Return: print("This string\nspans multiple\nlines.") This string spans multiple lines. يمكننا الدمج بين محارف التهريب، إذ سنطبع في المثال الآتي سلسلةً نصيةً على أكثر من سطر، ونستعمل فيها مسافة جدولة (tab) بين الترقيم ومحتوى السطر: print("1.\tShark\n2.\tShrimp\n10.\tSquid") 1. Shark 2. Shrimp 10. Squid علامة الجدولة الأفقية التي وضعناها عبر محرف التهريب ‎\t ستؤدي إلى محاذاة الكتابة في العمود النصي الثاني في المثال أعلاه، مما يجعل قراءتها سهلةً جدًا. وصحيحٌ أنَّ محرف التهريب ‎\n يعمل عملًا جيدًا في النصوص القصير، لكن لا نُغفِل أهمية أن تكون الشيفرة المصدرية مقروءةً بسهولةٍ أيضًا. فلو كان النص طويلًا، فأرى أنَّ من الأفضل استخدام علامات الاقتباس الثلاثية. رأينا أنَّ محارف التهريب تُستعمَل لإضافة تنسيق إلى السلاسل التي كان من الصعب (أو حتى المستحيل) عرضها عرضًا سليمًا دونها. فهل تستطيع مثلًا أن تطبع السلسلة النصية الآتية دون استخدام محارف التهريب Sammy says, "The balloon's color is red.‎"‎؟ السلاسل النصية «الخام» ماذا لو أردنا تجاهل كل محارف التنسيق الخاصة في سلاسلنا النصية؟ فلربما أردنا مقارنة أو التحقق من صحة بعض الشيفرات الحاسوبية التي تستخدم الخط المائل الخلفي، ولا نريد من بايثون تفسيره على أنها محرف تهريب. أتت «السلاسل النصية الخام» (raw strings) في بايثون لتحل هذه المشكلة، وتتجاهل جميع محارف التنسيق داخل سلسلة نصية، بما في ذلك محارف التهريب. يمكننا إنشاء سلسلة نصية خام بوضع الحرف r في بداية السلسلة النصية، قبل علامة الاقتباس الأولى مباشرةً: print(r"Sammy says,\"The balloon\'s color is red.\"") Sammy says,\"The balloon\'s color is red.\" سنستطيع الإبقاء على محارف التهريب كما هي في السلاسل النصية إن أسبقناها بالحرف r لتحويلها إلى سلاسل نصية خام. الخلاصة شرحنا في هذا الدرس عدِّة طرائق لتنسيق النص في بايثون 3؛ وعرفنا كيف نُظهِر السلاسل النصية كما نريد عبر استخدامنا لمحارف التهريب أو السلاسل النصية الخام، لكي يستفيد المستخدم من النص الناتج ويقرأه بسهولة. هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة -وبتصرّف- للمقال How To Format Text in Python 3 لصاحبته Lisa Tagliaferri اقرأ أيضًا المقال التالي: مقدمة إلى دوال التعامل مع السلاسل النصية المقال السابق: مدخل إلى التعامل مع السلاسل النصية المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون
  7. «السلسلة النصية» (string) هي مجموعة من المحارف (أي الأحرف والأرقام والرموز) التي إما أن تكون قيمة ثابتة أو قيمة لمتغير. وهذه السلاسل النصية مُشكَّلة من محارف يونيكود (Unicode) والتي لا يمكن تغيير مدلولها. ولأنَّ النص هو شكلٌ شائعٌ من أشكال البيانات الذي نستعمله يوميًا، لذا فإنَّ السلاسل النصية مهمة جدًا وتُمثِّل لُبنةً أساسيةً في البرمجة. سيستعرض هذا الدرس كيفية إنشاء وطباعة السلاسة النصية، وكيفية جمعها مع بعضها وتكرارها، وآلية تخزين السلاسل النصية في متغيرات. إنشاء وطباعة السلاسل النصية تتواجد السلاسل النصية إما داخل علامات اقتباس فردية ’ أو علامات اقتباس مزدوجة "، لذا لإنشاء سلسلة نصية، كل ما علينا فعله هو وضع مجموعة من المحارف بين أحد نوعَي علامات الاقتباس السابقَين: 'هذه سلسلة نصية ضمن علامتي اقتباس مفردتين.' "هذه سلسلة نصية ضمن علامتي اقتباس مزدوجتين" يمكنك الاختيار بين النوعَين السابقَين، لكن أيًّا كان اختيارك، فعليك أن تحافظ على استخدامك له في كامل برنامجك. يمكنك طباعة السلاسل النصية إلى الشاشة باستدعاء الدالة print()‎ بكل بساطة: print("Let's print out this string.") Let's print out this string. بعد أن فهمتَ كيفية تهيئة السلاسل النصية في بايثون، لنلقِ نظرةً الآن إلى كيفية التعامل مع السلاسل النصية في برامجك وتعديلها. جمع السلاسل النصية عملية الجمع (concatenation) تعني إضافة سلسلتين نصيتين إلى بعضهما بعضًا لإنشاء سلسلة نصية جديدة. نستخدم المعامل + لجمع السلاسل النصية؛ أبقِ في ذهنك أنَّ المعامل + يعني عملية الجمع عند التعامل مع الأعداد، أما عندما نستخدمه مع السلاسل النصية فيعني إضافتها إلى بعضها. لنجمع السلستين النصيتين "Sammy" و "Shark" مع بعضها ثم نطبعهما باستخدام الدالة print()‎: print("Sammy" + "Shark") SammyShark إذا أردتَ وضع فراغ بين السلسلتين النصيتين، فيمكنك بكل بساطة وضعه عند نهاية السلسلة النصية الأولى، أي بعد الكلمة “Sammy”: print("Sammy " + "Shark") Sammy Shark لكن احرص على عدم استعمال المعامل + بين نوعَين مختلفَين من البيانات، فلن نتمكن من جمع السلاسل النصية والأرقام مع بعضها، فلو حاولنا مثلًا أن نكتب: print("Sammy" + 27) فسنحصل على رسالة الخطأ الآتية: TypeError: Can't convert 'int' object to str implicitly أما إذا أردنا أن نُنشِئ السلسلة النصية "Sammy27" فعلينا حينها وضع الرقم 27 بين علامتَي اقتباس ("27") مما يجعله سلسلةً نصيةً وليست عددًا صحيحًا. سنستفيد من تحويل الأعداد إلى سلاسل نصية عندما نتعامل مع أرقام الهواتف على سبيل المثال، لأننا لن نحتاج إلى إجراء عملية حسابية على رمز الدولة ورمز المنطقة في أرقام الهواتف، إلا أننا نريدهما أن يظهرا متتابعَين. عندما نجمع سلسلتين نصيتين أو أكثر فنحن نُنشِئ سلسلةً نصيةً جديدةً التي يمكننا استخدامها في برنامجنا. تكرار السلاسل النصية هنالك أوقاتٌ نحتاج فيها إلى استخدام بايثون لأتمتة المهام، وإحدى الأمور التي يمكننا أتمتتها هي تكرار سلسلة نصية لعدِّة مرات. إذ نستطيع فعل ذلك عبر المعامل *، وكما هو الأمر مع المعامل + فإنَّ المعامل * له استخدامٌ مختلف عندما نتعامل مع أرقام، حيث يُمثِّل عملية الضرب. أما عندما نستخدمه بين سلسلةٍ نصيةٍ ورقمٍ فإنَّ المعامل * هو معامل التكرار، فوظيفته هي تكرار سلسلة نصية لأي عدد مرات تشاء. لنحاول طباعة السلسلة النصية “Sammy” تسع مرات دون تكرارها يدويًا، وذلك عبر المعامل *: print("Sammy" * 9) المخرجات: SammySammySammySammySammySammySammySammySammy يمكننا بهذه الطريقة تكرار السلسلة النصية لأيِّ عددٍ نشاء من المرات. تخزين السلاسل النصية في متغيرات المتغيرات هي «رموز» التي يمكننا استعمالها لتخزين البيانات في برنامج. يمكنك تخيل المتغيرات على أنها صندوقٌ فارغٌ يمكنك ملؤه بالبيانات أو القيم. السلاسل النصية هي نوعٌ من أنواع البيانات، لذا يمكننا استعمالها لملء المتغيرات. التصريح عن السلاسل النصية كمتغيرات سيُسهِّل علينا التعامل معها في برامجنا. لتخزين سلسلة نصية داخل متغير، فكل ما علينا فعله هو إسنادها إليه. سنُصرِّح في المثال الآتي عن المتغير my_str: my_str = "Sammy likes declaring strings." أصبح المتغير my_str الآن مُشيرًا إلى سلسلةٍ نصيةٍ، والتي أمسى بمقدورنا طباعتها كما يلي: print(my_str) وسنحصل على الناتج الآتي: Sammy likes declaring strings. استخدام المتغيرات لاحتواء قيم السلاسل النصية سيساعدنا في الاستغناء عن إعادة كتابة السلسلة النصية في كل مرة نحتاج استخدامها، مما يُبسِّط تعاملنا معها وإجراءنا للعمليات عليها في برامجنا. الخلاصة لقد تعلمنا في درسنا هذا أساسيات التعامل مع السلاسل النصية في لغة بايثون 3. بما في ذلك إنشاءها وطباعتها وجمعها وتكرارها، إضافةً إلى تخزينها في متغيرات، وهذه هي المعلومات الأساسية التي عليك فهمها للانطلاق في تعاملك مع السلاسل النصية في برامج بايثون 3. هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3. ترجمة -وبتصرّف- للمقال An Introduction to Working with Strings in Python 3 لصاحبته -Lisa Tagliaferri اقرأ أيضًا المقالة التالي: كيفية تنسيق النصوص المقال السابق: فهم أنواع البيانات المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون
  8. Python هي لغةٌ سهلة القراءة للغاية ومتنوعة ومتعددة الاستخدامات، واسمها مستوحى من مجموعة كوميدية بريطانية باسم «Monty Python»، وكان أحد الأهداف الأساسية لفريق تطوير بايثون هو جعل اللغة مرحةً وسهلة الاستخدام، وإعدادها بسيطٌ، وطريقة كتابتها مباشرة وتعطيك تقريرًا مباشرًا عند حدوث أخطاء، وهي خيارٌ ممتازٌ للمبتدئين والوافدين الجدد على البرمجة. لغة بايثون هي لغة متعددة الاستعمالات، وتدعم مختلف أنماط البرمجة مثل كتابة السكربتات والبرمجة كائنية التوجه (object-oriented)، وهي مناسبةٌ للأغراض العامة، واستعمالها يتزايد في سوق العمل إذ تعتمدها منظماتٌ مثل «United Space Alliance» (شركة في مجال إرسال مركبات فضائية وتتعاقد معها ناسا) و «Industrial Light & Magic» (أستوديو للتأثيرات السينمائية وللرسوم المتحركة)، وتوفِّر بايثون قدراتٍ كثيرةٍ لمن يريد تعلم لغة برمجة جديدة. طوِّرَت اللغة في نهاية الثمانينات من القرن الماضي، ونُشِرَت أوّل مرة في عام 1991، طُوِّرَت بايثون من قِبل Guido van Rossum، وهو عضوٌ نشطٌ للغاية في المجتمع. وتعتبر بايثون على أنَّها بديلٌ عن لغة ABC، وأوّل إصدار منها كان يتضمن التعامل مع الاستثناءات (exception handling) والدوال والأصناف (classes) مع إمكانية الوراثة فيها. وعدما أُنشِئ منتدى محادثة في Usenet باسم comp.lang.python في 1994، فبدأت قاعدة مستخدمي بايثون بالنمو، مما مهّد الطريق لها لتصبح واحدة من أكثر لغات البرمجة شيوعًا وخصوصًا لتطوير البرمجيات مفتوحة المصدر. لمحة عامة قبل أن ننظر إلى إمكانيات إصدارَي بايثون 2 وبايثون 3 (مع الاختلافات البرمجية الرئيسية بينهما)، فلننظر إلى لمحة تاريخية عن الإصدارات الرئيسية الحديثة من بايثون. بايثون 2 نُشِرَ هذا الإصدار في أواخر عام 2000، وأصبحت بايثون 2 لغة برمجة شاملة مقارنةً بالإصدارات التي تسبقها وذلك بعد تطبيق اقتراح PEP ‏(Python Enhancement Proposal)، وهو مواصفةٌ (specification) تقنيةٌ التي توفِّر معلومات إلى أعضاء مجتمع بايثون أو تصف ميزاتٍ جديدة في اللغة. بالإضافة إلى ذلك، تضمنت بايثون 2 ميزاتٍ برمجية جديدة مثل «cycle-detecting garbage collector» لأتمتة عملية إدارة الذاكرة، وزيادة دعم يونيكود لتدعم اللغة جميع المحارف المعيارية …إلخ. وأثناء عملية تطوير بايثون 2 أضيفت ميزات جديدة بما في ذلك توحيد الأنواع والأصناف في بايثون في بنية هيكلية وحيدة (وذلك في إصدار 2.2 من بايثون). بايثون 3 تُعتَبر بايثون 3 هي مستقبل لغة بايثون وهي النسخة قيد التطوير من اللغة، وهذا إصدارٌ رئيسيٌ نُشِر في أواخر عام 2008 لإصلاح بعض المشاكل الجوهرية في تصميم الإصدارات السابقة من اللغة، وكان التركيز أثناء تطوير بايثون 3 هو تحسين الشيفرات التي تبنى عليها اللغة وحذف التكرارات، مما يعني أنَّ هنالك طريقة وحيدة فقط لإنجاز مهمّة معيّنة. التعديلات الأساسية التي حدثت في بايثون 3.0 تتضمن تغير العبارة print إلى دالة مُضمَّنة باللغة، وتحسين قسمة الأعداد الصحيحة، وتوفير دعم إضافي ليونيكود. في البداية، انتشرت بايثون 3 ببطء نتيجةً لعدم توافقيتها مع بايثون 2، مما يعني أنَّ على المستخدمين اختيار ما هو الإصدار الذي عليهم استخدامه. بالإضافة إلى ذلك، كانت الكثير من المكتبات البرمجية متاحةً فقط لبايثون 2، لكن بعد تقرير فريق تطوير بايثون 3 أنَّه يجب أن التخلي عن دعم بايثون 2، فبدأت عملية تحويل المكتبات إلى بايثون 3. يمكننا معرفة زيادة الاعتماد على بايثون 3 من خلال عدد الحزم البرمجية التي تدعم بايثون 3، والتي هي (في وقت كتابة هذا المقال) 339 من أصل 360 من أشهر الحزم. بايثون 2.7 بعد إصدار بايثون 3.0 في 2008، أُصدِرَت نسخة بايثون 2.7 في تموز 2010 وهي آخر إصدار من سلسلة ‎2.x، الغرض من إصدار بايثون 2.7 هو جعل الطريق ممهدًا أمام مستخدمي بايثون ‎2.x لتحويل برامجهم إلى بايثون 3 بتوفير بعض التوافقية بينهما. وهذه التوافقية تضمنت دعم بعض الوحدات المُحسّنة في 2.7 مثل unittest لأتمتة الاختبارات، و argparse لتفسير خيارات سطر الأوامر، وبعض الفئات في collections. ولخصوصية بايثون 2.7 ولكونها جسرًا واصلًا بين الإصدارات القديمة من بايثون 2 وبين بايثون 3.0، فأصبحت خيارًا شائعًا بين المبرمجين بسبب توافقيتها مع الكثير من المكتبات. عندما نتحدث اليوم عن بايثون 2، فنحن نشير عادةً إلى إصدار بايثون 2.7 لأنَّه أكثر إصدار مستخدم؛ لكنه يُعتَبَر أنَّه إصدارٌ قديم، وسيتوقف تطويره (التطوير الحالي هو إصلاح العلل فقط) تمامًا في 2020. الاختلافات الأساسية بغض النظر أنَّ بايثون 2.7 وبايثون 3 تتشاركان في الكثير من الأشياء، لكن لا يجدر بك أن تظن أنَّهما متماثلتان ويمكن تبديل الشيفرات بينهما. وعلى الرغم من أنَّك تستطيع كتابة شيفرات جيدة وبرامج مفيدة في أيّ إصدار منهما، لكن من المهم أن تفهم أنَّ هنالك بعض الاختلافات في بنية الشيفرات وفي طريقة تفسيرها. سأعرض هنا بعض الأمثلة، لكن عليك أن تعلم أنَّك ستواجه المزيد من الاختلافات أثناء مسيرة تعلمك لبايثون. print في بايثون 2، تُعامَل print كتعبيرٍ برمجيٍ بدلًا من كونها دالة، وهذا كان يثير ارتباكًا لأنَّ الكثير من الأمور داخل بايثون تتطلب تمرير وسائط (arguments) بين قوسين، إذا فتحتَ مُفسِّر بايثون 2 لطباعة «Sammy the Shark is my favorite sea creature»، فستكتب تعبير print الآتي: print "Sammy the Shark is my favorite sea creature" أما في بايثون 3، فستُعامَل print()‎ كدالة، لذا لطباعة السلسلة النصية السابقة، فيمكننا استخدام شكل استدعاء الدوال التقليدي كما يلي: print("Sammy the Shark is my favorite sea creature") هذا التعديل جعل من البنية اللغوية في بايثون موحدةً وسهَّلَ من التبديل بين مختلف دوال الطباعة فيها. يجدر بالذكر أنَّ الدالة print()‎ متوافقة مع بايثون 2.7، لذا ستعمل شيفرات بايثون التي تستعمل print()‎ بشكلٍ صحيحٍ في أيّ الإصدارَين. قسمة الأعداد الصحيحة في بايثون 2، أيُّ عددٍ تكتبه دون فواصل عشرية سيُعامَل على أنَّه من النوع integer، تأتي الإشكالية عندما تحاول قسمة الأعداد الصحيحة على بعضها، فتتوقع في بعض الأحيان حصولك على عددٍ عشري (تسمى أيضًا بالأعداد ذات الفاصلة العائمة float) كما في التعبير الرياضي: 5 / 2 = 2.5 لكن الأعداد الصحيحة في بايثون 2 لن تتحول إلى أعداد عشرية عندما تتطلب العملية التي تُجرى عليها ذلك. عندما يكون العددان الموجودان على جانبَي معامل القسمة / عددين صحيحين، فإن بايثون 2 ستجري عملية القسم وستُنتِج عددًا عشريًا إلا أنها ستُعيد العدد الصحيح الأصغر أو المساوي للناتج، وهذا يعني أنَّه لو كتبتَ ‎5 / 2 فستُعيد بايثون 2.7 العدد الصحيح الأصغر أو المساوي للعدد 2.5، وهو في هذه الحالة 2: a = 5 / 2 print a 2 لإعادة عدد عشري، فيجب إضافة فواصل عشرية إلى الأرقام التي ستُجري عليها عملية القسمة كما في ‎5.0 / 2.0 لكي تحصل على النتيجة المنطقية 2.5. أما في بايثون 3، فقسمة الأعداد الصحيحة أصبحت كما نتوقع: a = 5 / 2 print a 2.5 يمكنك استخدام ‎5.0 / 2.0 لإعادة 2.5، لكن إن أردتَ تقريب ناتج القسمة فاستخدم المعامل // الموجود في بايثون 3، كالآتي: a = 5 // 2 print a 2 هذا التعديل في بايثون 3 جعل من قسمة الأعداد الصحيحة أمرًا سهلًا، لكن هذه الميزة غير متوافقة مع بايثون 2.7. دعم محارف يونيكود عندما تتعامل لغات البرمجة مع السلاسل النصية (strings، والتي هي سلسلةٌ من المحارف)، فهي تفعل ذلك بطرائق مختلفة لكي تتمكن الحواسيب من تحويل الأعداد إلى أحرف ورموز. تستعمل بايثون 2 محارف ASCII افتراضيًا، لذا عندما تكتب "Hello, Sammy!‎" فستتعامل بايثون 2 مع السلسلة النصية كمجموعة من محارف ASCII، والتي هي محدودةٌ لحوالي مئتَي محرف، أي أنَّ محارف ASCII هي طريقة غير عملية لترميز المحارف خصوصًا المحارف غير اللاتينية (كالعربية مثلًا). لاستخدام ترميز محارف يونيكود (Unicode) الذي يدعم أكثر من 128000 محرف تابع للكثير من اللغات والرموز، فعليك أن تكتب u"Hello, Sammy!‎"‎ حيث تُشير السابقة u إلى Unicode. تستعمل بايثون 3 محارف يونيكود (Unicode) افتراضيًا، مما يوفِّر عليك بعض الوقت أثناء التطوير، ويمكنك كتابة وعرض عدد أكبر بكثير من المحارف في برنامجك بسهولة. يدعم يونيكود الكثير من المحارف بما في ذلك الوجوه التعبيرية (emojis)، واستعمالها كترميز محارف افتراضي يعني أنَّ الأجهزة المحمولة ستكون مدعومةً في مشاريعك تلقائيًا. إذا كنت تحب أنَّ تكون شيفرات بايثون 3 التي تكتبها متوافقةً مع بايثون 2، فأبقِ على حرف u قبل السلاسل النصية. استمرار التطوير الفارق الرئيسي بين بايثون 3 وبايثون 2 ليس في البنية اللغوية وإنما في أنَّ إصدار بايثون 2.7 سيتوقف دعمه في 2020، وسيستمر تطوير بايثون 3 بميزاتٍ جديدة وإصلاحٍ لمزيدٍ من العلل. التطويرات الأخيرة في اللغة تتضمن تخصيصًا أبسط لإنشاء الأصناف، وطريقةً أوضح للتعامل مع المصفوفات… الاستمرار بتطوير بايثون 3 يعني أنَّ المطورين يمكن أن يعتمدوا على اللغة، وسيطمئنون أنَّ المشاكل التي قد تحدث فيها ستُحَل في فترةٍ قريبة، ويمكن أن تصبح البرامج أكثر كفاءة بإضافة المزيد من الميزات للغة. نقاط أخرى يجب أخذها بالحسبان عليك أن تضع النقاط الآتية بعين الاعتبار عندما تبدأ مشوارك كمبرمج بلغة بايثون، أو عندما تبدأ بتعلم لغة بايثون بعد تعلمك لغيرها، وهنا ننصحك بتعلمها أكاديميًا باحترافية، من خلال الانضمام إلى دورة تطوير تطبيقات باستخدام لغة بايثون المقدمة من أكاديمية حسوب. إذا كنتَ تأمل بتعلم اللغة دون أن تفكِّر بمشروعٍ معيّن، فأنصحك بالتفكير بمستقبل بايثون، فسيستمر تطوير ودعم بايثون 3 بينما سيوقف دعم بايثون 2.7 عمّا قريب. أما إذا كنتَ تُخطِّط للانضمام لفريق تطوير أحد المشاريع، فعليك أن تنظر ما هو إصدار بايثون المستخدم فيه، وكيف يؤدي اختلاف الإصدار إلى اختلاف طريقة تعاملك مع الشيفرات، وإذا ما كانت المكتبات البرمجية المستعملة في المشروع مدعومةً في مختلف الإصدارات، وما هي تفاصيل المشروع نفسه… إذا كنت تُفكّر ببدء أحد المشاريع، فيجدر بك أن تنظر ما هي المكتبات المتوفرة وما هي إصدارات بايثون المدعومة. وكما قلنا سابقًا، الإصدارات الأوليّة من بايثون 3 لها توافقية أقل مع المكتبات المبنية لبايثون 2، لكن الكثير منها قد جرى تحويله إلى بايثون 3، وسيستمر ذلك في السنوات الأربع المقبلة. الخلاصة لغة بايثون كبيرة جدًا وموثقة توثيقًا ممتازًا وسهلة التعلم، ومهما كان اختيارك (بايثون 2 أو بايثون 3) فستتمكن من العمل على المشاريع الموجودة حاليًا. صحيحٌ أنّ هنالك بعض الاختلافات المحورية، لكن ليس من الصعب الانتقال من بايثون 3 إلى بايثون 2، وستجد عادةً أنَّ بايثون 2.7 قادرة على تشغيل شيفرات بايثون 3، خصوصًا في بدايات تعلمك للغة. من المهم أن تبقي ببالك أنَّ تركيز المطورين والمجتمع أصبح منصبًّا على بايثون 3، وسيصبح هذه اللغة رائدةً في المستقبل وستلبي الاحتياجات البرمجية المطلوبة، وأنَّ دعم بايثون 2.7 سيقل مع مرور الزمن إلى أن يزول في 2020. ترجمة -وبتصرّف- للمقال Python 2 vs Python 3: Practical Considerations لصاحبته Lisa Tagliaferri اقرأ أيضًا المقالة التالية: تثبيت بايثون على مختلف أنظمة التشغيل وإعداد بيئتها البرمجية المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون مميزات لغة بايثون
  9. أهلًا بك، يمكنك أن تسجل باستخدام حساب بي بال، أو أن تطلب من أحد معارفك ممن يملكون بطاقةً أن يشتروا لك بطاقة هدية لدورات أكاديمية حسوب. شكرًا لك.
  10. على الرحب والسعة، ركزت في مقالتي على منتجات حسوب لأيماني أنها أفضل المصادر لتعلم البرمجة باللغة العربية. وحتى أن دورات أكاديمية حسوب من أفضل الدورات عربيًا بل وتتوفق على الأجنبية في بعض النواحي. وكذلك الأمر بخصوص الموسوعة، فهي مرجع ممتاز يشرح المفاهيم بالعربية ويضع المقابلات الأجنبية وراء المصطلح العربي لمن أراد أن يستزيد من المصادر الأجنبية وتوثيقاتها مضبوطة ضبطًا ممتازًا وبعضها يتفوق على التوثيقات الأجنبية (أجرِ مقارنةً بسيطةً بين توثيق Sass العربي في الموسوعة وتوثيقها الأجنبي وستجد الفروق واضحةً أمامك). على الرحب والسعة، وأرجو لكما التوفيق كله.
  11. تتوفر عدِّة «أدوات بناء» (build tools) تعمل على تسهيل عملية تطوير مواقع الويب وأتمتة المهام الروتينية مثل «تصغير» (minify) ملفات JavaScript و HTML و CSS والصور، وتحويل ملفات LESS و SASS إلى CSS، والتحقق مباشرةً من صحة البنية اللغوية لملفات JavaScript والكثير من الأمور التي ستغيّر من طريقة عملك وتجعلها أكثر سرعةً وكفاءة. سنشرح في درسنا هذا إحدى أدوات البناء المشهورة وهي الأداة Gulp بالإضافة إلى أساسيات استخدامها، شرحًا مدعمَّا بالأمثلة العملية. ملاحظة مهمة: هذا الدليل كتب من أجل الإصدار 3 من Gulp وهنالك بعض الاختلافات والتغييرات الجوهرية في الإصدار 4. لذلك، إن كنت تستعمل الإصدار 4، فهذا الدليل ليس لك لأنه هنالك بعض الدوال والواجهات البرمجية التي لن تعمل مع ذلك الإصدار. ما هي أداة البناة Gulp؟ أدوات البناء هو أمر حديث العهد ولم ينتشر إلا منذ بضعة سنوات. كانت ملفات ‎.sh التي تكتب بلغة Bash أحد أشهر أدوات البناء غير المباشرة وغير المخصصة المستعملة آنذاك لكتابة التعليمات وأتمتة بعض المهام الروتينية المحدودة مثل مراقبة الملفات وتحديثها. هذه الملفات تعمل مع الصدفة shell التي تأتي مدمجة مع أنظمة التشغيل. انتشرت أدوات بناء أخرى مثل Ant و Gradle ولكنهما كانتا ضمن عالم جافا وتحتاجان إلى استعمال لغة XML لضبطهما وهي اللغة التي لا يحبها مطوري الويب عدا عن تعلم لغة جديدة لا يألفونها. اللغة التي يحبها مطورو الويب هي جافاسكربت وهنا جاءت أداتا البناء Gulp و Grunt لسد هذه الفجوة. تتفوق أداة البناء Gulp على منافستها Grunt بأنها أسرع بكثير لأنها تنفذ المهام على التوازي (على عكس Grunt) كما أنها تمرر الملفات المفتوحة عبر مجاري Streams داخلية. أضف إلى ذلك أن اللغة المستعملة في ضبط مهام Gulp هي جافاسكريبت بالتأكيد وهي اللغة التي يحبها مطورو الويب كثيرًا. المتطلبات المسبقة سيجري العمل على نظام التشغيل أوبنتو 18.04 ويجب أن تكون قد ثبَّت Node.js و npm مسبقًا. إن لم تكن Node.js مثبتةً لديك، فابدأ بتثبيتها هي ومدير الحزم npm عبر الأمر التالي: sudo apt update sudo apt install nodejs للتحقق من عملية التثبيت (وللتأكد من الإصدار المثبت مسبقًا لديك)، نفذ الأمر التالي: nodejs --version يجب أن ترى ناتجًا شبيهًا بالناتج التالي: v8.10.0 ولتكون قادرًا على تنزيل وتثبيت حزم npm، تحتاج إلى تنزيل npm (مدير حزم Node.js) إن لم يكن مثبَّتًا لديك: sudo apt install npm تحقق من عملية التثبيت (أو الإصدار المثبت مسبقًا) عبر الأمر التالي: npm --version يجب أن ترى ناتجًا شبيهًا بالناتج التالي: 3.5.2 أنت الآن جاهز لإكمال هذا الدليل! تثبيت Gulp يمكن تثبيت Gulp عبر مدير الحزم npm الذي تحدثنا عنه منذ قليل، بشكل سهلٌ للغاية فكل ما عليك فعله هو تنفيذ الأمر الآتي في سطر الأوامر لتثبيت Gulp 3 لعموم النظام: npm install gulp@3.9.0 ثم عليك تهيئة مجلد المشروع الذي ستعمل عليه. نفِّذ الأمر الآتي في مجلد المشروع: npm init عليك الآن تثبيت Gulp في المشروع وإضافته إلى الاعتماديات (أي devDependencies): npm install --save-dev gulp أصبح Gulp مثبتًا في مشروعك وجاهزًا للاستخدام. البنية الهيكلية للمشروع الذي سنعمل عليه مرونة Gulp تسمح لنا باستخدامه بغض النظر عن بنية المجلدات التابعة للمشروع، كل ما يلزمك هو فهمٌ صحيحٌ كاملٌ لآلية عمله مما يسمح لك بتعديله لتلبية احتياجاتك. سنعتمد في مقالتنا البنية الآتية للمجلدات: |- app/ |- css/ |- fonts/ |- images/ |- index.html |- js/ |- scss/ |- dist/ |- gulpfile.js |- node_modules/ |- package.json هذه بنيةٌ بسيطة نستعمل فيها مجلد app لاحتواء ملفات التطوير، بينما المجلد dist (اختصار للكلمة «distribution») سيحتوي على الملفات الإنتاجية. كتابة أول مهمة من مهام Gulp عندما نُشغِّل Gulp من سطر الأوامر، فسيبحث عن وجود الملف gulpfile.js في مجلد المشروع. سيُخبِر هذا الملف Gulp ما هي الإضافات التي يجب تحميلها وما هي المهام التي يجب القيام بها. علينا إذًا إنشاء الملف gulpfile.js وتضمين gulp في بدايته كما يلي: var gulp = require('gulp'); عبارة require السابقة ستخبر Node أن تبحث في مجلد node_modules عن حزمة باسم gulp، وعند العثور عليها فستُسنَد محتوياتها إلى المتغير gulp. يمكننا الآن البدء بكتابة «مهمّة Gulp» (أي Gulp task) بالاستفادة من المتغير gulp. البنية الأساسية لمهمّة Gulp هي: gulp.task('task-name', function() { // سنضع الشيفرات هنا }); task-name تشير إلى اسم المهمّة، والتي ستُستعمَل في أي مكانٍ تريد تشغيل هذه المهمّة فيه. نستطيع تشغيل هذه المهمة بتمرير اسمها كوسيط للأمر gulp في سطر الأوامر (أي تنفيذ gulp task-name). لنجرِّب ما سبق بإنشائنا للمهمّة hello في ملف gulpfile.js السابق (الذي يحتوي على عبارة require) والتي ستطبع الجملة Hello World: gulp.task('hello', function() { console.log('Hello World'); }); يمكننا تشغيل هذه المهمة بتنفيذ الأمر gulp hello في سطر الأوامر، لا تنسَ الانتقال إلى مجلد المشروع (عبر الأمر cd في لينكس أو ماك، أو dir في ويندوز) قبل تنفيذ الأمر. مخرجات الأمر السابق هي: user@linuxbox:~/gulp-test$ gulp hello [18:34:41] Using gulpfile ~/gulp-test/gulpfile.js [18:34:41] Starting 'hello'... Hello World [18:34:41] Finished 'hello' after 127 μs حسنًا، لا تكون مهام Gulp بهذه البساطة، حيث تحتوي عادةً على دالتين تابعتين للكائن gulp بالإضافة إلى استخدام إضافة (plugin) أو أكثر من إضافات Gulp. هذا مثالٌ عن البنية العامة لمهمّات Gulp: gulp.task('task-name', function () { //الحصول على الملفات التي يجب إجراء عمليات عليها // gulp.src باستخدام الدالة return gulp.src('source-files') .pipe(aGulpPlugin()) // ومن ثم تمريرها إلى إضافة .pipe(gulp.dest('destination')) // وإخراج الملف إلى المجلد الهدف }) كما هو واضح، تتطلب المهام المفيدة في gulp دالتين هما gulp.src و gulp.dest. الدالة gulp.src تخبر Gulp ما هي الملفات التي يجب تطبيق المهمة عليها، بينما الدالة gulp.dest تخبر Gulp أين يجب إخراج الملفات بعد تنفيذ المهمّة. يَستخدم Gulp المجاري (streams) التي توفرها node.js، وهذا يسمح بتمرير البيانات التي ستُعالَج عبر الأنابيب (pipes) وهذا ما تفعله الدالة ‎.pipe()‎؛ لشرحٍ تفصيليٍ عن المجاري في node.js، فأحيلك إلى هذه المقالة. سنشرح كيفية الاستفادة من Gulp عبر استعماله لتحويل ملفات Sass إلى ملفات CSS. تحويل ملفات Sass إلى CSS عبر Gulp نستطيع استخدام إضافة باسم gulp-sass لتحويل ملفات Sass إلى CSS. عليك تثبيت الإضافة gulp-sass في مشروعك باستخدام الأمر npm install كما فعلنا سابقًا مع gulp. يفضل أيضًا استخدام الخيار ‎--save-dev لكي تُضاف الحزمة gulp-sass إلى devDependencies في ملف package.json وهذا يسهل نسخ الملف إلى مشروع آخر وتثبيت نفس الاعتماديات (عبر الأمر npm install فقط): npm install gulp-sass –save-dev علينا الآن تضمين الحزمة gulp-sass عبر require من مجلد node_modules كما فعلنا أول مرة مع gulp كي نتمكن من استخدام الإضافة: var gulp = require('gulp'); // gulp-sass تضمين إضافة var sass = require('gulp-sass'); يمكننا استخدام gulp-sass بوضع الدالة sass()‎ بدلًا من aGulpPlugin()‎ في المثال السابق. ولمّا كان الغرض من المهمّة هو تحويل ملفات Sass إلى CSS فلنسمِّها sass: gulp.task('sass', function(){ return gulp.src('source-files') .pipe(sass()) // gulp-sass استخدام إضافة .pipe(gulp.dest('destination')) }); علينا توفير ملفات مصدرية بصيغة sass -ومجلد لإخراج الناتج فيه- إلى المهمّة sass، لنبدأ بإنشاء الملف styles.scss في مجلد app/scss، ومن ثم سنضع مسار هذا الملف في الدالة gulp.src في مهمّة sass. ونريد أيضًا إخراج ملف styles.css النهائي إلى مجلد app/css الذي سيكون هو مجلد الوجهة (destination) لدالة gulp.dest. gulp.task('sass', function(){ return gulp.src('app/scss/styles.scss') .pipe(sass()) .pipe(gulp.dest('app/css')) }); سنختبر المهمّة sass الآن للتأكد من عملها عملًا صحيحًا، لكن قبل ذلك علينا استخدام دالة من دوال Sass ضمن ملف styles.scss. // styles.scss .testing { width: percentage(5/7); } إذا نفَّذتَ الأمر gulp sass في سطر الأوامر، فيجب أن يُنشَأ الملف styles.css في مجلد app/css، مع تبديل الدالة percentage(5/7)‎ وتحويلها إلى 71.42857%‎. /* styles.css */ .testing { width: 71.42857%; } تحققنا أنَّ المهمّة sass تعمل بشكلٍ سليم. أحيانًا نحتاج إلى بناء أكثر من ملف ‎.scss وتحويلها إلى ملفات CSS في نفس الوقت، وسنحتاج حينها إلى استخدام «محارف التحديد» (Globs). محارف التحديد في Node تسمح لك محارف التحديد بتمرير أكثر من ملف إلى الدالة gulp.src، وهي شبيهة بالتعابير النمطية (regular expressions) لكنها تُستعمَل خصيصًا لمسارات الملفات. عند استخدامك لمحرف تحديد (glob) فسيتحقق جهازك من أسماء الملفات والمسارات المُحدَّدة بالنمط المستخدم، فإن تمت مطابقة النمط فسيُضاف الملف إلى الدالة gulp.src. أغلبية الأنماط التي نستعملها مع Gulp تنضوي تحت لواء الأنماط الآتية: النمط ‎*.scss: رمز النجمة * هو محرفٌ خاصٌ يُطابِق أيّة ملف في المجلد المعيّن. وفي مثالنا ستُطابَق جميع الملفات التي تنتهي باللاحقة ‎.scss في المجلد الرئيسي للمشروع. النمط ‎**/*.scss: هذه حالةٌ أعم من المثال السابق، حيث ستتم مطابقة أيّة ملفات تنتهي باللاحقة ‎.scss في المجلد الرئيسي للمشروع وفي جميع المجلدات الفرعية الموجودة فيه. النمط ‎!not-me.scss: الرمز ! يعني أنَّ على Gulp استثناء هذا النمط من الملفات المُحدَّدة، وهذا مفيدٌ إن شئتَ أن تستثني ملفًا من التحويل؛ وفي مثالنا سنستثني الملف not-me.scss. النمط ‎*.+(scss|sass): إشارة الزائد + والأقواس ()‎ ستسمح لأداة Gulp بمطابقة عدِّة أنماط معًا، والتي سيُفصَل بينها بمحرف الخط العمودي |. وفي مثالنا سيُطابِق Gulp جميع الملفات التي تنتهي باللاحقة ‎.scss أو ‎.sass في المجلد الرئيسي للمشروع. بعد تعلمنا لمحارف التحديد، أصبح بإمكاننا وضع التعبير app/scss/**/*.scss بدلًا من app/scss/styles.scss، وبهذا ستُطابَق جميع الملفات التي لها اللاحقة ‎.scss في المجلد app/scss أو أيّ مجلدٍ فرعيٍ موجودٍ داخله. gulp.task('sass', function() { // scss الحصول على جميع الملفات التي تنتهي باللاحقة // app/scss والموجودة في المجلد return gulp.src('app/scss/**/*.scss') .pipe(sass()) .pipe(gulp.dest('app/css')) }) سيُضمَّن أيّ ملف Sass موجود في مجلد app/scss تلقائيًا في مهمّة sass المُعدَّلة. فلو أضفتَ ملف print.scss إلى المشروع، فستلاحظ توليد الملف print.css في مجلد app/css تلقائيًا. حسنًا، تمكّنا من تحويل جميع ملفات Sass إلى ملفات CSS بأمرٍ وحيد، لكن السؤال الآن هو: ما الفائدة من المهمّة التي أنشأناها إن كنا سنحتاج إلى تطبيق الأمر gulp sass يدويًا في كل مرة نرغب فيها بتحويل ملفات Sass إلى CSS؟ لحسن الحظ، يمكننا أن نخبر Gulp أن يُشغِّل المهمّة sass تلقائيًا في كل مرة يُحفَظ فيها أحد تلك الملفات، وهذا ما ندعوه «المراقبة» (watching). مراقبة التغييرات في ملفات Sass يوفِّر لنا Gulp الدالة watch لمعرفة إن حُفِظَ أحد الملفات. الشكل العام لدالة watch هو: gulp.watch('files-to-watch', ['tasks', 'to', 'run']); إذا أردنا مراقبة جميع ملفات Sass وتشغيل المهمّة sass عندما يحفظ أحد تلك الملفات، فعلينا أن نضع app/scss/**/*.scss بدلًا من files-to-watch ونضع ['sass'] بدلًا من ['tasks', 'to', 'run']: gulp.watch('app/scss/**/*.scss', ['sass']); وفي أغلبية الأوقات سنحتاج إلى مراقبة أكثر من نوع من الملفات في آنٍ واحد، ويمكننا إنشاء مجموعة من عمليات المراقبة مع بعضها ضمن مهمّة باسم watch: gulp.task('watch', function(){ gulp.watch('app/scss/**/*.scss', ['sass']); // عمليات المراقبة الأخرى }) إذا جرّبتَ تنفيذ الأمر gulp watch الآن، فسترى أنَّ Gulp قد بدأ مراقبة الملفات فوريًا. user@linuxbox:~/gulp-test$ gulp watch [21:10:00] Using gulpfile ~/gulp-test/gulpfile.js [21:10:00] Starting 'watch'... [21:10:00] Finished 'watch' after 12 ms وأنَّ Gulp سيُنفِّذ المهمّة sass عندما تحفظ أحد ملفات ‎.scss، جرِّب الآن فتح الملف styles.scss السابق وإضافة أيّ شيء إليه وحفظه، ثم انظر إلى ناتج Gulp: abd@linuxbox:~/gulp-test$ gulp watch [21:10:00] Using gulpfile ~/gulp-test/gulpfile.js [21:10:00] Starting 'watch'... [21:10:00] Finished 'watch' after 12 ms [21:12:03] Starting 'sass'... [21:12:03] Finished 'sass' after 69 ms ما رأيك أن نخطو خطوةً إلى الأمام ونجعل Gulp يُعيد تحميل الصفحة عندما نحفظ أحد ملفات ‎.scss لكي يظهر تأثير تغيير الصفحة مباشرةً على المتصفح، وذلك بالاستعانة بأداة Browser Sync. تحديث الصفحة آنيًا باستخدام Browser Sync يُسهِّل Browser Sync من تطوير الويب بإنشاء خادوم ويب الذي يساعدنا على تحديث الصفحات آنيًا عند تغييرها. وله ميزاتٌ أخرى، مثل مزامنة الأحداث بين أكثر من جهاز. لنبدأ أولًا بتثبيت الأداة Browser Sync كالمعتاد: npm install browser-sync –save-dev علينا بعدئذٍ أن نُضمِّن Browser Sync عبر التعليمة require: var browserSync = require('browser-sync').create(); سنحتاج إلى إنشاء مهمّة باسم browserSync للسماح لأداة Gulp بتشغيل خادوم ويب باستخدام Browser Sync، ولأننا سنُنشِئ خادوم ويب، فعلينا أن نُحدِّد للخادوم ما هو المجلد الرئيسي للموقع، والذي هو في حالتنا المجلد app: gulp.task('browserSync', function() { browserSync.init({ server: { baseDir: 'app' }, }) }) علينا أيضًا تغيير المهمّة sass قليلًا لكي يتمكن Browser Sync من تحديث أنماط CSS في المتصفح عندما تُشغَّل المهمّة sass: gulp.task('sass', function() { return gulp.src('app/scss/**/*.scss') .pipe(sass()) .pipe(gulp.dest('app/css')) .pipe(browserSync.reload({ stream: true })) }); انتهينا الآن من ضبط الأداة Browser Sync، لكن علينا تشغيل المهمّتَين watch و browserSync في نفس الوقت لكي تُحدَّث الصفحة آنيًا. من غير المقبول فتح نافذَتي سطر أوامر وتشغيل gulp browserSync في إحداها و gulp watch في الأخرى، لذا لنجعل Gulp يشغِّلهما معًا بإخبار المهمّة watch أنَّه يجب إكمال المهمّة browserSync قبل السماح بتنفيذ watch. يمكننا فعل ذلك بتمرير وسيطٍ ثانٍ إلى المهمّة watch. الشكل العام هو: gulp.task('watch', ['array', 'of', 'tasks', 'to', 'complete','before', 'watch'], function (){ // ... }) وفي حالتنا هذه سنضيف المهمّة browserSync: gulp.task('watch', ['browserSync'], function (){ gulp.watch('app/scss/**/*.scss', ['sass']); // عمليات المراقبة الأخرى }) علينا أيضًا أن نتأكد أنَّ المهمّة sass ستعمل قبل watch لكي يتم تحويل أيّة ملفات sass حُفِظَت قبل تشغيل Gulp إلى ملفات CSS. gulp.task('watch', ['browserSync', 'sass'], function (){ gulp.watch('app/scss/**/*.scss', ['sass']); // عمليات المراقبة الأخرى }); إذا شغَّلتَ gulp watch في سطر الأوامر، فسيُشغِّل Gulp المهمّتين sass و browserSync ثم بعد إكمالهما ستُشغَّل المهمّة watch. ستجد في الناتج رابط URL شبيه بالرابط الآتي http://localhost:3000 الذي إذا فتحته في نافذة المتصفح، فسترى صفحة app/index.html التي يجب أن يكون محتواها شبيهًا بما يلي: <!doctype html> <html> <head><link rel="stylesheet" href="css/styles.css"></head> <body></body> </html> شغِّل الآن المتصفح وادخل إلى الرابط السابق وستعرض أمامك صفحة فارغة، ولتجربة التحديث الآني للصفحة، فافتح ملف styles.scss وأضف لونًا للخلفية (مثلًا: body {background-color: red;} واحفظ الملف وستجد أنَّ الصفحة قد حُدِّثَت تلقائيًا! ما رأيك الآن أن نضيف قليلًا على المهمّة السابقة ونراقب تغيير ملفات HTML أو JavaScript (بالإضافة إلى ملفات ‎.sass) ومن ثم إعادة تحميل الصفحة حينها؟ يمكننا فعل ذلك بإضافة عمليتَي مراقبة، واستدعاء الدالة browserSync.reload عند حفظ الملف: gulp.task('watch', ['browserSync', 'sass'], function (){ gulp.watch('app/scss/**/*.scss', ['sass']); // تحديث الصفحة عند حفظ هذه الملفات gulp.watch('app/*.html', browserSync.reload); gulp.watch('app/js/**/*.js', browserSync.reload); }); حتى الآن قمنا بثلاثة أمور: تشغيل خادوم ويب للتطوير تحويل ملفات Sass إلى CSS إعادة تحميل الصفحة في المتصفح عند حفظ الملفات سنشرح في القسم الآتي كيفية تحسين الملفات الملحقة بصفحات الويب، وسنبدأ بتحسين ملفات CSS و JavaScript. تحسين ملفات CSS و JavaScript يُجري المطورون مهمّتَين عندما يحاولون تحسين ملفات CSS و JavaScript: تصغير الملفات وجمعها في ملفٍ واحد. إحدى المشاكل التي يواجهها المطورون عند أتمتة هذه العملية هي الصعوبة في جمع السكربتات بترتيبٍ صحيح. لنقل أننا أضفنا 3 وسوم script في صفحة index.html: <body> <script src="js/lib/a-library.js"></script> <script src="js/lib/another-library.js"></script> <script src="js/main.js"></script> </body> هذه السكربتات موجودة في مجلدين مختلفين، ومن الصعب جمعها باستخدام الإضافات التقليدية مثل gulp-concatenate؛ لكن لحسن الحظ، تأتي إضافة gulp-useref لتحل لنا هذه الإشكالية. تجمع إضافة gulp-useref أيّ عدد من ملفات CSS أو JavaScript إلى ملفٍ وحيد بالبحث عن تعليق يبدأ بالتعبير ‎<!--build:‎ وينتهي بالتعبير <‎!--endbuild--‎> الشكل العام له هو: <!-- build:<type> <path> --> ... HTML Markup, list of script / link tags. <!-- endbuild --> النوع <type> يمكن أن يكون إما js أو css أو remove. من الأفضل تحديد نوع type للملفات التي تحاول جمعها؛ وإذا ضَبطتَ type إلى remove فسيَحذف Gulp الشيفرةَ المُحدَّدةَ ولن يولِّد ملفًا. أما <path> فيشير إلى مسار الملف الذي سيولّد. إذا أردتَ أن يكون ملف JavaScript المولَّد في مجلد js باسم main.min.js فستبدو الشيفرة كالآتي: <!--build:js js/main.min.js --> <script src="js/lib/a-library.js"></script> <script src="js/lib/another-library.js"></script> <script src="js/main.js"></script> <!-- endbuild --> لنضبط الآن إضافة gulp-useref في ملف gulpfile.js. علينا أولًا تثبيت الإضافة ثم تضمينها في الملف. نفِّذ الأمر الآتي للتثبيت: npm install gulp-useref –save-dev أضف السطر الآتي إلى ملف gulpfile.js: var useref = require('gulp-useref'); تهيئة المهمّة useref شبيهة بتهيئة المهام الأخرى التي أنجزناها من قبل. هذه هي الشيفرة: gulp.task('useref', function(){ return gulp.src('app/*.html') .pipe(useref()) .pipe(gulp.dest('dist')) }); إذا شغّلتَ مهمّة useref الآن فسيأخذ Gulp ملفات JavaScript المُحدَّدة وسيجمعها في ملف dist/js/main.min.js. لكن الملف لم يُصغَّر (minified) إلى الآن، وعلينا استخدام إضافة gulp-uglify لفعل ذلك. وسنحتاج أيضًا إلى إضافة تدعى gulp-if لكي نُصغّر ملفات JavaScript دونًا عن غيرها. تثبيت الإضافة: npm install gulp-uglify --save-dev الشيفرة التي سنضيفها إلى ملف gulpfile.js: var uglify = require('gulp-uglify'); var gulpIf = require('gulp-if'); gulp.task('useref', function(){ return gulp.src('app/*.html') .pipe(useref()) // JavaScript التصغير إذا كان الملف .pipe(gulpIf('*.js', uglify())) .pipe(gulp.dest('dist')) }); يجب أن يولِّدَ Gulp الملفَ main.min.js في كل مرة تشغِّل فيها المهمّة useref. إحدى الأمور الرائعة التي لم أخبرك عنها بعد هي أنَّ إضافة gulp-useref ستحوِّل جميع السكربتات الموجودة بين ‎<!--build:‎ و <‎!--endbuild--‎> إلى ملف JavaScript وحيد الذي يُشير إلى js/main.min.js وذلك في صفحة index.html، أي أنَّ ملف index.html الناتج سيكون شبيهًا بما يلي: <!doctype html> <html> <head></head> <body> <script src="js/main.min.js"></script> </body> </html> سنستعمل نفس الطريقة لجمع ملفات CSS: <!--build:css css/styles.min.css--> <link rel="stylesheet" href="css/styles.css"> <link rel="stylesheet" href="css/another-stylesheet.css"> <!--endbuild--> يمكننا أيضًا تصغير ملف CSS الناتج، لكننا بحاجة إلى تثبيت الإضافة gulp-cssnano: npm install gulp-cssnano الشيفرة التي سنضيفها إلى ملف gulpfile.js: var cssnano = require('gulp-cssnano'); gulp.task('useref', function(){ return gulp.src('app/*.html') .pipe(useref()) .pipe(gulpIf('*.js', uglify())) // CSS التصغير إذا كان الملف .pipe(gulpIf('*.css', cssnano())) .pipe(gulp.dest('dist')) }); ستحصل الآن على ملف CSS وملف JavaScript وحيد ومُصغّر في كل مرة تُشغِّل فيها المهمّة useref. تحسين دعم المتصفحات لخاصيات CSS وشيفرة JavaSctipt نبدأ بتحسين دعم خاصيات CSS عبر مختلف المتصفحات. فمن المؤكد أنك رأيت شيفرة CSS مكتوبة بالشكل التالي: .navigation { display: -webkit-box; display: -ms-flexbox; display: flex } ولابد أنك استصعبت - أثناء كتابة شيفرة CSS - البحث عن دعم كل خاصية من خاصيات CSS لمختلف المتصفحات وتساءلت عن وجود أداة تضيف السوابق الخاصة بدعم الخاصيات في المتصفحات الأخرى مثل ‎-webkit-box و ‎-ms-flexbox. الحل بسيط جدًا وهو استعمال الإضافة gulp-autoprefixer الموجودة لهذا الغرض. نفذ الأمر التالي لتثبيت هذه الإضافة: npm install gulp-autoprefixer --save-dev أضف السطر الآتي إلى ملف gulpfile.js: const autoprefixer = require('gulp-autoprefixer'); تهيئة واستعمال هذه الإضافة - مع الإضافة gulp-sass والإضافة browser-sync التي تحدثنا عنهما في الأعلى - يكون بالشكل التالي: gulp.task("styles", function() { gulp .src("app/css/**/*.scss") .pipe(sass().on("error", sass.logError)) .pipe(autoprefixer({ // دعم آخر إصدارين للمتصفح browsers: ["last 2 versions"] }) ) .pipe(gulp.dest("css")) .pipe(browserSync.stream()); }); نحصل بذلك على شيفرة CSS السابقة عند كتابة: .navigation { display: flex } فقط لتصبح الشيفرة مدعومة على كافة المتصفحات التي لا تدعم خاصيات CSS محدَّدة مثل التي رأيناها للتو. ملاحظة: إن أردت استعمال الإضافة gulp-autoprefixer مع الإضافة gulp-cssnano (وإضافات أخرى)، فيمكنك استعمالهما سوية عبر استعمال الإضافة gulp-postcss. تمرر هذه الإضافة شيفرة CSS إلى عدة إضافات مع تحليل الشيفرة مرةً واحدةً مما يزيد من سرعة تنفيذ العملية. var postcss = require('gulp-postcss'); var gulp = require('gulp'); var autoprefixer = require('autoprefixer'); var cssnano = require('cssnano'); gulp.task('css', function () { // CSS تحديد الإضافات المراد استعمالها مع شيفرة var plugins = [ autoprefixer({browsers: ['last 2 version']}), cssnano() ]; return gulp.src('app/css/*.css') .pipe(postcss(plugins)) .pipe(gulp.dest('./dest')); }); اطلع على توثيق الإضافة الرسمي لمزيد من الأمثلة وكيفية الاستخدام. ننتقل الآن إلى تحسين دعم شيفرة JavaScript وأتحدث الآن عن دعم ميزات ES6 عبر مختلف المتصفحات. الحقيقة أن الميزات التي يوفرها الإصدار ES6 (أو ECMAScript 6) مغرية جدًا لجميع مطوري الويب ولكن نقص الدعم في مختلف المتصفحات والإصدارات القديمة هو من أكبر العقبات أمام الاستفادة من تلك الميزات. انطلاقًا من ذلك، جاءت فكرة وجود محول يدعى transpiler والذي يحول من لغة برمجية معينة إلى برمجية أخرى (في حالتنا من ECMAScript 6 إلى ES2015 أو ما قبلها). المحول الذي سنختاره هنا هو المحول الشهير Babel JS الذي يحوي الكثير من الميزات والمدعوم بقوة من قبل المجتمع. سنستعمل الإضافة gulp-babel لهذا الغرض. ثبت الإضافة عبر الأمر التالي: # Babel 6 npm install --save-dev gulp-babel # Babel 7 npm install --save-dev gulp-babel@next @babel/core أضف الشيفرة التالية إلى ملف gulpfile.js: var babel = require("gulp-babel"); gulp.task("default", function () { return gulp.src("app/js/*.js") // تحويل الشيفرة إلى الإصدار الأقدم .pipe(babel()) .pipe(gulp.dest("dist")); }); تنبيه: احرص على استدعاء babel()‎ قبل إجراء أية عملية تصغير أو ضغط على شيفرة JavaScript. تحسين الصور أظن أنك ستتوقع أننا سنحتاج إلى تثبيت إضافة لمساعدتنا في موضوع تحسين الصور، وهي gulp-imagemin. الضغط الذي ستوفره هذه الإضافة هو الضغط غير الفَقُود (lossless compression). npm install gulp-imagemin –save-dev يمكننا تصغير صورة png و jpg و gif وحتى svg باستخدام إضافة gulp-imagemin. لننشِئ مهمّة images لهذا الغرض: var imagemin = require('gulp-imagemin'); gulp.task('images', function(){ return gulp.src('app/images/**/*.+(png|jpg|gif|svg)') .pipe(imagemin()) .pipe(gulp.dest('dist/images')) }); ولأن كلُّ نوعٍ من أنواع الصور سيُحسّن بطريقةٍ مختلفة، فربما ترغب بإضافة بعض الخيارات إلى imagemin لتخصيص كيفية تحسين الصورة. على سبيل المثال، يمكننا إنشاء صورة GIF متداخلة (interlaced) بضبط قيمة الخيار interlaced إلى ture. gulp.task('images', function(){ return gulp.src('app/images/**/*.+(png|jpg|jpeg|gif|svg)') .pipe(imagemin({ interlaced: true })) .pipe(gulp.dest('dist/images')) }); إن كانت تريد إجراء عملية ضغط فقود (lossy compression)، والذي سيخفّض حجم الصورة بشكل كبيرة على حساب الجودة، فاستعمل الإضافة imagemin-pngquant. npm install imagemin-pngquant –save-dev خيار الضغط الذي توفره هذه الإضافة يتمتع بالذكاء والذي يدعى PNG quantization (توضيح صور PNG)، إذ يعمل على اللعب بالألوان التي يراها دماغنا على أنها تقريبًا متماثلة محولًا الصورة إلى حجم 256 أو 8 بت للألوان. أفضل شيء في هذه الإضافة هو أنها لن تعدل أي شيء على الصور إن لم تتحقق حدود معينة متعلقة بالجودة. سنعدل على المثال السابق لاستعمال pngquant()‎ مع imagemin بالشكل التالي: var imagemin = require('gulp-imagemin'); gulp.task('default', function() { return gulp.src('app/images/*') .pipe(imagemin({ progressive: true, use: [pngquant()] })) .pipe(gulp.dest('dist/images')); }); على أي حال، عملية تحسين الصور هي عمليةٌ بطيئةٌ للغاية، ولا ترغب في تكرارها إلا إذا كان ذلك ضروريًا، ويمكننا تخزينها مؤقتًا باستخدام إضافة gulp-cache. npm install gulp-cache –save-dev الشيفرة التي سنضيفها إلى ملف gulpfile.js: var cache = require('gulp-cache'); gulp.task('images', function(){ return gulp.src('app/images/**/*.+(png|jpg|jpeg|gif|svg)') // تخزين الصور المُحسّنة مؤقتًا .pipe(cache(imagemin({ interlaced: true }))) .pipe(gulp.dest('dist/images')) }); انتهينا تقريبًا من عمليات التحسين، وبقي مجلدٌ أخير يجب علينا نقله من مجلد app إلى dist وهو مجلد الخطوط. لمزيد من التفاصيل حول تحسين الصور في موقعك، ننصحك بالإطلاع على مقال «دليلك الشامل لتحسين أداء الصور على موقعك». نسخ الخطوط إلى مجلد dist لن تحتاج الخطوط إلى أيّة عمليات تحسين، وكل ما علينا فعله هو نسخها إلى مجلد dist. يمكننا نسخ الملفات باستخدام Gulp ببساطة باستخدام الدالتين gulp.src و gulp.dest دون أيّة إضافات: gulp.task('fonts', function() { return gulp.src('app/fonts/**/*') .pipe(gulp.dest('dist/fonts')) }) سينسخ Gulp مجلد fonts من app إلى dist في كل مرة تشغِّل فيها الأمر gulp fonts. أصبحت لدينا ست مهام مختلفة في ملف gulpfile.js، ويجب علينا استدعاؤها يدويًا باستخدام سطر الأوامر. ربما نفكّر بربط المهام جميعًا إلى أمرٍ وحيد، لكن قبل ذلك لننظر إلى كيفية حذف الملفات المولّدة تلقائيًا. حذف الملفات المولدة تلقائيًا لمّا كنّا نولِّد الملفات تلقائيًا، فعلينا أن نتأكد أنَّ الملفات التي لم تعد مستخدمةً لن تبقَ موجودةً دون علمنا. سنحتاج إلى استخدام del لمساعدتنا في ذلك. npm install del –save-dev الدالة del تأخذ مصفوفةً من محارف التحديد (globs) التي تخبرها ما هي المجلدات التي يجب حذفها. يمكننا استخدامها كمهمّة في Gulp كما فعلنا من قبل: var del = require('del'); gulp.task('clean:dist', function() { return del.sync('dist'); }) سيَحذف Gulp المجلد dist في كل مرة تُشغِّل فيها الأمر gulp clean:dist. ملاحظة: لا حاجة أن نقلق من حذف مجلد dist/images لأنَّ إضافة gulp-cache خزَّنت نسخةً مؤقتةً من الصور في نظامك. ستحتاج إلى إنشاء مهمّة منفصلة لحذف النسخة المؤقتة وليكن اسمها cache:clear: gulp.task('cache:clear', function (callback) { return cache.clearAll(callback) }) جمع مهام Gulp مع بعضها ما فعلناه إلى الآن هو إنشاء مجموعتين منفصلتين من مهمات Gulp. الغرض من المجموعة الأولى هو المساعدة في التطوير، حيث حوّلنا ملفات Sass إلى CSS، وراقبنا تغيرات الملفات، وأعدنا تحميل الصفحة في متصفح الويب وقت الحاجة. أما المجموعة الثانية فكانت لتحسين الملفات الملحقة بالصفحة، حيث جهّزنا جميع الملفات للموقع الإنتاجي، وذلك بدمج ملفات CSS و JavaScript وتصغيرها، وتحسين الصور ونسخ الخطوط من app إلى dist. لقد وضعنا أول مجموعة من المهمات في بُنية يمكن تشغيلها باستخدام الأمر gulp watch: gulp.task('watch', ['browserSync', 'sass'], function (){ // ... }) أما المجموعة الثانية فتستخدم لإنشاء الموقع الإنتاجي، وهي تتضمن المهمات clean:dist و sass و useref و images و fonts. يمكننا إنشاء مهمة باسم build التي ستجمع كل ما سبق في مهمة واحدة. gulp.task('build', [`clean`, `sass`, `useref`, `images`, `fonts`], function (){ console.log('Building files'); }) للأسف لا يمكننا كتابة المهمّة build بهذه الطريقة لأن Gulp سيشغِّل المهمات المُمرَّرة كوسيطٍ ثانٍ إلى الدالة task معًا وليس بالترتيب. فهنالك احتمالٌ أن ينتهي تنفيذ المهمات useref أو images أو حتى fonts قبل إكمال المهمّة clean التي تُسبِّب حذف كامل مجلد dist! لذا لضمان أنَّ المهمات ستُنفَّذ بترتيبٍ صحيح، فسنحتاج إلى استخدام إضافة خارجية باسم Run Sequence: npm install run-sequence –save-dev وهذه هي البنية العامة لطريقة تشغيل سلسلة من المهمّات: var runSequence = require('run-sequence'); gulp.task('task-name', function(callback) { runSequence('task-one', 'task-two', 'task-three', callback); }); عندما تستدعى المهمّة task-name فسيُشغِّل Gulp المهمّة task-one أولًا، وبعد انتهاء تنفيذها سيُشغِّل المهمة task-two، ثم task-three وهكذا. تسمح لك إضافة Run Sequence بتشغيل المهمات معًا إذا وضعتَها في مصفوفة: gulp.task('task-name', function(callback) { runSequence('task-one', ['tasks','two','run','in','parallel'], 'task-three', callback); }); في هذه الحالة، سيُشغِّل Gulp المهمّة task-one، وبعد انتهاء تنفيذها فسيُشغِّل جميع المهمات الموجودة في المصفوفة معًا، وجميع المهمات الموجودة في المصفوفة يجب أن ينتهي تنفيذها قبل تنفيذ task-three. لذا يمكننا الآن إنشاء مهمة التي تُشغِّل المهمّة clean:dist أولًا، ثم تتبعها بقية المهمات: gulp.task('build', function (callback) { runSequence('clean:dist', ['sass', 'useref', 'images', 'fonts'], callback ) }) ولجعل الملف متناسقًا، فسنستخدم نفس الطريقة مع أوّل مجموعة من المهمات، وسنستخدم الاسم default كاسمٍ للمهمّة: gulp.task('default', function (callback) { runSequence(['sass','browserSync', 'watch'], callback ) }) لماذا اخترنا الاسم default؟ لأنّ المهمّة ذات الاسم default ستُشغَّل إن كَتبتَ الأمر gulp دون تمرير اسم المهمة له، مما يوفِّر عليك بعض الوقت :-) . الخلاصة بدأنا درسنا ونحن لا نعرف شيئًا عن Gulp، ثم كتبنا مهمةً بسيطةً لتحويل ملفات Sass إلى CSS ومراقبة ملفات HTML و JS، وتعلمنا كيف نشغِّل تلك المهمّة في سطر الأوامر باستخدام الأمر gulp. ثم بنينا بعد ذلك مهمّةً ثانيةً، ألا وهي build، والتي تُنشِئ المجلد dist لكي يحتوي على ملفات الموقع الإنتاجي، وحوّلنا ملفات Sass إلى CSS وأجرينا عمليات تحسين على ملحقات الصفحة، ونسخنا المجلدات الضرورية إلى مجلد dist؛ وتمكنّا من تنفيذ كل ما سبق بأمرٍ وحيد وهو gulp build. في النهاية، أنشأنا المهمّة clean التي تحذف المجلد dist وبالتالي سنحذف أيّة ملفات غير مستخدمة في الموقع. لا تقف عند هذا الحد! Gulp واسع جدًا وإمكانياته لا تنتهي، وأرى أنَّ عليك تصفّح الإضافات الخاصة به والموجودة في الموقع الرسمي، وعليك الاستعانة بتوثيق API إن أردت رؤية توثيق دوال Gulp. المصادر تعتمد هذه المقالة اعتمادًا أساسيًا على مقالة Gulp for Beginners لصاحبها Zell Liew قائمة بالمقالات التي تتحدث عن Gulp المجاري (streams) في Node مقالة Automate Your Tasks Easily with Gulp.js لصاحبها Justin Rexroad
  12. وفرنا في أكاديمية حسوب دليلًا للمبتدئين لتعلم البرمجة بالتفصيل، يمكنك الاطلاع عليه من الرابط الآتي: https://academy.hsoub.com/programming/general/تعلم-البرمجة-r662/
  13. "أريد تعلم البرمجة لكنني لا أعرف من أين أبدأ!" هذه هي أكثر عبارة تتردد على سمعي من حديثي العهد بالبرمجة، إذ يأتيني هذا السؤال مرارًا وتكرارًا؛ وفي كل مرة أحاول أن أجيب عنه في سياقه، أجد أنني أضيف معلومات جديدة على إجاباتي السابقة، لذا قررت كتابة هذا المقال بعنوان "تعلم البرمجة" لعله يفيد الراغبين في تعلم تطوير التطبيقات في بدء رحلتهم مع تعلم البرمجة من الصفر. جدول المحتويات حرصًا على تنظيم المقالة ولتسهيل الوصول إلى القسم الذي تريده بسهولة، سنذكر هنا جدول المحتويات باختصار: ما هي البرمجة؟ لماذا تتعلم البرمجة؟ ما عليك معرفته لتصبح مبرمجًا الأدوات اللازمة للبدء في تعلم البرمجة لماذا هناك العديد من لغات البرمجة؟ مفاهيم البرمجة مصادر تعلم البرمجة تطوير واجهات المستخدم تطوير الواجهات الخلفية تعلم تطوير تطبيقات الجوال تطوير الألعاب تطوير الأنظمة المدمجة تطوير تطبيقات سطح المكتب كيفية اختيار لغة البرمجة التي تناسبك نصائح لتعلم البرمجة ما هي البرمجة؟ البرمجة هي عملية تقسيم مهمة معينة يراد تنفيذها عبر الحاسوب إلى أجزاء صغيرة ومترابطة وقابلة للتنفيذ بأوامر بسيطة. بعد ذلك، تجري كتابة هذه الأوامر والتعليمات بإحدى لغات البرمجة، والتي هي وسيلة للتخاطب مع الحاسوب. إليك المثال العملي التالي الذي يشرح ماهية البرمجة: إن كنت تتوقع زيارة صديق لك اليوم، واتصل بك ليقول لك: "أنا واقف بجانب الحديقة ولا أعرف كيف أصل إلى منزلك". أنت عادةً تمر كل يوم من جانب الحديقة وتعرف الطريق بينها وبين منزلك شبرًا بشبر. برأيك هل ينفع إن قلت له: "منزلي معروف وقريب من الحديقة وأنا كل يوم أمر من جانبها"؟ لا، بالتأكيد. تحتاج إلى أن تقسِّم المشكلة إلى أجزاء تمثل خطوات بسيطة يستطيع صديقك فهمها وتنفيذها. مثلًا، أخبره أن ينفذ الأوامر التالية: "سر إلى الأمام عشرة أمتار" ثم "اتجه إلى اليمين" ثم "سر إلى نهاية الشارع" ثم "اتجه إلى اليسار". أخبره بعد ذلك: "عُدَّ الأبنية الموجودة على اليسار حتى تصل إلى البناء الرابع" ثم "اصعد إلى الطابق الثاني" ثم "اطرق على الباب الذي سيظهر أمامك". مبارك! بهذه الطريقة، تستطيع أن تدل صديقك على منزلك بدقة. البرمجة هي الشيء نفسه تمامًا. فهل ترى التعابير المكتوبة بين علامتي الاقتباس؟ إنها التعابير التي تكتب بإحدى لغات البرمجة والتي تخاطب الحاسوب بدلًا من صديقك السابق. لغات البرمجة هي مجموعة من المفردات والقواعد اللغوية التي تشكل لغةً وسيطةً للتخاطب مع الحاسوب وأمره بتنفيذ تعليمات وأشياء محدَّدة. فلا الحاسوب يفهم لغة البشر ولا البشر يفهمون لغة الحاسوب، لذا كان هنالك حاجة ملحة لوجود لغة وسيطة يفهمها كلاهما؛ نتيجةً لذلك، انبثق مفهوم لغة البرمجة. بعبارة أخرى، لو أردنا أن نقول للحاسوب "افعل كذا"، فسنحتاج إلى لغةٍ مشتركةٍ بيننا وبينه ليفهم ما نبتغيه، وهنا يأتي دور لغات البرمجة، إذ يمكنك أن تعدّ لغات البرمجة على أنها وسيط بين المبرمج والحاسوب. يهتم المبرمج بالتفكير في تسلسل الخطوات التي على الحاسوب القيام بها لإتمام العمل المطلوب منه (مثل حساب العمر اعتمادًا على تاريخ الولادة)، ثم كتابة هذه الخطوات بترتيب منطقي بإحدى لغات البرمجة. ربما لاحظتَ في الجملة السابقة أن جزءًا من مهمة المبرمج هو التفكير المنطقي، وهذا يجعلنا ننتقل إلى السؤال الشائع "هل أستطيع تعلم البرمجة وأصبح مبرمجًا؟" أو "هل أنا مؤهل لأصبح مبرمجًا؟". لماذا تتعلم البرمجة؟ يبدو أن تعلم البرمجة من الصفر ليس بالصعوبة التي توقعتها، لكنك تريد حافزًا يجعلك تتعلم البرمجة. تسمع كثيرًا أن البرمجة هي مجال المستقبل، وأن وظائف المبرمجين ستكتسح مجال التوظيف في السنوات القادمة؟ أستطيع أن أؤكد لك ذلك، كما أنَّ وظائف البرمجة هي من أعلى الوظائف دخلًا. فلو كنت تريد بدء مشوارك الاحترافي وتريد عملًا مستقرًا وذا دخلٍ ممتاز، فإن تعلم البرمجة والعمل بها هو أفضل خيارٍ أمامك. وظائف البرمجة مريحة عمومًا، فالعمل كله مكتبي أمام حاسوب في بيئة مريحة ومناسبة، وأغلبية الشركات تتبع نظام العمل 40 ساعة في الأسبوع (أي 5 أيام لمدة 8 ساعات يوميًا)، ولا تغفل عن قدرتك على العمل عن بعد من خلال الانترنت أو كمستقل في أوقات فراغك. تعلم البرمجة سيوسع أفق تفكيرك كثيرًا، خصوصًا أن تعاملك مع الحاسوب يتبع إلى التفكير المنطقي، وستجد أن البرمجة ستسهل لك القيام بأمور أخرى في الحاسوب. ما عليك معرفته لتصبح مبرمجًا يتردد الكثيرون في تعلم البرمجة متذرعين بأن مستواهم في الرياضيات ليس ممتازًا، وهذا ليس صحيحًا، فصحيحٌ أنَّ هنالك أمور تعترضك أثناء أداء عملك كمبرمج تتطلب خبرة في الرياضيات، إلا أنَّه قد تمر عليك فترات طويلة لا تحتاج فيها إلى مسائل رياضية. كل ما يلزمك للبدء في تعلم البرمجة هو الأساسيات التي يعرفها الجميع. إلى حين اعتراضك أية مسألة أو مشكلة تتطلب مهارة في الرياضيات، هنالك الكثير من المصادر والمراجع التي تستطيع الرجوع إليها آنذاك. بعبارة أخرى، أجِّل هذا الأمر قليلًا ولا تخف. الأهم من ذلك هو أن تكون قادرًا على التفكير بشكل منطقي. التفكير المنطقي التفكير المنطقي هو المهارة التي تجمع كافة المبرمجين تحت مظلة واحدة، وهي أساس كتابة الخوارزميات، إذ يجب أن تكون قادرًا على اكتساب هذه المهارة وتطويرها. الخوارزميات كلمة "الخوارزميات" هي الكلمة المرعبة التي ينفر منها البعض، فكل ما يتخيلونه عند ذكرها هو الرياضيات المعقدة والمعادلات الطويلة والرموز العجيبة، لكن الأمر بسيط جدًا؛ فالخوازرميات هي تطبيقٌ للتفكير المنطقي في خطوات متسلسلة واضحة تمامًا لحل مشكلة ما. لكي أوضِّح لك أن الخوارزميات ليست أمرًا معقدًا، سأخبرك بكيفية كتابة برنامج يسأل المستخدم عن سنة ميلاده، ثم يعيد عمره الحالي بالسنوات. الخطوة الأولى: إظهار رسالة نصية نطلب فيها من المستخدم إدخال تاريخ ميلاده. الخطوة الثانية: تخزين سنة الميلاد التي أدخلها المستخدم. الخطوة الثالثة: الحصول على السنة الحالية. الخطوة الرابعة: طرح مدخلات المستخدم من السنة الحالية. الخطوة الخامسة والأخيرة: إظهار الناتج. ما سبق هو خوارزمية بسيطة تتألف من خطوات متسلسلة، لكن إذا أمعنّا النظر فيها سنجد خللًا في حال أدخل المستخدم تاريخًا أكبر من التاريخ الحالي، أي لو أدخل 2050 مثلًا بدلًا من 1995. عندها سيصبح العمر المعاد من الخوارزمية سالبًا، ويمكننا أن نحل هذه الإشكالية منطقيًا بوضع شرط يمنع المستخدم من إدخال تاريخ أكبر من التاريخ الحالي. إطارات العمل كلمة أخرى شائعة جدًا في عالم البرمجة هي "إطارات العمل" frameworks، إطارات العمل هي مجموعة من الشيفرات البرمجية التي تسهل على المبرمج إنشاء التطبيقات، بتوفير وحدات جاهزة تقدم خدمات متنوعة مثل تسجيل المستخدمين، وإرسال البريد الإلكتروني، والتعامل مع قواعد البيانات. أي يمكنك أن تعدّها أدوات برمجية تساعدك في برمجة تطبيقك وتسهِّل لك فعل ذلك. الأدوات اللازمة للبدء في تعلم البرمجة تحتاج إلى حاسوبٍ بمواصفات جيدة (ليس من الضروري أن يكون من أفضل الحواسيب، وإنما أن يمتلك مقدارًا جيدًا من الذاكرة العشوائية). لا ننصح بمواصفات معينة أو نظام تشغيل معين، استعمل ما يحلو لك وما ترى نفسك معتادًا عليه (سواءً كان ويندوز أو لينكس أو ماك). ستحتاج أيضًا إلى اتصالٍ جيد بالإنترنت للوصول إلى المواد التعليمية، ولتنزيل البرمجيات والمكتبات اللازمة للتطوير. أما بخصوص أدوات التطوير، فستحتاج إلى برمجية لكتابة الشيفرات، وهنالك نوعان رئيسيان لها: المحررات النصية: مثل Visual Studio Code أو Atom أو Sublime Text أو Bracktes أو Notepad++‎. وهذه المحررات النصية تكون بسيطة في أغلبها، وتوفر ميزات أساسية مثل تلوين الشيفرات، وبعض ميزات الإكمال التلقائي، وتدعم أغلبيتها إضافات لزيادة وظائفها. وظيفة هذه المحررات النصية عمومًا هي تعديل الشيفرات بسهولة وسرعة. ننصحك بتجربة Visual Studio Code لشهرته حاليًا وكثرة إضافاته ودعمه الممتاز من شركة Microsoft. بيئات التطوير المدمجة: مثل Visual Studio و Eclipse و Android Studio و NetBeans و Apple Xcode وغيرها. وهذه البيئات توفر ميزات أكثر بكثير من المحررات النصية، مثل تشغيل الشيفرات وتنقيحها (debugging) وميزات التحكم بالإصدارات (version control) والاتصال بقواعد البيانات وخلاف ذلك. لماذا هناك العديد من لغات البرمجة؟ قد تتساءل، لماذا هناك العديد من لغات البرمجة؟ أليست هذه اللغات كلها تنفذ الهدف ذاته؟ لماذا لا يكون هنالك لغة موحدة بين المبرمجين والحاسوب؟ الحقيقة أنّه توجد لغة برمجة واحدة ولكن ليست إحدى اللغات التي تراها أمامك في الصورة. اللغة التي نشير إليها هي لغة الآلة التي يستطيع معالج الحاسوب قراءتها وفهمها. أتتساءل ما هي لغة الآلة وكيف تبدو؟ إليك مقطعًا منها: معلومٌ أنّ معالج الحاسوب لا يفهم شيئًا سوى الأصفار والواحدات، وهذه اللغة -أي لغة الآلة- هي تمثيل للأصفار والواحدات بطريقة تخبر الحاسوب بما يجب عليه فعله. الجدير بالذكر أن هذه اللغة عصية الفهم على البشر، وحتى إن استطعت كتابة شيفرة مثل الشيفرة الموضحة بالصورة (كما في السنوات الأولى من بداية اختراع الحاسوب)، لن يفهمها الآخرون ولن يستطيع أحد التعديل على الشيفرة وتطويرها لاحقًا باستثنائك. سعيًا لإيجاد لغة قريبة من لغة البشر، انقسمت لغات البرمجية إلى قسمين: لغات البرمجة منخفضة المستوى، ولغات البرمجة عالية المستوى وذلك تبعًا لمدى قربها من لغة الآلة أو لغة البشر على التوالي. أي أنّ لغات البرمجة منخفضة المستوى هي اللغات الأقرب للغة الآلة آنفة الذكر مثل لغة التجميع Assembly language، ولغات البرمجة عالية المستوى هي اللغات الأقرب للغة البشر مثل لغة بايثون وجافا. تنفيذ البرامج المكتوبة بلغات برمجة عالية المستوى يقودنا الحديث عن اللغات عالية المستوى واللغات منخفضة المستوى إلى الحديث عن كيفية تنفيذ المعالج للشيفرة المكتوبة بلغة عالية المستوى لا يفهمها المعالج (أليس هذا ما تفكر به الآن؟). عرفنا أن المعالج يفهم الأوامر والتعليمات المكتوبة بلغة منخفضة المستوى (لغة الآلة)، فمَثَلُ هذه العملية كمَثَلِ شخصٍ أجنبي تعلم اللغة العربية وبدأ التحدث مع ناطقٍ باللغة العربية، إذ يمكن لهما التواصل مباشرةً - ليخبر كل منها ما يريد من الآخر فعله - دون وسيط. أمَّا مَثَلُ كتابة برنامج بلغة عالية المستوى أقرب إلى لغة البشر والطلب من الحاسوب تنفيذه كمثل ناطق باللغة الهندية يريد التخاطب مع ناطق باللغة العربية دون أن يفقه أحدهما لغة الآخر. في هذه الحالة، لن يستطيع أحدهما فهم ما يتكلم به الآخر وستفشل عملية التواصل. قد تقول: لماذا لا يحضران مترجمًا يترجم ما يقوله كل منها للآخر؟ حسنًا، هذا ما يحصل تمامًا عندما يراد تنفيذ برنامج بلغة لا يفهمها معالج الحاسوب. في اللغات البشرية، هنالك نوع واحد من المترجمين يعرفه الجميع للترجمة من لغة إلى آخرى؛ أما في لغات البرمجة، هنالك نوعان من المترجمين بين اللغات هما: المفسر interpreter، والمترجم compiler. بناءً على ذلك، تنقسم لغات البرمجة إلى لغات مفسرة ولغات مترجمة. (من الآن وصاعدًا، كلما ذكرنا لغات البرمجة، فنحن نشير إلى لغات البرمجة عالية المستوى.) -المفسر (interpreter): وهو برنامج خاصٌ يفسِّر الشيفرة المصدرية لبرنامج مكتوب بلغة عالية المستوى سطرًا بسطر ويحولها إلى لغة منخفضة المستوى لينفذها الحاسوب مباشرةً. -المترجم (compiler): وهو برنامج خاصٌ يحوِّل الملفات المصدرية لبرنامج مكتوب بلغة عالية المستوى إلى ملف تنفيذي مكتوب بلغة الآلة دفعةً واحدةً، ثم يمكن تشغيل الملف التنفيذي على الحاسوب للقيام بالمهمة المطلوبة. لماذا يوجد الكثير من لغات البرمجة عالية المستوى؟ الآن وبعد أن عرفت الفرق بين لغة الآلة ولغة البشر، لربّما ما زلت تتساءل عن كثرة اللغات البرمجية عالية المستوى المتوافرة وعدم وجود لغة واحدة. نستطيع القول أنك خطوت خطوةً جيدةً للأمام إذ أصبحت الآن أكثر دقة. جواب سؤلك هو أنّ كل لغات البرمجة تُستخدم لتحويل فكرة منطقية إلى سلسلة أوامر يمكن للحاسوب أن ينفذها. فعلى سبيل المثال لا الحصر يمكنك استخدام أي من Ruby أو Java أو Python أو C#‎ أو Go أو JavaScript لبناء موقع ويب. لكن يمكنك أن تعدّ لغات البرمجة على أنها أدوات، وكل أداة تسهّل مهمة دونًا عن أخرى. فعلى سبيل المثال، السيارة والحافلة والدراجة والمحراث الزراعي كلها وسائط نقل، لكنها مختلفة الاستخدام؛ فلا يمكنك أن تذهب وعائلتك لقضاء إجازة صيفية مستخدمين المحراث الزراعي، كما لا يمكنك استخدام سيارة سباق في مدينة مكتظة ذات شوارع ضيقة للذهاب بها إلى العمل. مع أن آلية عمل هذه المركبات متشابهة. والأمر سيانٌ بالنسبة إلى البرمجة. خلاصة القول أنّ هنالك لغات برمجة متخصصة بإنشاء تطبيقات سطح المكتب، وأخرى متخصصة بإنشاء تطبيقات الجوال، وأخرى تستعمل خصيصًا لمواقع الويب، وأخرى لبرمجة العتاد، وهذا ما يحيلنا إلى الحديث عن مجالات البرمجة واللغات الأنسب لكلٍ منها. مفاهيم البرمجة "حسنًا، اقتنعتُ أن البرمجة مناسبة لي وليست صعبة كما كنتُ أتخيل، من أين أبدأ طريقي في تعلم البرمجة إذًا؟" قبل الإجابة عن السؤال السابق، سآخذ وقتي لأشرح لك بعض المفاهيم الخاصة بالبرمجة، ثم سنتحدث عن مجالات العمل فيها وما المسار الأفضل لتعلمك كلًا منها. أنت تعلم أن البرنامج هو سلسلة أوامر ينفذها الحاسوب لحل مشكلة ما، والبرنامج نفسه مكتوب بلغة يفهمها الحاسوب تسمى لغة الآلة. من الأمور الملحوظة التركيز كثيرًا على لغة البرمجة ذاتها أثناء بداية تعلم البرمجة. سأخبرك حقيقةً صادمةً: "لغة البرمجة التي تستعملها ليست بتلك الأهمية التي تتوقعها"، أنا لا أقول لك أن جميع لغات البرمجة متماثلة أو تُستعمل لنفس الاستعمالات، لكن لا تركِّز كثيرًا على تعلم كيفية الكتابة في لغة برمجة ما وتهمل المفاهيم البرمجية التي تقف وراءها. المتغيرات والثوابت عليك أن تتعرف على مفهوم المتغيرات variables المستعمل في جميع لغات البرمجة، والذي يعني إسناد قيمة ما إلى رمز أو كلمة وتخزين هذه القيمة في الذاكرة. فلو أردنا أن نخزن العبارة "Hello World" في متغير ما فنكتب شيئًا شبيهًا بما يلي: var variable_name = "Hello World"; أي أننا نسند الجزء الموجود على يمين إشارة المساواة إلى المتغير المذكور على يسار إشارة المساواة. يمكننا أن نستنتج من اسم "المتغيرات" أن قيمتها قابلة للتغيير خلال تنفيذ البرنامج، فيمكننا في مكانٍ ما من الملف المصدري أن نعيد تعريف المتغير السابق بكتابة: var variable_name = "New value"; أما الثوابت فهي تتشابه مع المتغيرات في كثيرٍ من النواحي، إلا أنك لا تستطيع إعادة تعريف قيمتها بعد تعريفها أول مرة. قد تستفيد من الثوابت عندما تكون متأكدًا تمامًا من عدم تغيير القيمة خلال فترة تنفيذ البرنامج. فلو أردنا تعريف ثابت اسمه pi يحتوي على القيمة 3.14 (والتي سنعرف أنها لن تتغير مطلقًا)، فيمكننا أن نكتب: const pi = 3.14; وإذا حاولتَ تغيير قيمة الثابت بعد تعريفه فستحصل على رسالة خطأ. الشروط تدعم جميع لغات البرمجة تعريف شروط تُنفَّذ في حالات معينة. ففي الخوازرمية السابقة التي شرحنا فيها حساب العمر، يمكننا أن نكتب الشرط بالعربية كما يلي: إذا كان (تاريخ الميلاد أكبر من التاريخ الحالي): نقول للمستخدم أن هنالك خطأ وإلا: سنحسب العمر بطرح تاريخ الميلاد من التاريخ الحالي وإذا أردنا كتابتها بإحدى لغات البرمجة فستبدو شبيهةً بما يلي: if ( user_birth > current_year ) { // ERROR! } else { age = current_year - user_birth; } لا تلقِ للأقواس بالًا، فهي جزء من لغة البرمجة، وقد تختلف من لغة لأخرى، وليست موضع اهتمامنا حاليًا. حلقات التكرار ماذا لو كانت لدينا قاعدة بيانات فيها أكثر من مستخدم ولكل مستخدم تاريخ ميلاد. لا تقل لي سنأخذ التواريخ يدويًا وندخلها إلى البرنامج! هذا مضيعةٌ للوقت، والصواب هو إنشاء حلقة تكرار تأخذ قيمة تاريخ الميلاد الخاص بكل مستخدم ثم تحسب عمره كما أسلفنا في القسم السابق. دعنا نعدل الخوارزمية البسيطة لنضيف تكرارًا فيها: ما أجمل البرمجة! تخيل لو كان عندك ألف مستخدم، وكان عليك حساب أعمارهم، يمكنك بضغطة زر أن تحسبها كلها. الدوال الدالة function هي مجموعة من التعليمات البرمجية التي تقبل مدخلات وتعيد القيمة المطلوبة. تكون الدوال عادةً قصيرةً وتقوم بمهمة وحيدة فقط. فمثلًا لو أردنا تعريف دالة باسم divide تقبل عددين، وتعيد ناتج قسمة العدد الكبير على الصغير، فيمكننا أن نكتب الخورزمية الآتية: مصادر تعلم البرمجة للمبتدئين أول ما سيتبادر إلى ذهنك بعد قرارك تعلم البرمجة هو من أين سأتعلم؟ هنا يأتي دور القسم التعليمي المتكامل في حسوب ليقدم للمبتدئ (والمحترف على حدٍ سواء) محتوى علمي مميز ومبسط. تزخر أكاديمية حسوب بالمحتوى المفيد عن تعلم البرمجة للمبتدئين وحتى الخبراء أي على كافة المستويات، ستجد فيها أقسامًا تشرح لغات البرمجة وتقنياتها كلها. ولدينا قسم للأسئلة البرمجية التي يمكنك أن تطرح فيه سؤالك وسيجيب عليه أحد أفراد مجتمع أكاديمية حسوب. أضف إلى ذلك أن الأكاديمية توفر قسمًا للدورات المتخصصة التي تبدأ معك من الصفر وحتى احتراف لغة البرمجة التي تريد تعلمها مع كادر من المدربين المختصين الذي يقدمون لك المساعدة ويجيبون عن جميع استفساراتك. وهنالك قناة للأكاديمية على يوتيوب ننشر فيها دوريًا دروسًا قصيرةً عن تساؤلات محددة ومفاهيم البرمجة وخلافه. لا تنسَ الاشتراك في قناة الأكاديمية لتصلك الفيديوهات الجديدة. ماذا لو أردتَ التعمق أكثر في لغة معيّنة؟ تأتي هنا موسوعة حسوب التي توفِّر توثيقًا عربيًا كاملًا وعالي الجودة، مدعّمًا بالأمثلة لمختلف لغات البرمجة وتقنيات تطوير الويب والجوال. ستكون الموسوعة مرجعًا تعود إليه في مسيرتك البرمجية، وتستعين بها لمعرفة التفاصيل الدقيقة عن لغات البرمجة. فأنت لست مضطرًا لحفظ كل شيء في لغة البرمجة، إذ حتى المبرمجون المختصون ذوو الخبرة يعودون إلى التوثيقات بين الفينة والأخرى أثناء عملهم. لننطلق الآن للتحدث عن مجالات البرمجة الأساسية وما اللغات والتقنيات المستعملة فيها. تطوير واجهات المستخدم يبدأ أغلبية المطورين مشوارهم من خلال تعلم تطوير واجهات المستخدم عند اتخاذ قرارهم لدخول مجال تطوير وبرمجة مواقع الويب، وذلك لسهولة اللغات المستعملة في هذا المجال. هدف هذا المجال هو تطوير صفحات الويب التي تعرض محتوى مختلف مواقع الويب، وهي الصفحات التي تراها عند زيارتك لموقع أكاديمية حسوب أو موسوعة حسوب أو مستقل أو أي موقع آخر. تتألف صفحة الويب من مجموعة من المكونات، وتُكتَب هذه المكونات باستخدام لغة HTML، وبعد كتابة البنية الهيكلية للصفحة سنأتي على تنسيقها باستخدام لغة CSS، وهي اللغة المستعملة لإضفاء شكل وهيئة على عناصر HTML. أي أن عناصر HTML تصف محتوى الصفحة (مثل الترويسات والقوائم والمحتوى الرئيسي والفقرات والروابط والصور والفيدوهات)، وقواعد CSS تُعرِّف كيف يجب أن تبدو هذه العناصر (سواءً من ناحية الألوان أو المساحات أو الخلفيات أو الخطوط أو خلاف ذلك). تأتي لغة JavaScript مكملةً لهما وتستعمل لإعطاء بعض عناصر الصفحة صفاتٍ تفاعلية، مثل شريط متحرك من الصور أو قوائم تظهر عند وقوع حدث معيّن …إلخ. هنالك تقنيات كثيرة تستعمل في تسهيل إنشاء الواجهات الأمامية وسنذكر بعضها: إطار Bootstrap لتسهيل تنسيق عناصر الصفحة. مكتبة jQuery لتسهيل التعامل مع عناصر الصفحة باستخدام JavaScript. مكتبة React JS: لتسهيل تطوير واجهات المستخدم بالاعتماد على مكونات قابلة لإعادة الاستخدام. لغة Sass لإنشاء ملفات CSS بسرعة وسلاسة. أدوات بناء مثل Webpack الذي يسهِّل تحويل الملفات المصدرية للتطبيق إلى النسخة النهائية التي ستعرَض للمستخدم. لتعلم تطوير واجهات المستخدم، ننصحك بالتسجيل في دورة تطوير واجهات المستخدم المقدمة من أكاديمية حسوب، والتي تحتوي على أكثر من 72 ساعة فيديو تتوزع على ثمانية مسارات تعليمية تشرح أمثلة عملية تطبيقية شرحًا مفصلًا. أثناء مشاهدتك للدورة، يمكنك أن تعود إلى موسوعة حسوب لتتعرف على توثيق لغات البرمجة المذكورة، وذلك للاطلاع على تفاصيل وأمثلة أكثر عن كل جزئية من الجزئيات المشروحة في دورة تطوير واجهات المستخدم. اللغات والتقنيات المستخدمة في تطوير واجهات المستخدم: HTML و CSS و JavaScript و Bootstrap و Sass و jQuery و Webpack. تطوير الواجهات الخلفية قد تتساءل: ماذا يعني تطوير الواجهات الخلفية (backend)؟ وما الفرق بينه وبين تطوير واجهات المستخدم (frontend)؟ الفرق بينهما هو أن الواجهات الخلفية هي البرمجيات التي تُنفَّذ على الخوادم وتجري عمليات عليها مثل التعامل مع قواعد البيانات والملفات والخدمات الخارجية، أما واجهات المستخدم فهي الصفحات التي تظهر على شاشة الزائر في متصفحه. سأطرح عليك الخيارات المتاحة أمامك للبدء في مجال تطوير الواجهات الخلفية، وجميع اللغات المذكورة هنا هي لغات ناجحة وقوية ولا يهم أي لغة تختار منها، المهم أن تتطلع على شيفرات بسيطة من كل لغة وتتخذ قرار تعلمها، واحذر من تضييع وقتك في التنقل بين لغات البرمجة والبحث عن أفضلها، فكلُ لغةٍ ممتازةٌ في مجالها. تعلم البرمجة باستخدام لغة PHP بعد تبيان الفرق بين واجهات المستخدم والواجهات الخلفية، يمكن القول بأن أشهر لغة لتطوير الواجهات الخلفية هي لغة PHP، وتتفوق على اللغات المنافسة لها أضعافًا مضاعفة. تعلم البرمجة بلغة PHP أمر سلس، فهي لغة سهلة التعلم وبسيطة الشكل، والمجتمع حولها كبير وتطويرها مستمر. هذه اللغة هي خيار استراتيجي لمن يريد الدخول إلى مجال تطوير الواجهات الخلفية. هنالك عدد من البرمجيات المكتوبة بلغة PHP مثل ووردبريس WordPress ودروبال Drupal وميدياويكي MediaWiki (التي تشغِّل ويكيبيديا وموسوعة حسوب) وغيرها الكثير؛ إضافةً إلى عددٍ كبير من إطارات العمل مثل Laravel و CodeIgniter و Symfony و CakePHP و Yii وغيرها، وهذا ما يدل على إمكانيات اللغة الكبيرة والمجتمع الكبير الذي يحيط بها. لتعلم تطوير الواجهات الخلفية باستخدام PHP، ننصحك بالتسجيل في دورة تطوير تطبيقات الويب باستخدام PHP المقدمة من أكاديمية حسوب، والتي تحتوي على 75 ساعة فيديو تتوزع على اثني عشر مسارًا تعليميًا تبدأ بأساسيات لغة البرمجة PHP للمبتدئين، مرورًا بشرح أمثلة عملية تطبيقية بالتفصيل، ووصولًا لتطوير التطبيقات باستخدام إطار العمل Laravel، وشرح تطوير ووردبريس. أثناء مشاهدتك للدورة، يمكنك أن تعود إلى موسوعة حسوب للاطلاع على توثيق لغة PHP وإطار العمل Laravel. اللغات والتقنيات المستخدمة في تطوير تطبيقات الويب باستخدام PHP هي: PHP و Laravel وقواعد البيانات (مثل MySQL و PostgreSQL وغيرها). تعلم البرمجة باستخدام لغة روبي - Ruby إذا كنتَ تبحث عن لغةٍ أنيقة وسهلة الاستعمال فستجد ضالتك في لغة روبي Ruby فهي من أجمل اللغات وأسلسها كتابةً، وهي لغة برمجة عامة يمكن استخدامها لتطوير مختلف أنواع التطبيقات ومن ضمنها تطوير تطبيقات الويب. ذاع صيت روبي في تطوير الويب بعد نشر إطار العمل Ruby on Rails (يشار إليه اختصارًا "ريلز"). هنالك إطارات عمل أخرى مثل سيناترا Sinatra لكن يبقى ريلز أشهرها. لتعلم تطوير الواجهات الخلفية باستخدام روبي، ننصحك بالتسجيل في دورة تطوير تطبيقات الويب باستخدام روبي المقدمة من أكاديمية حسوب، والتي تحتوي على 20 ساعة فيديو تتوزع على أربعة مسارات تعليمية تشرح أمثلة عملية تطبيقية شرحًا مفصلًا، وتشرح تطوير التطبيقات باستخدام إطار العمل ريلز. أثناء مشاهدتك للدورة، يمكنك أن تعود إلى موسوعة حسوب للاطلاع على توثيق لغة روبي وإطار العمل ريلز. اللغات والتقنيات المستخدمة في تطوير تطبيقات الويب باستخدام روبي: روبي و ريلز وقواعد البيانات (مثل MySQL و PostgreSQL وغيرها). تعلم البرمجة باستخدام لغة جافا سكربت - JavaScript نعم! تستعمل JavaScript في تطوير الواجهات الخلفية أيضًا. الفضل يعود لبيئة Node.js التي تسمح للمطورين باستخدام JavaScript لكتابة برمجيات تعمل من جهة الخادم وذلك لتوليد صفحات ويب ديناميكية قبل إرسالها إلى المتصفح، وتستطيع Node.js التعامل مع الملفات وقواعد البيانات ومختلف أنظمة الشبكات وخدمات أنظمة التشغيل. هل يوجد أجمل من استخدام نفس اللغة لبرمجة الواجهات الأمامية لمواقع الويب والواجهات الخلفية؟ وكل ذلك باستخدام لغة سهلة التعلم والاستعمال ومدعومة دعمًا ممتازًا من المجتمع. تعلم لغة JavaScript لتطوير الواجهات الخلفية من خلال التسجيل في دورة تطوير التطبيقات باستخدام JavaScript المقدمة من أكاديمية حسوب، والتي تحتوي على 69ساعة فيديو تتوزع على أحد عشرمسارًا تعليميًّا تشرح أمثلة عملية تطبيقية شرحًا مفصلًا، وتشرح تطوير الواجهة الخلفية باستخدام Node.js. أثناء مشاهدتك للدورة، يمكنك أن تعود إلى موسوعة حسوب للاطلاع على توثيق لغة JavaScript وبيئة العمل Node.js. اللغات والتقنيات المستخدمة في تطوير تطبيقات الويب باستخدام JavaScript: لغة JavaScript وبيئة Node.js وإطار العمل Express.js وقواعد البيانات (مثل MongoDB و MySQL و PostgreSQL وGraphQL وغيرها). تعلم البرمجة باستخدام لغة بايثون - Python لغة بايثون متعددة الاستعمالات، ويمكن عدّها على أنها أسهل لغة برمجة على الإطلاق، إذ تبدو شيفرتها البرمجية كأنها مقالة مكتوبة باللغة الإنكليزية. إذا أردتَ لغةً سهلةً ومدعومةً دعمًا ممتازًا ولها أطر عمل كثيرة فأنت تبحث عن لغة بايثون. الخيارات المتاحة أمامك هي إطار العمل جانغو (Django) وفلاسك (Flask) وغيرها، يمكنك تعلم لغة البرمجة بايثون لتطوير الواجهات الخلفية من خلال قراءة سلاسل المقالات عن تعلم بايثون في قسم البرمجة في أكاديمية حسوب، ثم الانتقال إلى تعلم إطار العمل جانغو أو فلاسك. يمكنك أن تعود إلى موسوعة حسوب للاطلاع على توثيق لغة بايثون. تعلم لغة بايثون لتطوير الواجهات الخلفية من خلال التسجيل في دورة تطوير التطبيقات باستخدام Python المقدمة من أكاديمية حسوب، والتي تحتوي على 69 ساعة فيديو تتوزع على عشرةمسارات تعليمية تشرح أساسيات لغة بايثون للمبتدئين، ثم تطبق عمليًا بأمثلة واقعية، وتشرح إطار العمل جانغو Django وفلاسك Flask. اللغات والتقنيات المستخدمة في تطوير تطبيقات الويب باستخدام بايثون: لغة بايثون وإطارات العمل المبنية عليها (مثل جانغو وفلاسك) وقواعد البيانات (مثل MySQL و PostgreSQL وغيرها). تعلم تطوير تطبيقات الجوال ازداد عدد تطبيقات الجوال لأنظمة أندرويد و iOS ازديادًا كبيرًا في الفترة الماضية، وأصبح لكل شركة أو خدمة تطبيق خاص بها يسهِّل على مستخدميها الوصول إلى الخدمات التي توفرها. النظامان الرئيسيان المسيطران على سوق الجوال حاليًا هما أندرويد ثم iOS. يمكن برمجة تطبيقات أندرويد بلغة Java أو Kotlin (أو غيرهما) وبرمجة تطبيقات iOS باستخدام Swift (وغيرها). ستكتشف أنَّ عليك تطوير تطبيقين منفصلين تمامًا، واحد لهواتف أندرويد وآخر لهواتف iOS، وذلك يسبب زيادةً في حجم العمل المطلوب وصعوبةً في إدارة التغييرات. بسبب ذلك، ظهر مفهوم "التطبيقات متعددة المنصات"، وهي تطبيقات تعمل على نظام أندرويد و iOS دون أي تعديلات، وذلك باستخدام تقنيات مشتركة وهي في الغالب تقنيات الويب. أي أصبح بإمكان مطوري الويب الاستفادة من معلوماتهم في تطوير تطبيقات الجوال باستخدام منصات مثل آيونيك Ionic. تسمح آيونيك Ionic للمبرمجين بالتعامل مع مختلف وظائف الجهاز باستخدام لغة JavaScript، مثل الوصول إلى الموقع الجغرافي، والتقاط صور بالكاميرا، والتعامل مع الملفات وخلاف ذلك. طوِّرت في الفترة الماضية تقنيات أخرى مبنية على JavaScript مثل React Native المبنية على مكتبة React.js والتي تسمح للمطورين بكتابة تطبيقات أصيلة باستخدام تقنيات الويب. تستطيع تعلم تطوير تطبيقات الجوال عبر Ionic وعبر React Native من خلال التسجيل في دورة تطوير التطبيقات باستخدام لغة JavaScript المقدمة من أكاديمية حسوب، والتي تحتوي على 69 ساعة فيديو تشرح أمثلة عملية تطبيقية شرحًا مفصلًا. وكالعادة يمكنك أثناء مشاهدتك للدورة أن تعود إلى موسوعة حسوب للاطلاع على التوثيقات اللازمة. اللغات والتقنيات المستخدمة في تطوير تطبيقات الجوال: Java و Swift و Kotlin و Ionic و React Native وغيرها. تطوير الألعاب تطوير الألعاب هو المجال الذي يحلم جميع مستخدمي الحاسوب بالدخول إليه. فالأغلبية تعرفوا على الحاسوب من خلال ألعاب الفيديو ومن ثم بدؤوا برحلة الاستكشاف عن البرمجة والتطوير. أغلب من يجيب عن تطوير الألعاب يقول "عليك بتعلم لغة C++‎" لكن دعني أفصِّل لك الأمر قليلًا. برمجة الألعاب تتطلب عملًا كثيرًا من فريق عمل كبير، مدعوم من شركة تجارية. من الصعب على مطوِّر وحيد أن ينشئ لعبة كاملة من الصفر دون فريق. تُطور أغلبية الألعاب باستخدام محرِّك ألعاب Game Engine والذي يسهِّل الأمر على المطورين ويتيح بيئة تطوير مناسبة للألعاب، ويتيح الميزات الأساسية لجميع الألعاب مثل التحكم بالكاميرا ونمذجة الشخصيات ثلاثية الأبعاد وتحريكها والأمور الفيزيائية الأخرى. هنالك عدد كبير من محركات تطوير الألعاب، ومن المرجح أنك شاهدت شعارها في الألعاب التي لعبتها من قبل، ومن أشهرها: Unreal Engine و Unity و Godot. يمكن التعامل مع هذه المحركات باستخدام عدِّة لغات، مثل C++‎ (وهي أشهرها)، وجافا (خصوصًا للألعاب على هواتف أندرويد) وحتى يمكن استخدام JavaScript في التعامل مع بعضها. تذكر أنّ الألعاب غير محدودة بتطبيقات سطح المكتب أو الهواتف، فهنالك ألعاب كثيرة تعمل على المتصفحات باستخدام تقنيات HTML5 و JavaScript. اللغات والتقنيات المستخدمة في تطوير الألعاب: C++‎ و Java و JavaScript ومحركات Unity و Unreal Engine و Godot. برمجة الذكاء الاصطناعي يُعد الذكاء الاصطناعي (AI) من المجالات الرائدة والمطلوبة بكثرة في سوق العمل اليوم، لاسيما بعد أن أصبحت تطبيقاته في متناول الجميع وبات يستخدم في العديد من المجالات فتعلمك لهذا المجال الرائد يعزز قدراتك كمبرمج ويساعد على تعزيز تطبيقاتك بقدرات الذكاء الاصطناعي القوية. يتفرع الذكاء الاصطناعي لعدة مجالات من أبرزها تعلم الآلة والتعلم العميق التي تُمكّن الحواسيب من التعلم من البيانات وتحسين أدائها بمرور الوقت دون الحاجة لبرمجة تقليدية. كي تتعلم برمجة الذكاء الاصطناعي تحتاج لامتلاك فهم جيد للبرمجة وتحليل البيانات ويمكنك استخدام لغات برمجة متعددة في هذا المجال أشهرها لغة البرمجة بايثون التي توفر الكثير من المكتبات والأطر المساعدة مثل تنسرفلو TensorFlow وكيراس Keras وبايتورش PyTorch وباندا Pandas. إذا كنت مهتمًا بتعلم هذا الاختصاص القوي فستوفر لك دورة الذكاء الاصطناعي من أكاديمية حسوب التي تحتوي على 58 ساعدة تدريبية ممتدة على ثمانية مسارات تدريبية شاملة كل ما تحتاج إليه للبدء بتطوير تطبيقات ذكاء اصطناعي قوية واكتساب مهارة في تحليل البيانات من الصفر ودون الحاجة لامتلاك معرفة مسبقة. اللغات والتقنيات المستخدمة في برمجة الذكاء الاصطناعي هي Python و MongoDB وPymongo و TensorFlow و Keras و Pandas والنماذج اللغوية الكبيرة LLMs المختلفة. تطوير الأنظمة المدمجة الأنظمة المدمجة هي أنظمة حاسوبية شبيهة بالحاسوب ولكنها لا تملك كل ميزات الحاسوب الذي تراه أمامك الآن. بعبارة أخرى، النظام المدمج هو حاسوب صغير مبرمج لأداء مهام محددة فقط ومدمج ضمن الجهاز أو البيئة المراد استخدامه فيها. أنت الآن محاط بالكثير من الأنظمة المدمجة الآن مثل جهاز مقياس مستوى المياه وجهاز التحكم بالتلفاز وجهاز إنذار الحريق وأجهزة المراقبة …إلخ. حتى إشارات المرور وتنظيم السير وألعاب الأطفال الآلية تصنَّف على أنها أنظمة مدمجة. هل سمعت أيضًا بمصطلح "إنترنت الأشياء"؟ إنترنت الأشياء هو نظام مدمج متصل بالإنترنت. نعم، بهذه البساطة! لابد الآن أن يتبادر إلى ذهنك الساعات والثلاجات والغسالات الذكية وطائرات الدرون وأنظمة المراقبة عن بعد وأنظمة البيوت الذكية، إذ كلها أمثلة على إنترنت الأشياء. كيفية برمجة الأنظمة المدمجة أشهر وأكثر لغة برمجة تستعمَل في برمجة الأنظمة المدمجة وإنترنت الأشياء هي لغة C (أي لغة سي) وكل اللغات المشتقة منها (مثل‎ لغة أردوينو C). تُستعمَل لغة C++‎ كثيرًا في هذا المجال، إذ تعدُّ لغة ذات مستوى أعلى من لغة C لدعمها للبرمجة كائنية التوجه. أضف إلى ذلك أنه بدأ حديثًا استعمال لغة بايثون في برمجة تطبيقات الأنظمة المدمجة مع أنها لم ترتبط تقليديًّا بهذا المجال سابقًا. صحيح أنَّ لغة بايثون ليست بقوة لغة C و C++‎ في هذا المجال إلا أنها تستمد ميزاتها وفعاليتها من المكتبات الهائلة المتوافرة فيها. بعيدًا عن C وبايثون، تستعمل في مجال الأنظمة المدمجة أيضًا لغات أخرى تنضوي ضمن "لغات توصيف العتاد" (Hardware Description Languages)؛ لغتي VHDL و Verilog هما من أشهر لغات توصيف العتاد المستعملة في هذا المجال. تُستعمَل مثل هذه اللغات في برمجة "مصفوفة البوابات المنطقية القابلة للبرمجة" (FPGA أي Field Programmable Gate Array). أخيرًا، قد تجد بعض المراجع تشرح برمجة الأنظمة المدمجة بلغة أخرى تدعى "لغة التجميع" (Assembly Language) التي تصنف من اللغات منخفضة المستوى. يتطلب تعلم البرمجة باستخدام هذه اللغة فهمًا واسعًا بمعمارية وحدة التحكم المركزية والمعالج بالمجمل لأنها أقرب لغة يفهمها الحاسوب. الانتقال إلى هذه اللغة قد يكون في مستويات متقدمة من تعلمك لبرمجة الأنظمة المدمجة وتطبيقات إنترنت الأشياء. من ميزات البرمجة بهذه اللغة هي التحكم الواسع بالعتاد والمعالج الذي لا توفره لغات أخرى. يقال أن هذه اللغة صعبة بعض الشيء ومعقدة، ولكن لا أرى ذلك! قد يكون سبب قول ذلك هو أن لغة التجميع هي لغة منخفضة المستوى وأقرب شيء إلى لغة الآلة ولا يستطيع من يلقي نظرة على شيفرة مكتوبة فيها فهمها مطلقًا إن لم يعرفها. تطوير تطبيقات سطح المكتب مجال تطوير تطبيقات سطح المكتب كالمحيط الواسع؛ إن لم تملك بوصلة وتعرف إلى أين تريد الاتجاه، ستضيع فيه حتمًا. هنالك الكثير من أنظمة التشغيل أشهرها - وأكثرها سيطرةً على السوق حاليًا - هي: نظام التشغيل ويندوز، ولينكس، وماك (macOS)، ويملك كل نظام تشغيل تطبيقات مكتبية خاصة به. لذلك، يجب عليك أولًا -قبل الدخول إلى سوق برمجة تطبيقات سطح المكتب- تحديد نظام التشغيل المستهدف. أي يجب الإجابة على السؤال التالي: هل يستهدف تطبيقك نظام تشغيل محدد، أم تريد لتطبيقك أن يعمل على عدة أنظمة تشغيل في آن واحد؟! بعد تحديد نظام التشغيل المستهدف، اطلع على اللغات المفضل استعمالها في ذلك النظام لبرمجة تطبيقاته؛ فعلى سبيل المثال، اللغات C و C++‎ و C#‎ و VB.NET هي الأكثر استعمالًا في برمجة تطبيقات نظام التشغيل ويندوز، واللغات C و C++‎ و Bash هي الأكثر استعمالًا في برمجة تطبيقات توزيعات نظام التشغيل لينكس. أمَّا نظام الشغيل ماك، فينفرد باستعمال لغة Objective-C. حسنًا، دعني أخبرك الحقيقة، كل لغة برمجة عامية الغرض يمكن استعمالها في برمجة التطبيقات، إذ أشهر اللغات التي تُدرَّس أكاديميًّا في هذا المجال هي لغة جافا (Java). لا يخفى على القارئ دخول لغة بايثون بقوة على هذا المجال نظرًا لامتلاكها الكثير من المكتبات الرائعة وسهولة صياغتها. دخلت مؤخرًا لغة جافاسكريبت على سوق برمجة تطبيقات سطح المكتب عبر إطار العمل Electron (إلكترون)، إذ توظف في هذا المجال تقنيات تطوير الويب (HTML و CSS و JavaScript …إلخ.). بدأ هذا الإطار ينتشر كالنار في الهشيم مما دفع شركات كبيرة لتطوير تطبيقات سطح المكتب الخاصة بها باستعمال هذا الإطار ومنها شركة Slack التي استعملت هذا الإطار لتطوير تطبيقها المكتبي. أعلم أنك الآن تشعر بالضياع من كثرة لغات البرمجة والتقنيات المستعملة في هذا المجال؛ معك حق، فقد أخبرتك بذلك منذ قليل. دخول هذا السوق يحتاج منك تحديد هدفك منه بالضبط. هل لديك فكرة تطبيق وتريد إنشاءه والربح منه؟ هل تريد العمل لدى شركة محددة؟ ما هي مواصفات التطبيق الذي تريد إنشاءه أو تريد العمل على تطويره؟ كل ذلك يلعب دورًا في تحديد لغة البرمجة الأنسب لك لتعلمها. في النهاية، إن تعلمت أساسيات البرمجة وأتقنت العمل على لغة برمجية محددة، سيسهل عليك الانتقال إلى لغة برمجة أخرى، إذ أغلب لغات البرمجة تشبه بعضها بعضًا من ناحية المفهوم والمضمون وتختلف بعض الشيء من ناحية الصياغة والشكل. لذلك، اطمئن من هذه الناحية. كيفية اختيار لغة البرمجة التي تناسبك يمكنك اختيار لغة البرمجة اعتمادًا على المجال الذي تحب العمل فيه، سألخص لك مسار التعلم لمختلف مجالات العمل: العمل كمطور ويب full-stack: يعني ذلك تعلم تطوير واجهات المستخدم أو الواجهات الأمامية، وتطوير الواجهات الخلفية. يمكن التخصص بمجال واحد من هذين المجالين فقط، إذ يُطلَب كثيرًا في سوق العمل مبرمجين متخصصين في واجهات المستخدم أو الواجهات الخلفية. العمل كمطور تطبيقات للهواتف المحمولة: يمكنك تعلم برمجة تطبيقات أندرويد أو iOS كلًا على حدة، أو استعمال تقنيات مثل كوردوفا لتطوير لكلا النظامين معًا. العمل كمطور تطبيقات سطح المكتب: يمكنك البدء بالتخصص في تطوير تطبيقات مكتبية لنظام تشغيل محدَّد (مثل نظام التشغيل ويندوز أو لينكس) عبر تعلم لغة البرمجة المستعملة في ذاك المجال (كما أشرنا إلى ذلك في قسم تطوير تطبيقات سطح المكتب)؛ خيار آخر هو تعلم اللغات والتقنيات التي تمكنك من تطوير تطبيقات عابرة للمنصات (تعمل على عدة أنظمة تشغيل) مثل استعمال إطار العمل Electron. العمل كمطور للأنظمة المدمجة والأنظمة الذكية: لغة C هي أساس هذا المجال، سواءً كنتَ تتعامل مع المتحكمات مباشرةً، أو تتعامل مع شريحة مثل أردوينو (والتي تمتلك لغةً مشتقةً من C). يساعدك هذا الفيديو على معرفة المعايير التي من خلالها ستتمكن من اختيار لغة البرمجة التي تتناسب مع تطلعاتك وأهدافك المستقبلية. نصائح لتعلم البرمجة مشوار تعلم البرمجة من الصفر طويل وشائق، ولكنه جميل ومسلٍ بذات الوقت، قد تصاب بالإحباط في بداية طريقك لكثرة الأمور التي عليك الإلمام بها، لذا جهزت إليك النصائح الآتية من تجربتي في البرمجة: حدد هدفك من تعلم لغة البرمجة وسوق العمل الذي تريد دخوله واجعله واقعيًا. بدون هدف، أبشرك بأنك ستتخلى عن فكرة تعلم البرمجة بعد حين. انتبه إلى أن يكون هدفك واقعيًا وقابلًا للقياس والتجزيء على مراحل. بدون ذلك، ستفشل من أول عقبة وتترك تعلم البرمجة. أعرف نفسك جيدًا ونقاط قوتك وضعفك. كلنا لديه نقاط قوة وضعف، ولكن المفلح من عمل على ترميم وتحسين نقاط ضعفه في المجال الذي يرغب بتعلمه. رشح دورة واحدة وكتابًا واحدًا وابدأ بقراءة الكتاب ومتابعة الدورة تدريجيًّا ثم انتقل بعد الانتهاء إلى دورة أخرى وكتاب آخر، إذ سيجنبك ذلك التشتت بين الدورات الكثيرة والكتب العديدة. الشيء الذي أفعله قبل بداية تعلم شيء جديد هو ترشيح قائمة من عدة كتب ودورات ثم ترتيب هذه الكتب والدورات بحسب جودتها ومدى بساطتها وتعقيدها. أرقم الكتب والدورات وأبدأ بالخيار الأول منها. أحدد الوقت التقريبي الذي يأخذه كل خيار لدراسته وأجدول الخيارات البقية على رزنامتي الخاصة. لا تأخذ العلم إلا ممن تثق بعلمه، فالكثير من المبتدئين يحاولون مساعدة غيرهم وقد يضعون معلومات مغلوطة دون قصد. طبق ما تعلمته مباشرة، وأنشئ أي شيء من كل أمر جديد تتعلمه حتى لو كان رسمة بسيطة أو شيفرة من عدة أسطر فقط. فرحة إنجاز شيء مما تعلمته تدفعك لتعلم المزيد والاستمرار في طلب العلم. نظم وقتك بورقة وقلم، حدد بداية كل أسبوع خطةً لسائره والتزم بتنفيذها. أخبر أصدقائك أن لديك التزامات وأمور مهمة عليك إنجازها. خصص وقتًا للاستراحة بالطبع ولا تنسَ نصيبك منها. في نهاية كل أسبوع، وازن مدى الإنجاز الذي حققته ومدى تطبيق الخطة التي وضعها وحاول أن تصل النسبة إلى 100%. أنصحك بقراءة ومتابعة استراتيجيات تنظيم الوقت ورفع الإنتاجية. تمرس على حل المشكلات وتحدى نفسك باستمرار وتابع المسابقات البرمجية واشترك بها إن استطعت، أنصحك بقراءة مقالة حل المشكلات وأهميتها في احتراف البرمجة بعد هذه المقالة. لا تنسَ أن تكافئ نفسك في كل مرة تنهي فيها كتابًا أو تكمل العمل على مشروع. لا تنسَ حظك من الاستراحة، لأن طريق البرمجة قد يكون له بداية ولكن النهاية بعيدة ومتعبة -مثله مثل أي مجال آخر-. في النهاية أرجو لك كل التوفيق في مشوارك البرمجي. وأرجو منك أن تشاركنا تجربتك في تعلم البرمجة، لعل غيرك يستفيد منها. اقرأ أيضًا كيف تتعلم البرمجة المدخل الشامل إلى تعلم علوم الحاسوب المرجع الشامل إلى تعلم لغة بايثون تعرف على أعلى تخصصات البرمجة أجرا تعلم لغة HTML قواعد البرمجة ما هي فوائد تعلم البرمجة؟ أسهل لغات البرمجة أهمية البرمجة
  14. على الرحب والسعة. الحل هو سحب الرصيد ثم شراء الدورة عبر paypal.
  15. وعليكم السلام ورحمة الله وبركاته، أهلًا بك. كانت هذه الخدمة موجودة ولم تعد متاحةً للأسف.
  16. توفِّر JavaScript إمكانية بناء كائن Date جديد، وإذا أمعنت النظر في المعاملات التي يقبلها هذا الكائن، فستلاحظ أنَّ مجال «اليوم» بين 1 إلى 31. لاحظ أنَّه عند استدعاء الدالة البانية Date مع أكثر من وسيط، وكانت القيم أكبر من مجالها المنطقي (مثلًا: استخدمنا 13 مكان الشهر أو 70 مكان الدقائق أو 0 مكان «اليوم») فستُعدّل قيمة التاريخ بما يوافق ذلك. لذا سنستعمل 0 مكان «اليوم» عند بناء الكائن كما يلي: var dateNow = new Date(); var lastDayOfMonth = new Date(dateNow.getFullYear(), dateNow.getMonth() + 1, 0).getDate(); console.log(lastDayOfMonth); ما سبق سيُنشِئ كائن Date جديد للسنة الحالية، وللشهر القادم، ولليوم 0، ولمّا كان أول يوم في الشهر هو 1 وليس 0، فسيؤخذ آخر يوم من الشهر الذي يسبق الشهر الماضي (أي الشهر الحالي). راجع توثيق الدالة البانية Date والدالة getFullYear والدالة getMonth والدالة getDate في موسوعة حسوب للمزيد من المعلومات.
  17. إحدى المهارات الأساسية والمهمة التي عليك أن تتقنها هي القدرة على استيراد وتصدير قواعد البيانات، إذ تستطيع أخذ نسخ احتياطية لقواعد بياناتك وتستعيدها عند الحاجة، أو يمكنك الاستفادة منها لنقل البيانات إلى خادوم جديد أو بيئة تطوير مختلفة. إجراء عملية تصدير من قواعد بيانات MySQL و MariaDB هو أمرٌ بسيطٌ، وسنشرحه في هذا الدرس إضافةً إلى شرح طريقة استيراد النسخة الاحتياطية. المتطلبات المسبقة لتصدير أو استيراد قواعد بيانات MySQL ستحتاج إلى: وصول إلى خادوم لينكس يستعمل قواعد بيانات MySQL أو MariaDB. قاعدة بيانات مع معلومات المستخدم القادر على إجراء عمليات عليها. تصدير قاعدة البيانات تُستعمَل الأداة mysqldump لتصدير قواعد البيانات إلى ملفات SQL نصية، ويمكن بسهولة نقل تلك الملفات كيفما تشاء. ستحتاج إلى معرفة اسم قاعدة البيانات نفسها وإلى اسم المستخدم وكلمة المرور لحسابٍ يملك امتيازاتٍ تسمح –على الأقل– بأذونات القراءة الكاملة على قاعدة البيانات. استعمل الأمر الآتي لتصدير قاعدة البيانات: mysqldump -u username -p database_name > data-dump.sql username هو اسم المستخدم الذي يستطيع الوصول إلى قاعدة البيانات database_name هو اسم قاعدة البيانات التي نريد تصديرها data-dump.sql هو مسار الملف الذي نريد حفظ مخرجات عملية التصدير إليه لن يعرض الأمر السابق أيّة مخرجات مرئية، لكن يمكنك النظر في محتويات الملف data-dump.sql لتتأكد من وجود ملف SQL سليم فيه باستعمال الأمر: head -n 5 data-dump.sql يجب أن تبدو المخرجات (التي تظهر أوّل خمسة أسطر من الملف) كما يلي، يجدر بالذكر أنَّ الملف الآتي هو ناتج عملية تصدير قاعدة بيانات باسم database_name: -- MySQL dump 10.13 Distrib 5.7.16, for Linux (x86_64) -- -- Host: localhost Database: database_name -- ------------------------------------------------------ -- Server version 5.7.16-0ubuntu0.16.04.1 إذا حدثت أيّة أخطاء أثناء عملية التصدير، فسيعرضها mysqldump على الشاشة مباشرةً. استيراد قاعدة البيانات لاستيراد ملف SQL مُصدَّر إلى قاعدة بيانات MySQL أو MariaDB، فعليك أولًا إنشاء قاعدة بيانات جديدة التي ستستضيف البيانات المستوردة. سجِّل دخولك إلى عميل قواعد البيانات بحساب المستخدم root أو أي حساب آخر له امتيازات كافية لإنشاء قواعد بيانات جديدة: mysql -u root -p ستنتقل الآن إلى سطر الأوامر الخاص بقواعد MySQL، أنشِئ الآن قاعدة بيانات جديدة وليكن اسمها new_database: CREATE DATABASE new_database; يجب أن تشاهد الناتج الآتي الذي يدل على نجاح إنشائها: Query OK, 1 row affected (0.00 sec) اخرج الآن من عميل MySQL بكتابة الأمر exit أو بالضغط على الزرين Ctrl+D، وبعد عودتك إلى سطر الأوامر ستستطيع استيراد الملف باستخدام الأمر الآتي: mysql -u username -p new_database < data-dump.sql username هو اسم المستخدم الذي يستطيع الوصول إلى قاعدة البيانات new_database هو اسم قاعدة البيانات التي نريد استيراد البيانات إليها data-dump.sql هو مسار الملف الذي نريد استيراد محتوياته لن تظهر أية مخرجات إذا نجح تنفيذ الأمر السابق، لكن إن حدثت أخطاء خلال العملية فسيظهرها الأمر mysql على الشاشة. يمكنك التأكد من استيراد قاعدة البيانات بتسجيل الدخول إلى عميل MySQL مجددًا وتفحص البيانات، يمكن فعل ذلك بتحديد قاعدة البيانات الجديدة عبر USE new_database ثم كتابة الأمر SHOW TABLES;‎ أو ما شابهه. الخلاصة أصبحت تعلم كيف تصدِّر قواعد بيانات MySQL إلى ملفات وكيف تستوردها مجددًا. هنالك خياراتٌ أخرى متاحةٌ للأمر mysqldump التي تُعدِّل سلوك هذا الأمر عند تصدير قواعد البيانات، والتي يمكنك التعرف عليها في صفحة التوثيق الرسمي. ترجمة –وبتصرّف– للمقال How To Import and Export Databases in MySQL or MariaDB لصاحبه Mateusz Papiernik
  18. برمجية Jenkins هي خدمة أتمتة مفتوحة المصدر الغرض منها هو أتمتة المهام التقنية المتكررة التي تتعلق بدمج وتوفير البرمجيات. برمجية Jenkins مكتوبة بلغة Java ويمكن تثبيتها من حزم أوبنتو أو بتنزيل وتشغيل ملف WAR (الذي هو مجموعة من الملفات التي تُنشِئ تطبيق ويب كامل ليعمل على خادومك). سنُثبِّت Jenkins في هذا الدرس بإضافة مستودع حزم دبيان الخاص بها، ثم بتثبيت الحزم اللازمة عبر الأداة apt-get من ذاك المستودع. المتطلبات المسبقة سنحتاج إلى خادوم أوبنتو 16.04 فيه حساب مستخدم ليس جذرًا وفيه جدار ناري وذلك باتباع التعليمات الواردة في درس «الإعداد الابتدائي لخادوم أوبنتو 14.04](https://academy.hsoub.com/devops/servers/الإعداد-الابتدائي-لخادوم-أوبنتو-1404-r4/)»، ننصح باستعمال 1 غيغابايت من ذاكرة RAM على الأقل؛ راجع صفحة Choosing the Right Hardware for Masters لمزيدٍ من المعلومات حول العتاد اللازم لتثبيت Jenkins. يمكنك المتابعة مع هذا الدرس بعد إعداد الخادوم. الخطوة الأولى: تثبيت Jenkins الإصدار المتوافر في مستودعات أوبنتو الرسمية يكون قديمًا ومتأخرًا عن الإصدار الحالي لمشروع Jenkins؛ وللاستفادة من آخر التحديثات والميزات فسنستخدم الحزم المُصانة من قِبل مشروع Jenkins نفسه. علينا أولًا إضافة مفتاح المستودع إلى النظام: wget -q -O - https://pkg.jenkins.io/debian/jenkins-ci.org.key | sudo apt-key add - بعد إتمام إضافة المفتاح بنجاح فستظهر كلمة OK في الطرفية؛ ثم سنضيف بعد ذلك عنوان مستودع دبيان إلى ملف sources.list الموجود في الخادوم: echo deb http://pkg.jenkins.io/debian-stable binary/ | sudo tee /etc/apt/sources.list.d/jenkins.list بعد تنفيذ الخطوتين السابقتين، علينا تحديث فهرس الحزم عبر الأمر: sudo apt-get update ثم نثبّت Jenkins مع اعتمادياته كلها، بما فيها بيئة تشغيل Java: sudo apt-get install jenkins يمكننا الآن تشغيل خادوم Jenkins بعد تثبيته مع اعتمادياته. الخطوة الثانية: بدء تشغيل خادوم Jenkins سنستخدم الأداة systemctl لتشغيل Jenkins: sudo systemctl start jenkins ولعدم إظهار الأمر systemctl لأية مخرجات، فسنستخدم الخيار status لعرض حالة الخدمة للتأكد أنها قد بدأت دون مشاكل: sudo systemctl status jenkins إذا جرى كل شيءٍ على ما يرام، فيجب أن تشير المخرجات أنَّ الخدمة تعمل حاليًا وستُشغَّل تلقائيًا عند الإقلاع: ● jenkins.service - LSB: Start Jenkins at boot time Loaded: loaded (/etc/init.d/jenkins; bad; vendor preset: enabled) Active:active (exited) since Thu 2017-04-20 16:51:13 UTC; 2min 7s ago Docs: man:systemd-sysv-generator(8) بعد تشغيل خادوم Jenkins، علينا الآن تعديل قواعد الجدار الناري للسماح بالوصول إلى خدمة Jenkins من متصفح الويب لإكمال ضبطه المبدئي. الخطوة الثالثة: فتح المنفذ في الجدار الناري يعمل خادوم Jenkins افتراضيًا على المنفذ 8080، لذا سنفتحه باستخدام ufw: sudo ufw allow 8080 يمكننا رؤية القاعدة الجديدة بالتحقق من حالة جدار UFW الناري: sudo ufw status يجب أن نلاحظ أنَّ التراسل الشبكي إلى المنفذ 8080 متاحٌ من كل الشبكات: Status: active To Action From -- ------ ---- OpenSSH ALLOW Anywhere 8080 ALLOW Anywhere OpenSSH (v6) ALLOW Anywhere (v6) 8080 (v6) ALLOW Anywhere (v6) بعد أن ثبتنا Jenkins وضبطنا الجدار الناري للسماح بالوصول إليه من جميع الشبكات، فسنكمل في الخطوة التالية الضبط المبدئي له. الخطوة الرابعة: ضبط خدمة Jenkins علينا لضبط خدمة Jenkins أن نزور صفحته على المنفذ الافتراضي 8080 مستعملين اسم نطاق الخادوم أو عنوان IP الخاص به كما يلي: http://ip_address_or_domain_name:8080. سنستعمل الأمر cat في نافذة الطرفية لعرض كلمة المرور: sudo cat /var/lib/jenkins/secrets/initialAdminPassword انسخ كلمة المرور المكومة من 32 محرفًا ورقمًا من الطرفية وألصقها في حقل «Administrator password» ثم اضغط على زر «Continue»، ثم ستظهر لك صفحة تعرض خيارين لتثبيت الإضافات المقترحة أو تحديد إضافات معيّنة. اضغط على خيار «Install suggested plugins» الذي سيبدأ عملية التنزيل مباشرةً: بعد إكمال التثبيت سيُطلَب منك ضبط أوّل حساب مستخدم، وصحيح أنَّ من الممكن تخطي هذه الخطوة والإكمال باستعمال المستخدم admin مع كملة المرور المبدئية التي استعملناها أعلاه، لكن من الأفضل إنشاء مستخدم جديد. ملاحظة: الاتصالات المنشأة إلى خادوم Jenkins غير مشفرة، لذا ستُرسَل البيانات النصية كما هي دون أي تشفير، لذا أنصحك باتباع ما ورد في هذا الدرس لإعداد تشفير SSL مع خادوم Jenkins، مما يحمي بيانات المستخدم والمعلومات التي ترسَل عبر واجهة الويب. بعد ضبط أوّل حساب للمدير فستُعرَض لك صفحة فيها «Jenkins is ready!‎»: اضغط على زر «Start using Jenkins» للانتقال إلى لوحة التحكم الرئيسية لخادوم Jenkins: تهانينا، لقد ثبتنا خادوم Jenkins بنجاح. الخلاصة ثبتنا في هذا الدرس برمجية Jenkins من الحزم التي يوفرها المشروع، ثم بدأنا الخدمة وفتحنا منفذًا في الجدار الناري، وأنشأنا حسابًا جديدًا للمدير؛ وتستطيع الآن أن تستكشف Jenkins. ترجمة –وبتصرّف– للمقال How To Install Jenkins on Ubuntu 16.04لصاحبته Melissa Anderson
  19. عندما تصون مستودعًا لمشروع مفتوح المصدر، فأنت تأخذ راية القائد، فلو كنتَ مؤسس أحد المشاريع التي أطلقتها للعموم ليستخدموها ويشاركوا فيها، أو كنت تعمل في فريقٍ وكنت تصون جانبًا من جوانب المشروع، فأنت تقدم خدمةً مهمةً إلى مجتمع التطوير. وعلى الرغم من أنَّ المساهمات في المشاريع مفتوحة المصدر تكون عادةً عبر طلبيات pull وهي أمرٌ بالغ الأهمية للحفاظ على جودة البرمجيات وفائدتها للمستخدمين النهائيين، لكن المساهمين في المشروع ليس لهم نفس تأثير من يقوم بصيانته ويبلور الشكل العام للمشروع؛ فالصائنون يشاركون في صلب تطوير المشاريع مفتوحة المصدر، إذ يديرون المشروع وينظموه يوميًا ويطوروه، ويتفاعلون مع المستخدمين ويوفرون المعلومات اللازمة للمساهمين. سنستعرض في هذا المقال بعض التلميحات عن صيانة المستودعات العامة للمشاريع مفتوحة المصدر. فالمسؤولون عن المشاريع مفتوحة المصدر عليهم مسؤوليات كبيرة تقنية وغير تقنية، والعمل كصائن للمشروع يعطي الفرصة للتعلم من الآخرين، وأخذ خبرة في إدارة المشاريع، ويسمح بمراقبة مراحل نمو المشروع وكيف أصبح المستخدمون العاديون مساهمين فعالين فيه. اكتب توثيقًا مفيدًا سيزيد التوثيق سهل الفهم (والذي سيؤدي إلى جعل البرنامج سهل الاستخدام) من قاعدة مستخدميك، ويساعد في تحويل المستخدمين إلى مساهمين في مشروعك. عندما تفكر بالشيفرات التي تكتبها أثناء تطويرك للمشروع، وتكتب بعض الملاحظات الجانبية لتساعدك في دمج مختلف أجزاء مشروعك مع بعضها بعضًا، فسيسهل عليك البدء بكتابة التوثيق أثناء عملية التطوير؛ أو ربما تقرر أن تكتب التوثيق قبل برمجة التطبيق، متبعًا بذلك منهجية «التطوير الموجه بالتوثيق» (documentation-driven development) التي تقول أنَّ عليك كتابة توثيق ميزات المشروع أولًا ثم برمجة تلك الميزات اعتمادًا على توثيقها. هنالك بضعة ملفات عليك وضعها في المجلد الرئيسي لمشروعك بجانب الشيفرات وهي: - ملف README.md الذي يوفر ملخصًا عن المشروع وأهدافه. - ملف CONTRIBUTING.md الذي يحتوي على تعليمات المساهمة في المشروع. - رخصة مشروعك، والتي ستشجِّع الناس على المساهمة في موقعك، أرجو أن تراجع مقالة كيف تختار رخصة مفتوحة المصدر لبرامجك. قد يأخذ التوثيق أشكالًا عدِّة، لذا عليك أن تضع نوعية المستخدمين المحتملين ومجال المشروع في الحسبان، فمن الممكن أن يأتي التوثيق بمختلف الأشكال ويكون موجهًا نحو فئات مختلفة من مستخدمي المشروع. قد تقرر استخدام شكل أو أكثر من الأشكال الآتية اعتمادًا على مجال عملك: دليل عام لتعريف المستخدمين بالمشروع الدروس التعليمية لعرض مختلف حالات الاستخدام الأسئلة الشائعة (FAQ) للإجابة عن أكثر التساؤلات شيوعًا بين المستخدمين مقالات عن استكشاف الأخطاء التي قد يواجهها المستخدمون وكيفية إصلاحها مرجع للواجهة البرمجية (API) للتطبيق تسمح للمستخدمين معرفة معلومات عن الواجهة البرمجية بسرعة ملاحظات الإصدار (release notes) التي تُذكَر فيها العلل المعروفة والتغييرات التي حدثت في كل إصدار الميزات المستقبلية لتتبع وشرح ما هي الميزات التي ستأتي في إصدارات مستقبلية. تسجيل مقطع فيديو لتعريف المستخدمين بمشروعك عبر الوسائط المتعددة. قد تلائم بعض أشكال التوثيق السابقة مشروعك أكثر من غيرها، لكن توفير أكثر من شكل للتوثيق سيساعد مستخدمي مشروعك أن يفهموا كيف يتفاعلون معك فهمًا أعمق. عليك عند كتابة التوثيق أو تسجيل مقطع فيديو أن تكون واضحًا قدر الإمكان، ومن الأفضل ألّا تكون عندك افتراضات مسبقة عن القدرات التقنية لمستخدمي مشروعك، ومن الأفضل أن تتبع منهجية Top-Down عند تأليف التوثيق، أي أن تشرح بداية الأمر ما الذي تفعله البرمجية بشكل عام (مثلًا: أتمتة أمور إدارة النظام، أو بناء موقع إلكتروني …إلخ.) قبل التعمق في التفاصيل. وصحيحٌ أنَّ اللغة الإنكليزية هي اللغة الرائدة في عالم التقنية، لكن أبقِ في ذهنك مَن هم المستخدمون المتوقعون وما هي لغتهم الأم؛ فاللغة الإنكليزية هي خيارٌ جيدٌ إذا كانت لديك قاعدة مستخدمين واسعة ومن مختلف البلدان، لكن ضع في بالك أنَّ عدد كبيرًا ممن سيقرؤون توثيقك لا تكون اللغة الإنكليزية هي لغتهم الأم، لذا حاول استخدام لغة سهلة لا تُسبِّب لبسًا عند القراء، وإذا كان مشروعك موجّه لمنطقة أو لغة معيّنة مثل برنامج للتعرف على الكلام العربي فأنصحك حينئذٍ أن توفر التوثيق باللغة العربية. حاول أن تكتب التوثيق كما لو كنتَ تكتب لأحد المساهمين الجدد الذين يريدون أن يطلعوا على حالة المشروع، فلا تنسَ أنَّك تريد أن يتحول المستخدمون العاديون إلى مساهمين. تنظيم «القضايا» القضايا (issues) هي طريقةٌ تستعمل لتتبع أو التبليغ عن العلل، أو لطلب ميزات جديدة لتضاف إلى البرنامج. توفِّر خدمات استضافة مستودعات المشاريع مفتوحة المصدر مثل GitHub و GitLab و Bitbucket طرائق لتتبع القضايا التي تُنشِئها وتسمح للمستخدمين بإنشائها. فمن المتوقع عند إطلاق مشروعك مفتوح المصدر أن تُفتَح عدِّة قضايا من قِبل مجتمع المستخدمين، لذا سيمثِّل تنظيم ووضع أولويات لهذه القضايا أمرًا مهمًا لتبيان خارطة الطريق لما عليك فعله للمشروع مستقبلًا. ولأنَّ أي مستخدم يستطيع أن يفتح قضية، فلن تكون جميع القضايا لتبليغ العلل أو لطلب الميزات، فقد تأتيك أسئلة عبرها، أو قد تأتي طلبات لتحسينات صغيرة على واجهة المستخدم مثلًا. فمن الأفضل تنظيم هذه القضايا في أفضل شكل ممكن ومحاولة التواصل مع المستخدمين الذين أنشؤوها. يجب أن تمثِّل القضايا مهامًا محددة عليك تنفيذها برمجيًا، لذا عليك تنظيمها حسب أهميتها. يجب أن يكون هنالك حدود للوقت والعمل الذي تنفقه أنت أو المساهمون في المشروع للقضايا المفتوحة فيه، ويمكنكم التعاون على اتخاذ القرارات والخروج بخطة قابلة للتنفيذ، وعندما تعلم أنَّك غير قادر على حل مشكلة معيّنة في الإطار الزمني المتاح لك، فيمكنك التعليق عليها وإخبار المستخدم أنَّك قرأت المشكلة وستفعل ما بوسعك تجاهها، وقد تستطيع أن تخبره بالوقت المتوقع للنظر في أمر هذه المشكلة مرةً أخرى. أما لطلبات الميزات أو التحسينات، فيمكنك أن تسأل الشخص الذي أنشأ القضية إن كان يستطيع المساهمة في الشيفرة لتطبيق هذه الميزة، يمكنك توجيه المستخدمين إلى ملف CONTRIBUTORS.md أو إلى أيّة صفحات أخرى من التوثيق. ولأن الأسئلة لا تمثِّل عادةً مهامًا محدَّدة، فالتعليق على السؤال لتوجيه المستخدم بلباقة إلى صفحة التوثيق هو خيارٌ ممتاز لإبقاء تفاعلك مع المستخدم احترافيًا ولطيفًا؛ وإذا لم يحتوي التوثيق على جوابٍ لهذا السؤال فحان الوقت لإضافة التوثيق الملائم، وتعبِّر عن شكرك للمستخدم لأنه دلّك على موضع النقص في التوثيق. إذا كنتَ تستقبل عددًا كبيرًا من الأسئلة عبر القضايا، فربما تفكر بإنشاء صفحة الأسئلة الشائعة (FAQ) في التوثيق، أو صفحات ويكي أو منتدى لتتيح للآخرين المساعدة والمشاركة في الإجابة عن الأسئلة. وفي كل مرة يبلِّغ فيها أحد المستخدمين عن مشكلة، فحاول أن تكون لطيفًا معه قدر الإمكان، فتفاعل المستخدمين معك يعني أنَّ المشروع قد أعجبهم ويريدون جعله أفضل. محاولة تنظيم القضايا ستجعل مشروعك محدثًا دومًا وسيشعر المستخدمون أنهم يأثرون فيه، لذا احذف القضايا التي تقع خارج نطاق مشروعك أو القضايا القديمة، وضع أولويات للبقية لكي يكون تقدمك في المشروع مستمرًا. حفِّز المساهمين كلما رحّبتَ بالمساهمين الجدد وكافأتهم على جهودهم لوجدت أنَّك تحفِّز مساهمين جدد ليشاركوا في مشروعك، ولكي تجذب المساهمين إلى المشروع فاحرص على تضمين ملف CONTRIBUTING.md في المجلد الرئيسي لمستودعك، وإشارة إلى ذاك الملف في README.md. إذا أردتَ كتابة ملف جيد للمساهمين الجدد فيجب أن يتضمن كيفية بدء العمل على المشروع كمطوِّر، فقد تكتب دليلًا يوضح ذلك خطوةً بخطوة، أو قائمةً من الأمور التي يجب على المطورين اتباعها وتنفيذها، شارحًا كيف يمكن أن يدمجوا الشيفرة التي كتبوها بشيفرة المشروع عبر طلبية pull. إضافةً إلى توثيق كيفية المساهمة في المشروع، فلا تنسَ أن تبقي شيفرات المشروع منظمة وسهلة القراءة، فإذا كانت الشيفرة سهلة الفهم وفيها تعليقات كثيرة تشرح ما تفعله وطريقة استخدامها موحدة ومتناسقة مع بعضها بعضًا فسيساهم ذلك في تحفيز المساهمين المحتملين على المشاركة في المشروع. أقترح أيضًا أن تبقي على قائمة بالمساهمين، فيمكنك أن تجذب المساهمين عبر إضافتهم إلى القائمة بغض النظر عن حجم مشاركتهم (حتى تصحيح الأخطاء اللغوية هو مشاركة ومساهمة فعالة في المشروع، ويمكن أن تؤدي إلى مزيدٍ من المساهمات مستقبلًا). وهذا يعني أنَّك تقدِّر عمل المساهمين في المشروع وتشير إليهم أمام جميع مستخدمي مشروعك، مما يحمِّس بقية المستخدمين على المشاركة. ابنِ مجتمعًا حول مشروعك بتمكين مستخدميك عبر التوثيق وبالتجاوب مع القضايا وبتحفيزهم للمشاركة، فأنت في طريقك لبناء مجتمع حول مشروعك المفتوح المصدر، فالمستخدمون السعيدون بتجاوبك معهم والذين تعدُّهم على أنهم مساهمون سيحاولون الترويج مشروعك ما استطاعوا. إضافةً إلى ما سبق، يمكنك الترويج لمشروعك بمختلف السبل: التدوين تسجيل ونشر فيديوهات تعريفية إنشاء قائمة بريدية النشاط على مواقع التواصل الاجتماعي التعاون مع المشاريع الشبيهة أو المتعلقة بمشروعك والترويج لها. عليك أن تُناسِب ترويجك للمشروع مع مجاله وعدد أعضاء الفريق الفعالين والمساهمين الذين يعملون معك. فعندما ينمو المجتمع حول مشروعك، فيمكنك أن توفِّر مساحة أكبر للمساهمين والمستخدمين والقائمين على المشروع ليتفاعلوا، بعض تلك الخيارات تتضمن: برمجيات الويكي التي توفِّر توثيقًا مصانًا من المجتمع المنتديات لمناقشة الميزات المحتملة وللإجابة على الأسئلة قائمة بريدية للتفاعل مع المجتمع عبر البريد الإلكتروني ضع ببالك قاعدة مستخدمي مشروعك ومجاله بما في ذلك عدد الأشخاص القائمين عليه والموارد المتاحة لك قبل أن تتوسّع في المجالات السابقة، واستشر مجتمعك عن الخيار الأفضل قبل الإقدام عليه. وأهم من ذلك كله أن تكون لطيفًا معهم وتريهم أنَّك تهتم بهم عبر تفاعلك معهم، أعلمُ أنَّ الاتسام بصفة اللباقة طوال الوقت ليس أمرًا سهلًا، لكن ستؤتي أُكلها على المدى البعيد. الخلاصة يلعب صائن المستودع دورًا مهمًا في مجتمع البرمجيات مفتوحة المصدر الكبير. وصحيحٌ أنَّ هذا الدور يأخذ وقتًا وعملًا كبيرًا، لكن الخبرة التي تكتسبها خلال هذا العمل ستفيدك كمطوِّر وكمساهم، ولا تنسَ أنَّ الصائن اللطيف واللبق سيساعد في دفع عجلة تطوير المشروع الذي يهتم لأجله. ترجمة –وبتصرّف– للمقال Maintaining Open-Source Software Projects لصاحبته Lisa Tagliaferri. حقوق الصورة البارزة محفوظة لـ Freepik
  20. إذا طبّقنا بضعة انتقالات (transitions) على عناصر الصفحة فقد تؤدي إلى نتيجة رائعة إذا خططنا لها جيدًا، وسنتعلم في هذا المقال كيف نستخدم الانتقالات لدب الحياة في محتوى الصفحة، وسنخطط ونبني تأثيرات مرور الفأرة فوق العناصر لتبدو بأجمل مظهر. الانتقالات أم الحركات الانتقالات (transitions) رائعة للانتقال بسلاسة من حالة إلى أخرى (والعودة إلى الحالة الأصلية مجددًا) في المتصفح، وقد تبدو أنها بديلٌ أبسط من الحركات (animation) التي تستعمل keyframes و animation، لكن لكلٍ ميزاتها وفوائدها. فمثلًا، لن تستفيد من الانتقالات إذا كنت تريد حدوث حركة عند تحميل الصفحة، أو إذا أردتَ أن تستمر الحركة طوال الوقت؛ أما إذا كانت لديك حالتين وأردتَ التحريك بينهما بسهولة فستستفيد حينئذٍ من الانتقالات. إذا كنتَ مهتمًا بالفروقات بين الانتقالات والحركات فأنصحك بمراجعة هذه المقالة. سنبني في هذا الدرس مشهدًا من الفضاء وسنضيف إليه تأثير مرور الفأرة فوقه لعرض بعض المعلومات الإضافية عن الكوكب، وسنفِّكر بحالتَي العناصر وما هي الخاصيات التي سنستخدمها للحصول على انتقال سلس وجميل. يمكنك الاطلاع على المثال الكامل لأخذ فكرة عن ما سنفعله. See the Pen &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;a data-cke-saved-href=&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#39;https://codepen.io/donovanh/pen/qmNgXW/&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#39; href=&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#39;https://codepen.io/donovanh/pen/qmNgXW/&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#39;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Planet hover effect part 3: with transition&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/a&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; by Donovan Hutchinson (&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;a data-cke-saved-href=&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#39;https://codepen.io/donovanh&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#39; href=&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#39;https://codepen.io/donovanh&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#39;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;@donovanh&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/a&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;) on &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;a data-cke-saved-href=&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#39;https://codepen.io&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#39; href=&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#39;https://codepen.io&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#39;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;CodePen&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/a&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;. ضبط الحالتين أكثر الأمور التي تأخذ وقتًا وجهدًا عند إنشاء الانتقالات هي الحالة الأولية والحالة النهائية للعنصر، فيجب أن نُفكِّر كيف سيبدو العنصر قبل تطبيق الانتقال وماذا يجب أن يتغيّر في مظهره بعد حدوث الانتقال. عندما أُنشِئ الانتقالات المعقدة نسبيًا فلا أهتم بالحركة نفسها إلى أن يعجبني مظهر العناصر في الحالتين، ومن هذا المنطلق فسنهتم بالخاصيات التي سنستخدمها في حالة hover لأن هذه الخاصيات ستخضع إلى حركة. قبل الخوض في موضوع الانتقالات فلنضبط الحالتين أولًا. حالة البداية سيكون كوكب الأرض في منتصف المشهد بدايةً، وسيدور القمر حوله. شيفرة HTML تتضمن عنصر article الذي نضع فيه عنصرَي earth و moon، لاحظ أنَّ عنصر moon موجود داخل حاوية باسم moon-container لأنها ستساعدنا بموضع القمر لاحقًا. <article class="earth-demo"> <div class="earth"> <img src="https://cssanimation.rocks/images/random/earth.png"> </div> <div class="moon-container"> <div class="moon"> <img src="https://cssanimation.rocks/images/random/moon.png"> </div> </div> </article> هذه هي أنماط CSS التي سنستخدمها، سنجعل موضع العنصر earth مساويًا للقيمة absolute وسنستخدم الدالة calc لتوسيط الأرض رأسيًا وأفقيًا. .earth { position: absolute; top: calc(50% - 100px); left: calc(50% - 100px); } وسنُحدِّد موضع الصورة ونعطيها طولًا وعرضًا: .earth img { height: 200px; position: absolute; top: 0; left: 0; width: 200px; } وسنفعل المثل تقريبًا للقمر، إذ سنبدأ بوضع الحاوية في منتصف الشاشة: .moon-container { position: absolute; top: calc(50% - 25px); left: calc(50% - 25px); } ثم سنضبط أبعاد القمر ونُطبِّق الحركة spin عليه (والتي سنعرِّفها لاحقًا)، وهذا ما سيجعل القمر يدور حول الأرض. .moon { animation: spin 20s linear infinite; background: none; height: 50px; pointer-events: none; transform-origin: 25px; width: 50px; } عند هذه المرحلة سيكون القمر متموضعًا في منتصف الشاشة وفوق الأرض، لذا لنستخدم الخاصية transform لتحريك القمر إلى خارج الحاوية الخاصة بعيدًا عن الأرض. .moon img { height: 50px; transform: translateX(-160px) translateY(-160px); width: 50px; } هذا يعني أنَّ عنصر moon سيدور فوق الأرض، لكن الصورة ستدور حول الأرض. لنضف الآن حواف مدورة للعنصر لكي تظهر حواف الأرض والقمر بدقة وسنستعمل الخاصية box-shadow لإعطائهما توهجًا أزرقًا: .earth img, .moon img { border-radius: 50%; box-shadow: 0 0 12em 1em rgba(110, 140, 200, .6); } في النهاية، سنُعرِّف الحركة spin لتدوير القمر، وهذه الحركة بسيطة، إذ سندوِّر القمر حول المحور Z بزاوية 360 درجة. @keyframes spin { to { transform: rotateZ(360deg); } } هذا هو ناتج هذه المرحلة: See the Pen &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;a data-cke-saved-href=&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#39;https://codepen.io/donovanh/pen/aWZPeP/&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#39; href=&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#39;https://codepen.io/donovanh/pen/aWZPeP/&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#39;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Planet hover effect part 1: initial stage&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/a&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; by Donovan Hutchinson (&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;a data-cke-saved-href=&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#39;https://codepen.io/donovanh&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#39; href=&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#39;https://codepen.io/donovanh&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#39;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;@donovanh&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/a&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;) on &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;a data-cke-saved-href=&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#39;https://codepen.io&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#39; href=&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#39;https://codepen.io&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#39;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;CodePen&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/a&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;. عرض المزيد من المعلومات نريد أن نظهر معلومات مفيدة حول كوكب الأرض عند مرور الفأرة فوقه، ربما ستساعد هذه المعلومات الفضائيين في معرفة بعض الأمور الأساسية عن كوكبنا قبل أن يزورونا :-) . علينا قبل ضبط حالة hover أن نضيف هذه المعلومات، وسنبدأ بإضافة عنصر div ذي المعرِّف more-info الذي يحتوي على بعض النصوص: <article class="earth-demo"> <div class="earth"> <div class="more-info"> <h1>Earth</h1> <ul> <li>Third planet from the Sun</li> <li>Atmosphere: 21% oxygen</li> <li>Liquid water on surface</li> <li>Only planet that has life (that we know of)</li> </ul> </div> <img src="https://cssanimation.rocks/images/random/earth.png"> </div> <div class="moon-container"> <div class="moon"> <img src="https://cssanimation.rocks/images/random/moon.png"> </div> </div> </article> أضفنا هذه المعلومات داخل حاوية earth لأننا نريد جعلها جزءًا من التأثير، أي عند مرور الفأرة فوق النص فسيبقى على الشاشة. سنضيف بعض أنماط CSS إلى عنصر more-info: .earth .more-info { background-image: linear-gradient(to bottom, rgba(10,10,10,1), black); border-radius: 1em; color: #fff; opacity: 0; padding: 1em; transform-origin: 0 0; transform: scale(.8); width: 400px; } .earth .more-info h1 { margin: 0 0 1em; text-align: right; } ضبطنا هنا لون الخلفية والحواف المنحنية والحاشية وعرض العنصر، وغيّرنا قياسه قليلًا باستخدام transform وضبطنا transform-origin إلى الزاوية العليا اليسرى لكي يتغيّر قياسه بدءًا من تلك النقطة. خاصية opacity للعنصر more-info تساوي الصفر أي أنَّ العنصر شفاف تمامًا ولن يكون مرئيًا، وقد نضيف الخاصية visibility لكي نضمن أنَّ المحتوى لا يتداخل مع بقية العناصر، لكننا لن نفعل ذلك في مثالنا هذا. إنشاء انتقال للخاصيات المطلوبة عندما نضبط تأثير الانتقال فمن المهم أن نأخذ الأداء بالحسبان، فبعض الخاصيات مثل الارتفاع والحاشية (padding) وحجم الخط و background-position لن يكون أداؤها جيدًا إذا أجرينا تأثير الانتقال عليها، والسبب وراء ذلك هو أنَّها تجعل المتصفح يُعيد حساب الكثير من التفاصيل في تخطيط الصفحة عندما تتغير. لذا من الأفضل استخدام الخاصيتين opacity و transform عند إجراء حركات لأن أداءها جيد ولا تسبب «تعليق» في المتصفح. سنستخدم في هذا المثال الخاصيتين opacity و transform إذ سيُصغَّر العنصر more-info قليلًا باستخدام transform وكان شفافًا تمامًا (قيمة opacity تساوي الصفر). أما في حالة hover فسنغيّر شفافية العنصر more-info لكي يصبح مرئيًا وسنزيح الكوكب إلى الجانب. حالة مرور الفأرة فوق العناصر لنبدأ بضبط حالة ‎.earth:hover، إذا كنتَ ستستخدم Sass فيمكنك اختصار الكثير من الشيفرات التي سنكتبها، لكنني سأستخدم CSS في هذا الدرس. لنحرِّك الكوكب جانبًا بادئ الأمر: .earth:hover img { transform: translateX(-75px) translateY(-75px) scale(.5); } خاصية transform السابقة ستدفع الكوكب إلى اليسار بمقدار 75 بكسل وستصغره إلى 50% من حجمه. لنقم بشيءٍ مشابه إلى القمر: .earth:hover ~ .moon-container { transform: translateX(-75px) translateY(-75px) scale(.5); } .earth:hover ~ .moon-container img { transform: translateX(-140px) translateY(-140px); } نفّذنا خدعةً هنا إلى صورة القمر ألا وهي استخدام transform لتقريبه إلى الأرض. لنُظهِر الآن العنصر more-info: .earth:hover .more-info { opacity: 1; transform: none; } ضبطنا الشفافية إلى 1 وهذا يعني أنَّ العنصر أصبح ظاهرًا، وضبطنا الخاصية transform إلى none وهذا يعني أنَّ العنصر سيعود إلى حجمه الطبيعي. هذه هي النتيجة الحالية، مرر الفأرة فوق الكوكب لتشاهد مربع المعلومات. See the Pen &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;a data-cke-saved-href=&amp;amp;amp;amp;amp;amp;amp;amp;amp;#39;https://codepen.io/donovanh/pen/bWezpR/&amp;amp;amp;amp;amp;amp;amp;amp;amp;#39; href=&amp;amp;amp;amp;amp;amp;amp;amp;amp;#39;https://codepen.io/donovanh/pen/bWezpR/&amp;amp;amp;amp;amp;amp;amp;amp;amp;#39;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Planet hover effect part 2: basic hover effect&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/a&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; by Donovan Hutchinson (&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;a data-cke-saved-href=&amp;amp;amp;amp;amp;amp;amp;amp;amp;#39;https://codepen.io/donovanh&amp;amp;amp;amp;amp;amp;amp;amp;amp;#39; href=&amp;amp;amp;amp;amp;amp;amp;amp;amp;#39;https://codepen.io/donovanh&amp;amp;amp;amp;amp;amp;amp;amp;amp;#39;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;@donovanh&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/a&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;) on &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;a data-cke-saved-href=&amp;amp;amp;amp;amp;amp;amp;amp;amp;#39;https://codepen.io&amp;amp;amp;amp;amp;amp;amp;amp;amp;#39; href=&amp;amp;amp;amp;amp;amp;amp;amp;amp;#39;https://codepen.io&amp;amp;amp;amp;amp;amp;amp;amp;amp;#39;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;CodePen&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/a&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;. تطبيق تأثير الانتقال لقد أنجزنا الأمور الصعبة، وكل ما بقي علينا لنفعل هو إخبار المتصفح كيف سيتنقل بين الحالتين. ولحسن الحظ لن نحتاج إلى كتابة شيفرات طويلة، وإنما يمكن فعل ذلك بخاصية CSS وحيدة: .earth-demo * { transition: all 4s cubic-bezier(0,1.5,.3,1); } لنشرح القاعدة السابقة بالتفصيل. تُطبَّق القاعدة السابقة على كل العناصر الموجودة داخل عنصر earth-demo (وذلك باستخدام رمز النجمة *). ثم سنطبِّق تأثير الانتقال transition لجميع الخاصيات بمدة 4 ثواني وباستخدام دالة التوقيت cubic-bezier، وهذه الدالة ستبدأ بسرعة ثم تتباطأ حتى النهاية، وفي النهاية ستتخطى القيم المُحدَّدة في الحالة hover ثم ستعود إلى القيم التي ضبطناها. وقبل أن ننظر إلى النتيجة النهائية، فدعنا نضيف تأخيرًا بسيطًا إلى عناصر القائمة، فمن الجميل أن تظهر عناصر القائمة تلو بعضها بتأثيرٍ حركيٍ جميل. يمكننا فعل ذلك باستخدام الخاصية transition-delay. تأخير تأثير الانتقال سنضبط بدايةً شفافية تلك العناصر إلى الصفر، أي سيكونوا غير مرئيين قبل أن يبدأ تأثير الانتقال. .earth-demo h1, li { opacity: 0; } وعندما يبدأ الحدث hover فسنُظهرهم جميعًا: .earth-demo:hover h1, li { opacity: 0; } علينا الآن تأثير ظهور عناصر القائمة الأربعة، وذلك باستخدام المُحدِّد nth-child: .earth-demo:hover li:nth-child(1) { transition-delay: 0s; } .earth-demo:hover li:nth-child(2) { transition-delay: 0.2s; } .earth-demo:hover li:nth-child(3) { transition-delay: 0.4s; } .earth-demo:hover li:nth-child(4) { transition-delay: 0.6s; } الشيفرة السابقة ستُضيف تأخيرًا إلى كل عنصرٍ من عناصر القائمة، ولأننا استخدمنا المُحدِّد * فيما سبق فهذا يعني أنَّ كل عنصر من هذه العناصر سيُطبَّق عليه تأثير الانتقال، وكل ما علينا تحديده هو زمن التأخير. هذه هي النتيجة النهائية: See the Pen &amp;amp;amp;amp;amp;amp;amp;amp;lt;a data-cke-saved-href=&amp;amp;amp;amp;amp;amp;amp;#39;https://codepen.io/donovanh/pen/qmNgXW/&amp;amp;amp;amp;amp;amp;amp;#39; href=&amp;amp;amp;amp;amp;amp;amp;#39;https://codepen.io/donovanh/pen/qmNgXW/&amp;amp;amp;amp;amp;amp;amp;#39;&amp;amp;amp;amp;amp;amp;amp;amp;gt;Planet hover effect part 3: with transition&amp;amp;amp;amp;amp;amp;amp;amp;lt;/a&amp;amp;amp;amp;amp;amp;amp;amp;gt; by Donovan Hutchinson (&amp;amp;amp;amp;amp;amp;amp;amp;lt;a data-cke-saved-href=&amp;amp;amp;amp;amp;amp;amp;#39;https://codepen.io/donovanh&amp;amp;amp;amp;amp;amp;amp;#39; href=&amp;amp;amp;amp;amp;amp;amp;#39;https://codepen.io/donovanh&amp;amp;amp;amp;amp;amp;amp;#39;&amp;amp;amp;amp;amp;amp;amp;amp;gt;@donovanh&amp;amp;amp;amp;amp;amp;amp;amp;lt;/a&amp;amp;amp;amp;amp;amp;amp;amp;gt;) on &amp;amp;amp;amp;amp;amp;amp;amp;lt;a data-cke-saved-href=&amp;amp;amp;amp;amp;amp;amp;#39;https://codepen.io&amp;amp;amp;amp;amp;amp;amp;#39; href=&amp;amp;amp;amp;amp;amp;amp;#39;https://codepen.io&amp;amp;amp;amp;amp;amp;amp;#39;&amp;amp;amp;amp;amp;amp;amp;amp;gt;CodePen&amp;amp;amp;amp;amp;amp;amp;amp;lt;/a&amp;amp;amp;amp;amp;amp;amp;amp;gt;. لقد فعلناها! حركة معقدة نسبيًا أنجزناها باستخدام خاصية transition وحيدة. تعلمنا في هذا الدرس كيف نخطط ونبني مثالًا متكاملًا يستخدم الانتقالات لإنشاء حركات معقدة، وحرصنا أن تكون الانتقالات سلسلة وتبدو بشكلٍ جميل. الخلاصة الخدع البسيطة التي نقوم بها عبر الحركات والانتقالات هي التي تجعل موقعنا مميزًا وجميلًا ترجمة –وبتصرّف– للمقال Transitions in space لصاحبه Donovan Hutchinson
  21. بروتوكول نقل الملفات FTP (اختصار للعبارة File Transfer Protocol) هو بروتوكول شبكي كان شائعًا جدًا فيما قد سلف لنقل الملفات بين الخادوم والعميل، لكن استبدلته الطرائق الأكثر أمانًا وسرعةً لنقل الملفات، فكثيرٌ من مستخدمي الإنترنت يتوقعون تنزيل الملفات مباشرةً من متصفح الويب عبر بروتوكول http (أو https)، أما مستخدمي سطر الأوامر فيستعملون بروتوكولات أكثر أمانًا مثل scp أو sFTP. لكن ما يزال بروتوكول FTP مستخدمًا في التطبيقات القديمة أو للحالات التي لها متطلبات خاصة، فلو كنتَ تستطيع اختيار ما هو البروتوكول الذي ستستخدمه، فأنصحك بالنظر في بقية الخيارات الحديثة؛ لكن إذا كنت تحتاج إلى استخدام FTP فخادوم vsftpd هو خيارٌ ممتازٌ إذ إنَّ أداءه ممتاز وآمن ومستقر، ويوفِّر قدرًا كبيرًا من الحماية ضد كثيرٍ من المشاكل الأمنية الموجودة في خودايم FTP الأخرى، إضافةً إلى أنه الخادوم الافتراضي لخدمة FTP للعديد من توزيعات لينكس. سنتعلم في هذا الدرس كيفية ضبط خادوم vsftpd للسماح للمستخدمين برفع ملفاتهم إلى مجلد المنزل الخاص بهم باستخدام بروتوكول FTP مع تأمين معلومات الدخول عبر تشفير SSL/TLS. المتطلبات المسبقة خادوم أوبنتو 16.04 مع وصول إلى مستخدم يملك امتيازات الجذر عبر الأمر sudo: إذ سنُطبِّق الأوامر المذكورة في هذا الدرس عبر مستخدمٍ ليس جذرًا لكنه يمتلك امتيازات الجذر عبر الأمر sudo. يمكنك إنشاء مستخدم له امتيازات الجذر باستخدام الأمر sudo باتباع درس «الإعداد الابتدائي لخادوم أوبنتو 14.04](https://academy.hsoub.com/devops/servers/الإعداد-الابتدائي-لخادوم-أوبنتو-1404-r4/)». يمكننا أن نبدأ بتطبيق هذا الدرس بعد أن يكون الخادوم جاهزًا. الخطوة الأولى: تثبيت vsftpd سنبدأ بتحديث فهرس الحزم في خادومنا ثم تثبيت خادوم vsftpd: sudo apt-get update sudo apt-get install vsftpd عند إكمال التثبيت، فسننسخ ملف الضبط الافتراضي لنأخذ نسخةً احتياطيةً منه، ولنعدِّل الضبط كما نشاء. sudo cp /etc/vsftpd.conf{,.orig} بعد أن أخذنا نسخةً احتياطيةً من ملف الضبط، فحان الوقت الآن لإعداد الجدار الناري. الخطوة الثانية: فتح المنافذ الضرورية في الجدار الناري سنتحقق أولًا من حالة الجدار الناري لنرى إن كان مفعلًا أم لا، فإذا كان مفعلًا فعلينا السماح باتصالات FTP عبره لكي لا نواجه مشاكل عندما نجرِّب الخادوم. sudo ufw status نجد من الناتج الآتي أنَّ خدمة SSH مسموحٌ لها فقط: Status: active To Action From -- ------ ---- OpenSSH ALLOW Anywhere OpenSSH (v6) ALLOW Anywhere (v6) لاحظ أنَّ الناتج قد يكون مختلفًا عندك، فقد لا توجد أيّة قواعد لجدارك الناري أو لديك قواعد إضافية. ولمّا كان الجدار الناري لا يسمح إلا لاتصالات SSH فعلينا إضافة قواعد للسماح لاتصالات FTP، وسنحتاج إلى فتح المنفذين 20 و 21 لخدمة FTP، والمنفذ 990 لنستعمله لاحقًا عندما نضبط تشفير TLS، والمنافذ من 40000 إلى 50000 كمنافذ غير المباشرة (passive ports) التي نخطط لضبطها لاحقًا في ملف الضبط: sudo ufw allow 20/tcp sudo ufw allow 21/tcp sudo ufw allow 990/tcp sudo ufw allow 40000:50000/tcp sudo ufw status يجب أن تبدو قواعد الجدار الناري كما يلي: Status: active To Action From -- ------ ---- OpenSSH ALLOW Anywhere 990/tcp ALLOW Anywhere 20/tcp ALLOW Anywhere 21/tcp ALLOW Anywhere 40000:50000/tcp ALLOW Anywhere OpenSSH (v6) ALLOW Anywhere (v6) 20/tcp (v6) ALLOW Anywhere (v6) 21/tcp (v6) ALLOW Anywhere (v6) 990/tcp (v6) ALLOW Anywhere (v6) 40000:50000/tcp (v6) ALLOW Anywhere (v6) يمكننا الانتقال إلى الخطوة التالية بعد تثبيتنا لخادوم vsftpd وفتح المنافذ الضرورية. الخطوة الثالثة: تهيئة مجلد المنزل للمستخدم سنُنشِئ في هذا الدرس مستخدمًا جديدًا، لكن قد يكون لديك مستخدم موجود في نظامك ويحتاج إلى وصول FTP، وسنحرص على الحفاظ على وصول المستخدم إلى بياناته، لكن مع ذلك أنصحك بإنشاء مستخدم جديد إلى أن تضبط الخادوم وتجرّبه. سنُنشِئ بدايةً مستخدمًا جديدًا للتجربة: sudo adduser sammy أسنِد كلمة المرور إلى الحساب عند طلبها، ويمكنك تجاهل بقية الأسئلة بالضغط على زر Enter. تكون خدمة FTP أكثر أمانًا إذا كان المستخدمون محدودين بمجلدٍ معيّن، ويمكن لخادوم vsftpd فعل ذلك باستخدام chroot، فعند تفعيل chroot للمستخدمين المحليين، فلن يسمح لهم بالوصول إلى شيءٍ خارج مجلد المنزل الخاص بهم افتراضيًا، لكن خادوم vsftpd يحاول تأمين المجلد بعدم السماح بالكتابة عليه من قبل المستخدم (عبر سطر الأوامر)، ولا بأس بذلك للمستخدمين الجدد الذين يجب أن يتصلوا عبر FTP فقط، لكن إذا كان لدينا مستخدم موجود مسبقًا ويجب أن يستطيع الكتابة إلى مجلد المنزل الخاص به عبر سطر الأوامر فهذا لن يكون مناسبًا أبدًا. وبدلًا من إزالة إذن الكتابة من مجلد المنزل، فسنُنشِئ مجلد ftp لكي يكون chroot وسنُنشِئ داخله مجلد files يمكن الكتابة عليه ليحتوي على الملفات. لنُنشِئ مجلد ftp ونضبط ملكيته ونحذف إذن الكتابة منه بالأوامر الآتية: sudo mkdir /home/sammy/ftp sudo chown nobody:nogroup /home/sammy/ftp sudo chmod a-w /home/sammy/ftp لنتأكد من الأذونات: sudo ls -la /home/sammy/ftp الناتج: total 8 4 dr-xr-xr-x 2 nobody nogroup 4096 Aug 24 21:29 . 4 drwxr-xr-x 3 sammy sammy 4096 Aug 24 21:29 .. لنُنشِئ الآن المجلد الذي يحتوي على الملفات التي ستُرفَع ونضبط ملكيته إلى المستخدم: sudo mkdir /home/sammy/ftp/files sudo chown sammy:sammy /home/sammy/ftp/files لنتحقق أيضًا من أذونات المجلد files: sudo ls -la /home/sammy/ftp الناتج: total 12 dr-xr-xr-x 3 nobody nogroup 4096 Aug 26 14:01 . drwxr-xr-x 3 sammy sammy 4096 Aug 26 13:59 .. drwxr-xr-x 2 sammy sammy 4096 Aug 26 14:01 files وفي النهاية، لنضف ملفًا باسم test.txt لكي نستخدمه عند التجربة لاحقًا: echo "vsftpd test file" | sudo tee /home/sammy/ftp/files/test.txt بعد أن أصبح مجلد ftp آمنًا، وأعطينا المستخدم الأذونات اللازمة على مجلد files، فيمكننا الاهتمام الآن بموضوع الضبط. الخطوة الرابعة: ضبط وصول FTP خطتنا هي السماح للمستخدم الذي يملك وصولًا محليًا إلى سطر الأوامر بالاتصال عبر FTP، وهنالك خيارا ضبط رئيسيان مضبوطان في ملف vsftpd.conf. لنبدأ أولًا بفتح ملف الضبط للتأكد أنَّ التعليمات المذكورة فيه تُطابِق ما يلي: sudo nano /etc/vsftpd.conf محتوى الملف: . . . # Allow anonymous FTP? (Disabled by default). anonymous_enable=NO # # Uncomment this to allow local users to log in. local_enable=YES . . . علينا الآن تغيير بعض القيم في الملف، ولكي نسمح للمستخدم برفع الملفات فسنزيل رمز التعليق قبل التعليمة write_enable لكي يصبح السطر كما يلي: . . . write_enable=YES . . . سنُزيل رمز التعليق قبل التعليمة chroot_local_user لمنع المستخدم الذي يتصل عبر FTP من الوصول إلى أيّة ملفات خارج المجلد المضبوط: . . . chroot_local_user=YES . . . علينا إضافة التعليمة user_sub_token لكي نستطيع وضع اسم المستخدم في مسار local_root، وهذا لكي يعمل الضبط دون مشاكل لهذا المستخدم ولأي مستخدم آخر قد نضيفه مستقبلًا: user_sub_token=$USER local_root=/home/$USER/ftp سنُحدِّد مجال المنافذ المستخدم لاتصالات FTP غير المباشرة (passive FTP) لكي نحرص على توافر اتصالات كافية: pasv_min_port=40000 pasv_max_port=50000 تذكر أننا فتحنا هذه المنافذ سابقًا في الجدار الناري، أي لو استخدمتَ مجالًا مختلفًا عمّا سبق فاحرص على تحديث ضبط الجدار الناري بما يتوافق مع ذلك. ولأننا نخطط للسماح بوصول إلى FTP لمستخدمين معينين، فسنعدِّل في الضبط لكي نسمح بالوصول إلى قائمة معيّنة من المستخدمين: userlist_enable=YES userlist_file=/etc/vsftpd.userlist userlist_deny=NO التعليمة userlist_deny تُحدِّد ما الذي يجب فعله مع المستخدمين المذكورين في القائمة، فلو ضُبِطَت إلى YES فسيمنعون من الوصول إلى FTP، وإذا كانت NO فلن يسمح بالوصول إلى FTP إلا للمستخدمين المذكورين في القائمة. بعد أن تنتهي من إضافة الأسطر السابق فاحفظ الملف واخرج من المحرر النصي. علينا الآن إنشاء ملف القائمة وإضافة اسم المستخدم إليه، يمكننا استخدام الخيار ‎-a الخاص بالأمر tee لإسناد السطر إلى نهاية الملف: echo "sammy" | sudo tee -a /etc/vsftpd.userlist لنتأكد من محتوى الملف: cat /etc/vsftpd.userlist الناتج: sammy أعد تشغيل الخادوم لتطبيق التغييرات التي أجريناها في ملف الضبط: sudo systemctl restart vsftpd نحن جاهزون الآن للتجربة. الخطوة الخامسة: تجربة الوصول إلى FTP ضبطنا الخادوم للسماح للمستخدم sammy فقط بالوصول إلى FTP، لنتأكد من صحة ذلك. يجب ألّا يُسمَح للاتصال من المستخدمين المجهولين (anonymous users)، إذ عطلنا ذلك في الضبط، وسنجرِّب ذلك بمحاولة الاتصال بشكل مجهول، فإذا كان ضبطنا صحيحٌ فيجب ألّا يسمح لنا بالوصول إلى الخادوم: ftp -p 203.0.113.0 الناتج: Connected to 203.0.113.0. 220 (vsFTPd 3.0.3) Name (203.0.113.0:default): anonymous 530 Permission denied. ftp: Login failed. Ftp> أغلِق الاتصال: bye يجب ألّا يتمكن أيّ مستخدمٍ عدا sammy من الاتصال: وسنتحقق من ذلك عبر وضع اسم المستخدم الذي يملك امتيازات الجذر، ويجب ألّا يُسمَح له أيضًا قبل أن يُطلَب منه إدخال كلمة المرور: ftp -p 203.0.113.0 الناتج: Connected to 203.0.113.0. 220 (vsFTPd 3.0.3) Name (203.0.113.0:default): sudo_user 530 Permission denied. ftp: Login failed. Ftp> أغلِق الاتصال: bye يجب أن يتمكن المستخدم sammy من الاتصال، ومن قراءة وكتابة الملفات، لذا لنجرِّب ذلك: ftp -p 203.0.113.0 الناتج: Connected to 203.0.113.0. 220 (vsFTPd 3.0.3) Name (203.0.113.0:default): sammy 331 Please specify the password. Password: your_user's_password 230 Login successful. Remote system type is UNIX. Using binary mode to transfer files. Ftp> سندخل إلى المجلد files، ثم سنستعمل الأمر get لنقل ملف التجربة الذي أنشأناه سابقًا إلى جهازنا المحلي: cd files get test.txt الناتج: 227 Entering Passive Mode (203,0,113,0,169,12). 150 Opening BINARY mode data connection for test.txt (16 bytes). 226 Transfer complete. 16 bytes received in 0.0101 seconds (1588 bytes/s) ftp> سنحاول الآن إعادة رفع الملف باسمٍ جديد للتأكد من إمكانية الكتابة على المجلد: put test.txt upload.txt الناتج: 227 Entering Passive Mode (203,0,113,0,164,71). 150 Ok to send data. 226 Transfer complete. 16 bytes sent in 0.000894 seconds (17897 bytes/s) أغلِق الاتصال: bye بعد أن تأكدنا أنَّ الخادوم يعمل كما ضبطناه، فيمكننا إتباع إجراءات إضافية لتأمين الخادوم. الخطوة السادسة: جعل عمليات النقل آمنة لما كان بروتوكول FTP لا يشفِّر أيّة بيانات عند نقلها، بما في ذلك معلومات المستخدم، فعلينا تفعيل تشفير SSL/TLS لتأمين تلك البيانات، وأوّل خطوة هي إنشاء شهادات SSL لاستخدامها مع vsftpd. سنستخدم openssl لإنشاء شهادة جديدة، ونستخدم الخيار ‎-days لجعلها صالحةً لمدة سنة، وسنضيف –في الأمر نفسه– مفتاح ‎2048-bit RSA خاص، ثم بإسناد القيمة ذاتها إلى الخيارين ‎-keyout و ‎-out فستكون الشهادة والمفتاح الخاص في الملف نفسه. هذا هو الأمر الذي سنُطبِّقه: sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/vsftpd.pem -out /etc/ssl/private/vsftpd.pem سيُطلَب منّا توفير معلومات عن الشهادة التي سنُنشِئها، ضع المعلومات الخاصة بك عند الإجابة عن الأسئلة: Generating a 2048 bit RSA private key ............................................................................+++ ...........+++ writing new private key to '/etc/ssl/private/vsftpd.pem' ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:US State or Province Name (full name) [Some-State]:NY Locality Name (eg, city) []:New York City Organization Name (eg, company) [Internet Widgits Pty Ltd]:Hsoub Organizational Unit Name (eg, section) []: Common Name (e.g. server FQDN or YOUR name) []: Email Address []: بعد أن أنشأنا الشهادات، فعلينا الآن تعديل ضبط vsftpd مجددًا: sudo nano /etc/vsftpd.conf ستجد قرب نهاية الملف سطرين يبدآن بالسابقة rsa_‎، أضف قبلهما رمز التعليق ليصبحا كما يلي: # rsa_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem # rsa_private_key_file=/etc/ssl/private/ssl-cert-snakeoil.key أضف السطرين الآتيين بعدهما، اللذان يشيران إلى الشهادة والمفتاح الخاص اللذين أنشأناهما سابقًا: rsa_cert_file=/etc/ssl/private/vsftpd.pem rsa_private_key_file=/etc/ssl/private/vsftpd.pem علينا الآن جعل استخدام SSL إجباريًا، مما يمنع العملاء غير القادرين على التعامل مع تشفير TLS من الاتصال بخادومنا، وهذا ضروري للحرص على تشفير جميع المعلومات المنقولة لكن قد يجبر المستخدم على تغيير عميل الاتصال. عدِّل قيمة التعليمة ssl_enable إلى YES: ssl_enable=YES أضف بعد ذلك الأسطر الآتية لمنع الاتصالات المجهولة عبر SSL، ولإجبار استخدام SSL لتسجيل الدخول ولنقل البيانات: allow_anon_ssl=NO force_local_data_ssl=YES force_local_logins_ssl=YES علينا ضبط الخادوم استخدام TLS بدلًا من SSL عبر إضافة الأسطر الآتية: ssl_tlsv1=YES ssl_sslv2=NO ssl_sslv3=NO علينا بعد ذلك إضافة تعليمتين إضافيتين، الأولى تسمح بعدم استخدام SSL عند إعادة استخدام الجلسة (session reuse) لأنها لا تعمل مع أغلبية عملاء FTP، وسنطلب استخدام حزم عالية التشفير، وهذا يعني أنَّ طول المفتاح أكبر أو يساوي 128 بت: require_ssl_reuse=NO ssl_ciphers=HIGH بعد أن تنتهي من التعديلات السابقة، فاحفظ الملف وأغلق المحرر. علينا الآن إعادة تشغيل الخادوم لتأخذ التعديلات مجراها: sudo systemctl restart vsftpd لم نعد نتمكن الآن من استخدام عميل FTP غير الآمن الذي يعمل من سطر الأوامر، فلو جربناه فسنشاهد الناتج الآتي: ftp -p 203.0.113.0 Connected to 203.0.113.0. 220 (vsFTPd 3.0.3) Name (203.0.113.0:default): sammy 530 Non-anonymous sessions must use encryption. ftp: Login failed. 421 Service not available, remote server has closed connection ftp> سنتحقق من الاتصال في الخطوة القادمة باستخدام عميل يدعم تشفير TLS. الخطوة السابعة: تجربة الاتصال عبر TLS باستخدام FileZilla أغلبية عملاء FTP الحديثين يدعمون تشفير TLS، وسنشرح كيفية الاتصال عبر عميل FileZilla لأنه يعمل على جميع أنظمة التشغيل؛ راجع توثيق عميلك المفضل لتعرف كيف يمكن الاتصال عبر TLS فيه. عندما تفتح برنامج FileZilla فسنجد أيقونة Site Manager تحت قائمة File مباشرةً، أي أنَّها أوّل أيقونة في الشريط العلوي. اضغط عليها: ستُفتَح نافذة جديدة، اضغط فيها على زر «New Site» في الركن السفلي الأيسر: ستجد ظهور «New site» تحت أيقونة «My Sites»، يمكنك تسمية الموقع الآن أو إعادة تسميته لاحقًا بالضغط على زر «Rename». عليك أن تملأ حقل «Host» بعنوان IP أو اسم نطاق الموقع، وعليك أن تختار «Require explicit FTP over TLS» في قائمة «Encryption» أما لخيار «Logon Type» فاختر «Ask for password»، ثم أدخِل اسم المستخدم في حقل «User»: اضغط على زر «Connect» في أسفل النافذة، وستُسأل عن كلمة مرور المستخدم: اضغط على «OK» لتتصل، يجب أن يكون اتصالك مع الخادوم مشفرًا بتشفير TLS/SSL. بعد أن تقبل الشهادة، فانقر نقرًا مزدوجًا على مجلد files واسحب الملف upload.txt وأفلته في القسم اليساري من البرنامج لكي تبدأ بتنزيله: بعد ذلك، يمكنك أن تنقر بالزر الأيمن على الملف المحلي وتعيد تسميته إلى upload-tls.txt وتسحبه مجددًا إلى الخادوم لكي ترفعه عليه. لقد تمكنا من تنزيل الملفات ورفعها بأمان مع تفعيل تشفير SSL/TLS. الخطوة الثامنة: تعديل الوصول إلى سطر الأوامر (خطوة اختيارية) إن لم تتمكن من استخدام تشفير TLS بسبب محدوديات العميل، فيمكنك تأمين الخادوم قليلًا بمنع مستخدم FTP من تسجيل الدخول إلى سطر الأوامر، إحدى الطرائق البسيطة لفعل ذلك هي إنشاء صدفة (shell) خاصة. أكرِّر أنَّ الخطوة السابقة لا توفِّر أيّ تشفير، وإنما الغرض منها هو تقليل الوصول إلى حسابات المستخدمين المسموح لهم بالاتصال عبر FTP فقط. علينا أولًا إنشاء ملف باسم ftponly في مجلد ‎/bin: sudo nano /bin/ftponly سنضيف الآن رسالةً تخبر المستخدم أنَّه ليس قادرًا على تسجيل الدخول، أضف ما يلي يلي إلى الملف: #!/bin/sh echo "This account is limited to FTP access only." عدِّل الأذونات لجعل الملف قابلًا للتنفيذ: sudo chmod a+x /bin/ftponly افتح الملف الذي يضم قائمةً بالصدفات (shells) الصالحة للاستخدام في النظام: sudo nano /etc/shells وأضف في نهاية الملف: . . . /bin/ftponly عدِّل الصدفة الافتراضية للمستخدم عبر الآمر الآتي: sudo usermod sammy -s /bin/ftponly جرِّب الآن تسجيل الدخول بحساب المستخدم sammy: ssh sammy@203.0.113.0 ستجد رسالةً شبيهةً بالرسالة الآتية: This account is limited to FTP access only. Connection to 203.0.113.0 closed. هذا يؤكد أنَّ المستخدم غير قادر على تسجيل الدخول إلى الخادوم عبر الأمر ssh، ويُسمَح له بالوصول إلى FTP فقط. الخلاصة لقد شرحنا في هذا الدرس كيفية ضبط خادوم FTP للمستخدمين الذين يملكون حسابًا محليًا على النظام، أما إذا أردتَ استخدام مصدر خارجي للاستيثاق، فيمكنك إلقاء نظرة على دعم خادوم vsftpd للمستخدمين الوهميين (virtual users). هنالك خياراتٌ واسعة لدعم استخدام PAM مما يجعلك قادرًا على إدارة المستخدمين في نظام استيثاق مختلف مثل LDAP أو Kerberos. ترجمة –وبتصرّف– للمقال How To Set Up vsftpd for a User’s Directory on Ubuntu 16.04لصاحبته Melissa Anderson
  22. هنالك الكثير من الواجهات البرمجية المبنية على JSON التي توفر وصولًا عبر دوال من جهة الخادوم فقط، فلو أردنا استخدام عميل JavaScript مع تلك الواجهات البرمجية الخارجية، فسنحتاج إلى ضبط خادومنا للوصول إلى تلك الواجهات البرمجية الخارجية ثم تخديم تلك البيانات عبر Ajax أو عبر نقطة نهاية (endpoint) خاصة بك. ولحسن الحظ، فعل ذلك سهلٌ جدًا في ووردبريس. سأريك في هذا الدرس كيفية الحصول على أجور الشحن عبر Easy Post API باستخدام نقطة نهاية في ووردبريس. لتبسيط هذا الدرس (وجعله قصيرًا) فسنرسل ستة معاملات (وهو عدد كاف لواجهة Easy Post البرمجية لإعادة أجور الشحن): عرض الطرد ارتفاع الطرد طول الطرد وزن الطرد كود Zip للمرسل كود Zip للمستقبل ضبط نقطة نهاية JSON أوّل خطوة هي ضبط المسار الذي ستُخدَّم منه بيانات JSON، وفي هذه الحالة هو: /wp-json/easy-post/v1/rates/ الشيفرة: /** * Sets up a JSON endpoint at /wp-json/easy-post/v1/rates/ */ function easy_post_api_init() { $namespace = 'easy-post/v1'; register_rest_route( $namespace, '/rates/', array( 'methods' => 'GET', 'callback' => 'easy_post_return_rates', ) ); } add_action( 'rest_api_init', 'easy_post_api_init' ); عرض البيانات تتوقع نقطة النهاية إرسال ست قيم من البيانات كطلبية، وستستخدمها للحصول على البيانات من الواجهة البرمجية الخارجية، فلو لم تكن تلك القيم موجودةً فستستعمل القيم الافتراضية، وسنحرص أن تكون تلك القيم رقميةً فقط. مثال عن رابط URL مع قيم لكل المعاملات: /wp-json/easy-post/v1/rates/?zip=78701&width=10&length=10&height=10&weight=10&shipping_zip=78751 الشيفرة: /** * عرض مخرجات Easy Post عبر النهاية الطرفية * * نستخدم القيم المبدئية في حال عدم ذكر بيانات في الطلب، إلا أن إعادة خطأ ربما تكون فكرة أفضل. * Valid URL: /wp-json/easy-post/v1/rates/?zip=78701&width=10&length=10&height=10&weight=10&shipping_zip=78751 */ function easy_post_return_rates( WP_REST_Request $request ) { // الإعدادات المبدئية للشحن $defaults = array( 'zip' => 78701, 'width' => 10, 'length' => 10, 'height' => 10, 'weight' => 16, 'shipping_zip' => 94105 ); // الحصول على المعطيات من الطلب $params = $request->get_query_params(); // التأكد من توفر جميع المعطيات $params = array_replace( $defaults, $params ); // تنظيف المفاتيح $params = array_map( "absint", $params ); // إنشاء قيم مفاتيح فريدة لتحديد قيم البيانات المؤقتة transient أو العثور عليها $key = 'easy_post_' . implode( "_", $params ); /** * نحتفظ بالإجابة في قيمة مؤقتة transient ضمن قاعدة بيانات ووردبريس * لتجنب إرسال الكثير من الطلبات في وقت قصير * * تنتهي صلاحية هذه البيانات بعد عشر دقائق */ if ( false === ( $data = get_transient( $key ) ) ) { $data = easy_post_make_request( $params ); $response = new WP_REST_Response( $data ); // التخبئة لمدة عشر دقائق set_transient( $key, $response, 60 * 10 ); } else { // إرجاع القيمة المخبّأة return get_transient( $key ); } return $response; } الحصول على البيانات الدالة easy_post_make_request()‎ هي الدالة التي تطلب البيانات من الواجهة البرمجية الخارجية، والغرض من أغلبية الشيفرات التي هنا هي تجهيز الطلبية ضمن مصفوفة يمكن لواجهة Easy Post البرمجية أن تعالجها: /** *العثور على البيانات من الواجهة البرمجية لـ Easy Post باستخدام الدالة wp_remote_post */ function easy_post_make_request( $params ) { $to_address = array( 'zip' => $params['shipping_zip'], 'country' => 'US' ); $from_address = array( 'zip' => $params['zip'], 'country' => 'US' ); $parcel = array( 'length' => $params['length'], 'width' => $params['width'], 'height' => $params['height'], 'weight' => $params['weight'] ); $request = array( 'shipment' => array( 'to_address' => $to_address, 'from_address' => $from_address, 'parcel' => $parcel ) ); $args = array( 'method' => 'POST', 'blocking' => true, 'headers' => array( 'Content-Type' => 'application/json' ), 'timeout' => 15, 'sslverify' => false, 'body' => json_encode( $request ) ); // ضع مفتاح API الخاص بك مكان EASYPOST_TEST_API_KEY $api_url = 'https://' . EASYPOST_TEST_API_KEY . ':@api.easypost.com/v2/shipments'; $response = wp_remote_post( $api_url, $args ); if ( ! is_wp_error( $response ) ) { $response = json_decode( $response['body'] ); } return $response; } استخدام البيانات عندما نحاول الوصول إلى نقطة النهاية التي أنشأناها، فإن بيانات JSON التي جلبناها من Easy Post ستظهر لنا: يمكنك الآن الحصول على أجور الشحن ديناميكيًا في موقعك باستخدام نقطة النهاية التي أنشأناها في هذا الدرس. يمكنك الاطلاع على كامل الشيفرة هنا. ترجمة –وبتصرّف– للمقال How to Set JSON Endpoints in WordPress to Access an External API لصاحبه Devin Price حقوق الصورة البارزة محفوظة لـ Freepik
  23. أعلم أنَّه ليس من السهل الاعتراف بالأخطاء التي تقوم بها، لكن الخطأ هو جزءٌ رئيسي من كل عمليات التعليم، بدءًا من التعلم كيفية المشي إلى تعلم لغة برمجة جديدة مثل بايثون. هذه قائمة بثلاثة أخطاء التي وقعتُ فيها أثناء تعلمي لبايثون، عرضتها هنا لكي يحاول مبرمجو بايثون الجدد تفاديها وعدم الوقوع فيها، وهذه الأخطاء أدت إلى حدوث مشاكل كبيرة أخذت من وقتي ساعات حتى حللتها. الخطأ الأول: استخدام أنواع البيانات القابلة للتغير كوسائط افتراضية عند تعريف الدالة لنقل أنَّ لديك دالة صغيرة التي تبحث عن روابط في الصفحة الحالية وتستطيع إضافتها إلى قائمة (list) معيّنة. def search_for_links(page, add_to=[]): new_links = page.search_for_links() add_to.extend(new_links) return add_to لا يبدو أنَّ هنالك أيّ خطأ في الدالة السابقة، وهذا صحيح، فهي تعمل لكن هنالك مشاكل فيها؛ فلو مررنا قائمةً (list) إلى المعامل add_to فستعمل كما يجب، لكن ماذا يحدث لو تركنا القيمة الافتراضية دون تعديل؟ جرِّب تنفيذ الشيفرة الآتية: def fn(var1, var2=[]): var2.append(var1) print var2 fn(3) fn(4) fn(5) ستتوقع رؤية الناتج الآتي: [3] [4] [5] لكنك ستُفاجأ بالناتج الآتي: [3] [3, 4] [3, 4, 5] لماذا؟! يمكنك أن تستنتج أن القائمة (list) نفسها ستستعمل في كل مرة، فعندما نكتب دالة مثل الدالة السابقة في بايثون فسيتم تهيئة القائمة (list) كجزءٍ من تعريف الدالة، أي أنها لن تُهيّئ كل مرة تُستدعى فيها الدالة، وهذا يعني أنَّ الدالة ستحتفظ بكائن القائمة نفسه مرارًا وتكرارًا، ما لم تُحدِّد قيمةً أخرى له: fn(3, [4]) الناتج: [4, 3] الناتج يماثل ما قد توقعناه. الطريقة الصحيحة لفعل ذلك هي: def fn(var1, var2=None): if not var2: var2 = [] var2.append(var1) لنعد كتابة الدالة السابقة: def search_for_links(page, add_to=None): if not add_to: add_to = [] new_links = page.search_for_links() add_to.extend(new_links) return add_to نقلنا عملية التهيئة من مكان تعريف الدالة إلى داخلها مما يعني أنَّ عملية التهيئة ستتم في كل مرة تُشغَّل فيها الدالة. لاحظ أنَّ ذلك ليس ضروريًا إذا كنتَ تستعمل أنواع بيانات غير قابلة للتعديل مثل tuple أو string أو int. وهذا يعني أنَّك تستطيع تعريف دالة كما في الدالة الآتية دون إمكانية حدوث أخطاء غير متوقعة: def func(message="my message"): print message الخطأ الثاني: استخدام أنواع البيانات القابلة للتعديل كمتغيرات في الأصناف هذا الخطأ شبيهٌ كثيرًا بالخطأ السابق. تمعّن في الشيفرة الآتية: class URLCatcher(object): urls = [] def add_url(self, url): self.urls.append(url) الشيفرة السابقة تبدو طبيعية جدًا، فلدينا كائن لتخزين روابط URL، وعند استدعائنا للدالة add_url فسنمرر إليها رابط URL لتخزِّنه، صحيح؟ لنجرِّبها: a = URLCatcher() a.add_url('http://www.google.') b = URLCatcher() b.add_url('http://www.bbc.co.') الناتج: b.urls ['http://www.google.com', 'http://www.bbc.co.uk'] a.urls ['http://www.google.com', 'http://www.bbc.co.uk'] ما هذا؟! لم نتوقع ذلك. إذ أنشأنا كائنين منفصلين a و b، وأسندنا رابطًا للكائن a مختلفًا عن رابط الكائن b، فكيف امتلك كلا الكائنين الرابطين نفسهما؟ اتضح أنَّ هذه المشكلة شبيهة جدًا بالمشكلة في المثال الأول، فقائمة (list) عناوين URL قد تمت تهيئتها عند تعريف الصنف (class)، وبالتالي أمست جميع الكائنات المُنشَأة من ذاك الصنف تستعمل القائمة نفسها. هنالك بعض الحالات التي نستفيد فيها من هذه الميزة، لكنها ستضرك في أغلبية الأوقات، فلو أردتَ تخزين بيانات كل كائن على حدة فيمكنك تعديل الشيفرة لتصبح كما يلي: class URLCatcher(object): def __init__(self): self.urls = [] def add_url(self, url): self.urls.append(url) أصبحت قائمة urls تُهيّئ عند إنشاء الكائن، وعندما نُنشِئ كائنين فستُهيّئ قائمتان منفصلتان. الخطأ الثالث: عملية إسناد قيم إلى نوع بيانات قابل للتعديل هذا الخطأ أربكني لفترة حتى فهمته، دعنا نستعمل نوع بيانات قابل للتعديل مثل dict: a = {'1': "one", '2': 'two'} لنفترض أننا نريد أخذ قيمة المتغير a واستعمالها في مكانٍ آخر دون تعديل القيمة الأصلية: b = a b['3'] = 'three' أليس هذا بسيطًا؟ لننظر الآن إلى القيمة المخزّنة في المتغير a التي لم نُعدِّلها قط: {'1': "one", '2': 'two', '3': 'three'} ماذا؟! كيف ستبدو قيمة المتغير b إذًا؟ {'1': "one", '2': 'two', '3': 'three'} دعنا نعود خطوةً إلى الوراء وننظر ماذا يحدث لو استعملنا أنواع البيانات غير القابلة للتعديل، مثل tuple: c = (2, 3) d = c d = (4, 5) قيمة c هي: (2, 3) بينما قيمة d هي: (4, 5) لقد جرى كل شيءٍ على ما يرام، لذا ماذا حدث في مثالنا؟ عند استخدام أنواع البيانات القابلة للتعديل فسنحصل على شيءٍ شبيهٍ بالمؤشرات (pointers) في لغة C، فعندما قلنا أنَّ b = a في الشيفرة السابقة فهذا يعني أنَّ المتغير b أصبح يُشير إلى a، وكلا المتغيرين يشير إلى نفس الكائن في ذاكرة بايثون؟ هل هذا مألوف لديك؟ ذلك لأن هذه المشكلة شبيهة بالمشاكل السابقة، وكنتُ أنوي تسمية هذا الدرس باسم «المشاكل التي تحدث مع أنواع البيانات القابلة للتعديل». هل يحدث الأمر نفسه مع القوائم (list)؟ نعم. وكيف سنلتف على المشكلة؟ حسنًا، يجب أن نكتب الشيفرة الآتية التي تنسخ القائمة: b = a[:] السطر السابق سيؤدي إلى نسخ مرجعية كل عنصر من عناصر القائمة ووضعه في قائمة جديدة، لكن لنأخذ حِذرنا فإذا كان نوع بيانات أحد الكائنات الموجودة في القائمة قابلًا للتعديل فسيؤدي ذلك إلى الحصول إلى مرجعية لتلك الكائنات بدلًا من نسخها. تخيل وجود قائمة على قطعة من الورق، ففي المثال الأصلي كان ينظر الشخص A والشخص B إلى الورقة نفسها، فلو عدّل شخصٌ ما القائمةَ فسيرى كلا الشخصين التعديلات التي أجريت على القائمة، وعندما نسخنا المرجعيات فأصبح لكل شخصٍ قائمته الخاصة به، لكن لنفترض أنَّ تلك القائمة تحتوي على أماكن يمكن البحث فيها عن طعام، فلو كانت «الثلاجة» موجودة في القائمة فحتى لو نسخها الشخص A و B فما تزال تشير إلى الثلاجة نفسها؛ فلو أتى الشخص A وعدّل محتويات الثلاثة (لنفترض أنه أكل جميع الحلويات فيها) فسيلاحظ الشخص B أن الحلويات قد اختفت من الثلاثة. ولا توجد طريقة سهلة للالتفاف على هذه المشكلة، وهذا أمرٌ مهمٌ عليك تذكره عندما تبرمج لكي تكتب شيفرتك بطريقة لا تسبِّب أيّة مشاكل. تعمل أنواع dict بنفس الطريقة، ويمكنك إنشاء نسخة كاملة باستعمال الدالة copy()‎: b = a.copy() أكرِّر أنَّ ذلك سيُنشِئ متغيرًا جديدًا من نوع dict يُشير إلى نفس العناصر الموجودة في المتغير الأصلي، وبالتالي لو كان لدينا قائمتان متماثلتين وعدّلنا كائنًا قابلًا للتعديل مُشار إليه عبر مفتاح موجود في المتغير a فيمكن معرفة تلك التعديلات من داخل المتغير b. الإشكاليات التي تواجهنا مع أنواع البيانات القابلة للتعديل تكون نتيجةً لمرونة تلك الأنواع، حيث لا تُشكِّل أيٌّ مما سبق مشكلةً حقيقة، وإنما هي أمور ضرورية يجب أخذها بالحسبان لتنجب المشاكل. وعمليات النسخ الكاملة التي ذكرناها آنفًا لن تكون ضروريةً في 99% من الحالات، أي يجب تعديل برنامجك لكي لا يحتاج إلى استخدام تلك النسخ من الأساس. ترجمة –وبتصرّف– للمقال ‎3 mistakes to avoid when learning to code in Python لصاحبه Pete Savage
  24. التدوين الصوتي هو طريقةٌ رائعةٌ لمشاركة المعلومات وبناء المجتمعات التي تتشارك بالاهتمامات، وهذا الدرس يمثِّل دليلًا لكيفية البدء بالتدوين الصوتي. السبب الرئيسي وراء قدرتي على إعطاء نصائح بالتدوين الصوتي هو أنني أدوِّن صوتيًا لما يقارب ثلاث سنوات. مدونتي الصوتية باسم Sysadministrivia تتضمن كلامًا عن إدارة الأنظمة. ما هو التدوين الصوتي؟ انتشر التدوين الصوتي انتشارًا كبيرًا في مختلف المجالات وأصبح شائعًا جدًا، إذ ينشر بعض الأشخاص تدوينات صوتية لمختلف جوانب حياتهم مثل جلسات الألعاب الإلكترونية التي يلعبونها. يعمل التدوين الصوتي بتسجيلك للصوت (أو الفيديو لأن البرمجيات قد تطورت حديثًا)، واستخدام صيغة بيانات قياسية لتنبيه المستمعين أنَّك نشرت التدوينة الصوتية podcast مباشرةً. صيغة RSS (المسؤولة عن إذاعة خبر نشر التدوينة) ومواصفة XML معينة (وهي صيغة البيانات القياسية) تجعل إنشاء هذه التنبيهات التلقائية أمرًا ممكنًا. كيف أستطيع البدء بالتدوين الصوتي؟ كل ما يلزمك هو حاسوب ونظام تشغيل. فمن البدهي أنَّك تحتاج إلى حاسوب، ولا يهم ما هو نظام تشغيلك، لكنني أنصحك باستخدام لينكس أو نظام من عائلة BSD. برمجيات تسجيل وتحرير الصوت يمكنك استخدام برمجيات متعددة المنصات (أي تعمل على أكثر من نظام تشغيل) لتسجيل الصوت مثل Audacity أو إذا كنتَ تفضِّل تحكمًا أكثر فانظر إلى Ardour. تحتوي صفحة hosts في موقع Sysadministrivia على قائمة بمواصفات العتاد التي نستعملها في التدوين (بما في ذلك الميكروفون والسماعات الرأسية والمعدات الأخرى)؛ لا أنصحك بشراء عتاد معيّن إذ هنالك مختلف أنواع الميكروفونات لتسجيل الكلام. سنحتاج إلى ميكروفون على شكل قلب (cardioid أو supercardioid) إذا كنت ستتحدث بالقرب من الميكروفون ، أو استخدام ميكروفون أحادي الاتجاه (omnidirectional) إذا أردتَ تسجيل ما يدور في غرفة مليئة بالأشخاص باستعمال ميكروفون وحيد، واحرص على أن تكون تلك الميكروفونات ذات مكثّف (condenser) لأنها أفضل لتسجيل الحديث، وسماعات الرأس ضرورية إذا كنت ستدون مع شخص آخر ليس في غرفتك نفسها. لمزيدٍ من المعلومات حول ذلك فأنصحك بقراءة قسم «Uploading» في التدوينة How to run your own podcast. استضافة الموقع ستحتاج إلى استضافة لتضع فيها الملفات الصوتية وملف XML الذي سيذيع خبر نشر التدوينات الجديدة، وموقع إلكتروني (ليس من الضروري امتلاك موقع، لكنني أنصحك بذلك بشدة). يمكنك بشكلٍ بديل أن ترفع الملفات الصوتية (أو الفيديو) إلى خدمة مثل YouTube أو SoundCloud واستخدام ميزة الاشتراك فيها لنشر خبر صدور صدور تدوينة جديدة؛ لكن دون استخدام RSS (أو XML) فهذا يعني أنَّ تدويناتك ليست تدوينات صوتية (podcast) تقنيًا، لأن هذه المواقع تطلب من مستخدميك أن يسجلوا حسابًا فيها، والتدوينات الصوتية يجب أن تسمح للمستمعين أن يبقوا مجهولين تمامًا. صيغة الملفات حسنًا، التدوينات الصوتية ليست «حرة» تمامًا، لاستخدام صيغة MPEG-1/2 Audio Layer III المعروفة باسمها المختصر MP3. هذه الصيغة محمية ببراءة اختراع ولا تتوافق مع مبادئ البرمجيات الحرة؛ لكن يمكن استخدام صيغة بديلة عنها وهي OGG لكنها ستجعل نشرك لها محدودًا، إلا أنَّ بإمكانك توفير تدوينة صوتية podcast و oggcast، وصيغة ملفات XML لهما متشابهة جدًا (لكن مع بعض الاختلافات التي يمكن تعلمها بسهولة). ملفات «التغذية» أفصل عادةً بين مختلف ملفات التغذية (feed files، التي تكون بصيغة XML)، لمختلف الخدمات مثل iTunes و Google Play والتدوينات الصوتية التقليدية (podcasts) و oggcasts. لاحظ أنَّ متصفح Firefox سيحاول تفسير (أو عرض) هذه الملفات باستخدام عميل RSS المضمّن فيه، ولعرض ملفات XML الفعلية فاستخدام الأمر curl أو wget مع الروابط السابقة، أو اعرض مصدر الصفحة في متصفحك. الطريقة السابقة تسمح لي باستخدام وسوم XML خاصة بكل خدمة، وللمزيد من التفاصيل التقنية راجع التدوينة How to run your own podcast. الموقع الإلكتروني أستخدمُ نظام إدارة محتوى باسم Textpattern مع بعض التعديلات، لكن الكثير من المدونين الصوتيين يستعملون ووردبريس مع إضافة PowerPress. كيف أوسِّع من جمهوري؟ الترويج Syndication أسهل طريقة لجلب مستمعين إلى تدويناتك الصوتية هي خدمات الترويج، فيوفر موقع DistroWatch ترويجًا لبعض تدوينات oggcast الصوتية. وأشهر الخدمات للترويج هي iTunes (لمزيدٍ من المعلومات راجع صفحة iTunes Connect Resources and Help page) و Google Play (زر صفحة Podcasts in Google Play Music page)، ويمكن أن تساعدك مواقع وخدمات الترويج الأصغر في زيادة متابعيك، لكن iTunes و Google Play هي أشهرها لسهولة الوصول إليها من مستخدمي iOS و أندرويد. وسائل التواصل الاجتماعي لا تنسَ التواصل المباشر على مواقع التواصل الاجتماعي، فيمكن أن تستعمل تويتر كطريقة أخرى لنشر تدويناتك الصوتية الحديثة، وسيسمح لك بالتواصل مباشرةً مع جمهورك. أمور متفرقة هنالك تقنيات أخرى لإشهار تدويناتك مثل بيع البضائع أو استضافة ضيوف في حلقاتك، والمشاركة في الأحداث المحلية والمنظمات الخيرية، لكن هذه التقنيات ستكون فعالة أكثر بعد أن يكون عندك جمهور. كيف أستفيد ماديًا من التدوين الصوتي؟ عليك أن تبدأ مشوارك في التدوين الصوتي لرغبتك في إنتاج ومشاركة محتوى مع فئة من الناس الذين يتشاركون الاهتمامات، فالإجابة باختصار هي: لن تتمكن من الاستفادة ماديًا (أو ربما تتمكن من ذلك إن كنتَ محظوظًا). لكن التدوين الصوتي في أغلبية الأوقات يكون نابعًا من رغبتك بمشاركة ما تعرفه مع الآخرين (كما في البرمجيات الحرة). ترجمة –وبتصرّف– للمقال A quick-start guide to podcasting لصاحبه Brent Saner
  25. سيأتي حينٌ يحس فيه كل مطوِّر وب جديد بأنَّ شيئًا ما ينقصه عندما يطلب منه أحد الخبراء أن يفتح سطر الأوامر، لكن لا بأس في ذلك، فلكنا مرّ بذلك عندما ذهب إلى منتدى أو اجتماع محلي للقاء مطوري وب الآخرين. هذا الدرس مناسب لكل شخص لا يعرف ما هو سطر الأوامر، وهو ملائمٌ أيضًا لمن له دراية بسطر الأوامر إذ سيتضمّن بعض التفاصيل والملاحظات والتي ستجدها مفيدة وستتعلم منها. لن يشرح هذا الدرس ماذا عليك أن تكتب في الطرفية (terminal، وهي نافذة البرنامج التي تصل إلى سطر الأوامر عبرها) للقيام بأمور معينة، وإنما الهدف هنا هو شرح المفاهيم الأساسية لجعل واجهة سطر الأوامر (command line interface) مألوفةً لك، وبعد أن تتمكّن من الأساسيات فستجد أن فهم أحد الأوامر سهلٌ جدًا وليس عسيرًا كما تظن. ظهرت واجهة سطر الأوامر قبل الواجهات الرسومية أوّل معلومة تساعدك في فهم سطر الأوامر هي استيعاب أنَّ سطر الأوامر قد أتى في الفترة الأولى من وجود الحواسيب، إذ إنَّ البرامج الحالية رسومية فهي تعرض عدِّة نوافذ تابعة للبرنامج للمستخدم، ويظهر أيضًا «سطح المكتب» خلف تلك النوافذ. هذه النوافذ تساعد المستخدمين في استخدام الحاسوب، لكنها مجرد واجهة بسيطة وجميلة للتعامل مع نظام التشغيل. وقبل ظهور الواجهات الرسومية، كان هنالك ما يسمى «طرفيات»، والطرفية هي الوسيلة التي نصل فيها إلى سطر الأوامر (يجدر بالذكر أنَّ الطرفيات هي أجهزة فيزيائية كانت موجودة في الأيام الأولى للحواسيب، وهي جهاز يضم شاشة سوداء وتُظهِر نصًا أبيض، وفيها لوحة مفاتيح مدمجة)، تستطيع أن تقول أنَّ سطر الأوامر هو طريقةٌ مختلفة لتشغيل البرامج كما هي البرامج الرسومية في أنظمة ويندوز أو ماك أو لينكس. إلا أنَّ الاختلاف الرئيسي بينها وبين البرامج الرسومية هي أنَّ البرامج التي تعمل من سطر الأوامر لا توفر واجهةً جميلةً لتتعامل معها، ومستخدمو الحاسوب الذين عاصروا الطرفيات لديهم خبرةٌ كبيرةٌ مع الحواسيب، وتسمعهم يقولون أنَّ الواجهات الرسومية سببت في تقليل إنتاجيتهم، وما يزال أولئك الأشخاص موجودين في هذا العصر. تستطيع أن تستخدم الفأرة وتنقر نقرًا مزدوجًا على أيقونة البرنامج لتشغيله، أما في سطر الأوامر فعليك أن تكتب اسم الأمر (والذي هو اسم البرنامج في أغلبية الحالات)، وربما تضيف بضعة خيارات لتتحكم في سلوكه، ثم تنفِّذ الأمر. لاحظ أنني ذكرتُ استخدام الفأرة بوضوح في الفقرة السابقة وذلك لأنَّها أكبر الفروقات بين الواجهات الرسومية والسطرية، إذ لا تستعمل الفأرة في بيئة سطر الأوامر فالطرفيات كانت موجودة قبل انتشار استخدام الفأرة للتعامل مع الحاسوب، لذا ستجد أنَّ طريقة التفاعل الرئيسية مع سطر الأوامر هي استخدام لوحة المفاتيح. فرقٌ رئيسيٌ آخر هو أنَّ الواجهات الرسومية تكون في وضعية «الانتظار» غالب الوقت عندما يكون البرنامج «قيد التشغيل»، ولأنَّ البرامج التي تعمل من سطر الأوامر كانت من العصر الأول للحاسوب، فلم تكن هنالك مساحة فارغة لإظهار جميع البرمجيات «قيد التشغيل»، فهي تنفَّذ بسرعة كبيرة ثم تتوقف، أي أنَّ من غير الشائع أن يطول تنفيذ أمرٍ ما في سطر الأوامر أكثر من عدِّة ثواني (على الرغم من أنَّ بعضها قد يعمل لفترات طويلة مثل المحررات النصية). لنلخِّص الفروقات بين البرامج التي تعمل من سطر الأوامر والبرامج ذات الواجهة الرسومية: البرامج السطرية هي تطبيقات بسيطة تعمل مرة واحدة. على النقيض من أغلبية التطبيقات الرسومية التي تنتظر منك التفاعل معها، فإنَّ أغلبية التطبيقات السطرية تعمل بسرعة ثم تتوقف. تكون عادة الأوامر قصيرة لأن المستخدمين يريدون أن يكتبوا أقل ما يمكن لتنفيذ ما يشاؤون. لا تُستخدَم الفأرة (عادةً) في سطر الأوامر. الاختلافات بين أنظمة التشغيل يمكننا أن نعد سطر الأوامر على أنه طريقة مباشرة للتواصل مع الحاسوب، وهذا يتيح له قدرات أكثر، مما يجعله فعّالًا، لكن في المقابل هذا يعني أنَّ عليك معروفة المزيد من المعلومات حول طريقة عمل حاسوبك. أغلبية مستخدمي الحاسوب أولي المعرفة المتوسطة يعلمون أنَّ نظام ويندوز يختلف عن ماك ويختلف عن لينكس، لكنها يعلمون أنَّ بإمكانهم تشغيل متصفح Firefox أو برنامج ليبرأوفيس على تلك الأنظمة، لكن هذا ليس صحيحًا بالنسبة إلى سطر الأوامر. هنالك نوعان رئيسيان لسطر الأوامر عليك معرفتهما، النوع الأول هو سطر الأوامر للأنظمة الشبيهة بيونكس (Unix-like)، والنوع الثاني هو سطر أوامر نظام ويندوز. يُصنَّف نظام لينكس وماك على أنهما نظامان شبيهان بيونكس، وبالتي ستتعامل مع سطر الأوامر عبر ما يسمى «صدفة» (shell) باسم Bash (سنتحدث عن ذلك لاحقًا). صحيحٌ أنَّ هنالك اختلافات في طريقة التعامل مع مختلف أنظمة يونكس، لكنها ليست مهمة لمطوري الويب المحترفين، فهذه الاختلافات صغيرة ويمكنك تجاهلها إذا كنتَ مبتدئًا (لكن الحق يقال: ستواجه هذه الاختلاف في وقتٍ ما عندما تجد نفسك مرتاحًا في استخدام سطر الأوامر، وستجد أنَّ تلك الاختلافات لها أثرها). أما نظام ويندوز فهو خارج المنافسة في مجال سطر الأوامر، وهذا هو أحد الأسباب الرئيسية التي تجعل من مطوري الويب يهاجرون من ويندوز إلى لينكس أو ماك، إذ يختلف سطر الأوامر الموجود في نظام ويندوز عن بقية الأنظمة والأوامر ليست متشابهة، وسطر الأوامر فيه قديم وتراثي أضف إلى أنَّ مستخدمي ويندوز يخافون استخدامه. سطر الأوامر في ويندوز لا يمكني الجزم بواقع سطر أوامر ويندوز أو المنافع الآتية من استخدامه في أنظمة ويندوز (ويندوز 10 وما قبله) للقيام بمهام تطوير الويب من سطر الأوامر لأنني أستخدم لينكس منذ فترةٍ طويلة، لكنني سمعتُ شكوى ممن يستعملونه وصحيحٌ أنَّ أغلبية البرمجيات تعمل «نظريًا» في سطر أوامر ويندوز، لكنني سمعتُ أنها ليست عملية. أنا لا أقول لك أن تشتري جهاز ماك أو تثبِّت لينكس على جهازك (لكنني أحثّك على تجربة لينكس على أيّ حال) لتصبح مطوِّر ويب، لكنني أحب أن أشير إلى توافر ما يسمى «Bash on Ubuntu on Windows» في ويندوز 10 وبالتالي ستحصل على سطر أوامر شبيه بيونكس داخل نظام ويندوز، لذا جرِّبه وانظر إن كان مناسبًا لك. ولأغلبية أعمال تطوير الويب، خصوصًا في عالم البرمجيات مفتوحة المصدر الذي تقطنه ووردبريس، ستجد أنَّ الخواديم تُشغِّل إحدى توزيعات لينكس، وهذا يعني أنَّ عليك الاتصال مع الخواديم عبر سطر الأوامر، وهذا سهلٌ جدًا في ويندوز (عبر برمجية PuTTY) لكن هذه البرمجية غير مضمّنة افتراضيًا في ويندوز، وليست كمثيلاتها في بقية الأنظمة. العثور على الطرفية ذكرنا سابقًا أنَّك تستطيع الوصول إلى سطر الأوامر عبر الطرفية، والتي يمكنك تشغيلها في نظام ماك وأغلبية توزيعات لينكس التي توفِّر واجهةً رسوميةً عبر البحث عن تطبيقٍ باسم Terminal وتشغيله، ثم ستجد نافذة تتيح لك التفاعل مع حاسوبك بطريقة تختلف كثيرًا عمّا اعتدت عليه. أما في ويندوز فيسمى محاكي الطرفية باسم «موجِّه الأوامر» (Command Prompt)، الذي يسمح لك بتنفيذ البرامج بكتابة أمرٍ معيّن. يجدر بالذكر أنَّ هنالك أنواع مختلفة من محاكيات الطرفيات، فهنالك طرفية غنوم وطرفية كدي (Konsole) للينكس، وطرفية iTerm لنظام ماك، وهذه الطرفيات تختلف عن بعضها لكنها تؤدي الغرض نفسه ألا وهو الوصول إلى سطر الأوامر. أنواع الصدفات ملاحظة سريعة: هنالك أنواع مختلفة من الصدفات (وهي البرمجيات التي تتواصل عبرها مع سطر الأوامر)، فأغلبية الأشخاص يستخدمون صدفة باش (bash) (وإذا لم تكن تعرف ما هي الصدفة التي تعمل عليها فمن المرجح أنها باش). الاختلافات بين الصدفات هو موضوع معقد وخارج عن نطاق هذا الدرس، لكن كل ما أردتُ تنبيهك إليه هو أنَّك تشغِّل صدفة باش في نظامك (وليس ZSH أو FISH أو غيرها). كيف تصل إلى سطر أوامر خادومك آخر موضوع أريد أن أتحدث عنه في هذا الدرس هو أنَّك تستطيع أن تصل إلى سطر أوامر الخادوم نفسه (إلا إذا كان موقعك مستضافًا على استضافة مشتركة). لا تُشغِّل الخواديم واجهةً رسوميةً (السبب الرئيسي هو الأداء والحماية)، هذا يختلف عن حاسوبك الشخصي الذي يتيح لك التفاعل مع سطر الأوامر ومع الواجهات الرسومية في الوقت نفسه. في الحالات التي ترغب بالوصول فيها إلى سطر الأوامر في خادومك، فيمكنك فعل ذلك عبر SSH (اختصار للعبارة Secure Shell)، وهذه البرمجية تسمح لك بالوصول إلى سطر الأوامر في حاسوبٍ بعيد بأمان؛ وموضوع ضبط SSH خارج عن نطاق هذه المقالة، والغرض من ذكري له هو معرفة وجود طريقة للوصول إلى سطر الأوامر لحاسوبٍ بعيد بأمان. وأذكِّر أنَّ برمجية PuTTY التي ذكرناها سابقًا هي أشهر طريقة ليتصل عبرها مستخدمو ويندوز إلى SSH. أما مستخدمو ماك أو لينكس أو غيرهما فكل ما عليهم فعله للاتصال إلى حاسوبٍ بعيد هو استخدام الأمر ssh في سطر الأوامر. الخلاصة قد يبدو سطر الأوامر شيئًا غريبًا خصوصًا لمستخدمي الحاسوب الذين بدؤوا في استعماله بعد ظهور الواجهات الرسومية التي جعلت منه شيئًا غريبًا، لكن قد نضطر أحيانًا إلى استخدام سطر الأوامر لإنجاز بعض الأمور. أرجو أن تكون قد فهمتَ من المقالة أنَّ سطر الأوامر هو طريقة أخرى للتعامل مع الحاسوب، وقد تكون هي الطريقة الوحيدة للتعامل مع الحاسوب في بعض الحالات مثل الخواديم التي لا تُثبَّت عليها واجهة رسومية للحفاظ على مواردها. تذكّر أنَّنا سنتعامل مع نظام التشغيل في النهاية سواءً استعملنا الواجهة الرسومية أو السطرية، وهذا هو السبب وراء امتلاك نظام ويندوز لسطر أوامر مختلف تمامًا عن ماك أو لينكس؛ إذ يُصنَّف ماك ولينكس على أنهما شبيهان بنظام يونكس، وهذا هو السبب وراء تشابه سطر الأوامر فيهما. سطر الأوامر معقد ومتشابك وواسع، لذا لا تقلق إذا لم تجد نفسك مرتاحًا معه في البداية، إذ تستطيع استخدام أمر ما يوميًا ثم تكتشف أشياءً جديدةً كل فترة (مثل أوامر أخرى أو خيارات للأوامر التي تستعملها أو مفاهيم جديدة في سطر الأوامر). هنالك بعض الأوامر البسيطة التي تساعدك في الاعتياد على سطر الأوامر وفهمه، لذا أنصحك بتعلمها حتى لو لم تكن تستعملها كثيرًا. أعلم أنَّ بإمكانك فعل الكثير مع ووردبريس دون الحاجة إلى استخدام سطر الأوامر، لكنني أعتقد أنَّ من الضروري تعلّم سطر الأوامر لزيادة خبرتك في إدارة مواقع ووردبريس. أرجو أن تكون هذه المقالة قد وضحت لك بعض المفاهيم الغريبة، وإذا كنتَ مهتمًا بمزيدٍ من المعلومات حول سطر الأوامر فأنصحك بقراءة كتاب «سطر أوامر لينكس». ترجمة –وبتصرّف– للمقال What is the Command Line? CLIs from First Principles لصاحبه David Hayes
×
×
  • أضف...