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

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

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

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

  • Days Won

    60

السُّمعة بالموقع

241 Excellent
  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." إذًا، يمكننا التحكم بطريقة عرض علامات الاقتباس والفواصل العليا في سلاسلنا النصية عبر استخدام النوع الصحيح من علامات الاقتباس لإحاطة كامل السلسلة النصية. كتابة النص على أكثر من سطر طباعة السلاسل النصية على أكثر من سطر ستجعل منها واضحةً وسهلة القراءة. إذ يمكن تجميع النصوص المكتوبة بعدِّة أسطر لزيادة وضوحها، أو لتنسيقها كرسالة، أو للحفاظ على تعدد الأسطر في الأشعار. نستخدم ثلاث علامات اقتباس فردية ''' أو ثلاث علامات اقتباس مزدوجة """ للإحاطة بالسلسلة النصية التي تمتد على أكثر من سطر: ''' 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) وميزات التحكم بالإصدارات والاتصال بقواعد البيانات وخلاف ذلك. لماذا هناك العديد من لغات البرمجة؟ قد تتساءل، لماذا هناك العديد من لغات البرمجة؟ أليست هذه اللغات كلها تنفذ الهدف ذاته؟ لماذا لا يكون هنالك لغة موحدة بين المبرمجين والحاسوب؟ الحقيقة أنّه توجد لغة برمجة واحدة ولكن ليست إحدى اللغات التي تراها أمامك في الصورة. اللغة التي نشير إليها هي "لغة الآلة" التي يستطيع معالج الحاسوب قراءتها وفهمها. أتتساءل ما هي لغة الآلة وكيف تبدو؟ إليك مقطعًا منها: معلومٌ أنّ معالج الحاسوب لا يفهم شيئًا سوى الأصفار والواحدات، وهذه اللغة -أي لغة الآلة- هي تمثيل للأصفار والواحدات بطريقة تخبر الحاسوب بما يجب عليه فعله. الجدير بالذكر أن هذه اللغة عصية الفهم على البشر، إذ حتى إن استطعت كتابة شيفرة مثل الشيفرة الموضحة بالصورة (كما في السنوات الأولى من بداية اختراع الحاسوب)، لن يفهمها الآخرون ولن يستطيع أحد التعديل على الشيفرة وتطويرها لاحقًا باستثنائك. سعيًا لإيجاد لغة قريبة من لغة البشر، انقسمت لغات البرمجية إلى قسمين: لغات البرمجة منخفضة المستوى، ولغات البرمجة عالية المستوى وذلك تبعًا لمدى قربها من لغة الآلة أو لغة البشر على التوالي. أي أنّ لغات البرمجة منخفضة المستوى هي اللغات الأقرب للغة الآلة آنفة الذكر مثل لغة التجميع، ولغات البرمجة عالية المستوى هي اللغات الأقرب للغة البشر مثل لغة بايثون وجافا. تنفيذ البرامج المكتوبة بلغات برمجة عالية المستوى الحديث عن اللغات عالية المستوى واللغات منخفضة المستوى يقودنا إلى الحديث عن كيفية تنفيذ المعالج للشيفرة المكتوبة بلغة عالية المستوى لا يفهمها المعالج (أليس هذا ما تفكر به الآن؟). عرفنا أن المعالج يفهم الأوامر والتعليمات المكتوبة بلغة منخفضة المستوى (لغة الآلة)، إذ مَثَلُ هذه العملية كمَثَلِ شخصٍ أجنبي تعلم اللغة العربية وبدأ التحدث مع ناطقٍ باللغة العربية، إذ يمكن لهما التواصل مباشرةً - ليخبر كل منها ما يريد من الآخر فعله - دون وسيط. أمَّا مَثَلُ كتابة برنامج بلغة عالية المستوى أقرب إلى لغة البشر والطلب من الحاسوب تنفيذه كمثل ناطق باللغة الهندية يريد التخاطب مع ناطق باللغة العربية دون أن يفقه أحدهما لغة الآخر. في هذه الحالة، لن يستطيع أحدهما فهم ما يتكلم به الآخر وستفشل عملية التواصل. قد تقول: لماذا لا يحضران مترجمًا يترجم ما يقوله كل منها للآخر؟ حسنًا، هذا ما يحصل تمامًا عندما يراد تنفيذ برنامج بلغة لا يفهمها معالج الحاسوب. في اللغات البشرية، هنالك نوع واحد من المترجمين يعرفه الجميع للترجمة من لغة إلى آخرى؛ أما في لغات البرمجة، هنالك نوعان من المترجمين بين اللغات هما: المفسر، والمترجم. بناءً على ذلك، تنقسم لغات البرمجة إلى لغات مفسرة ولغات مترجمة. (من الآن وصاعدًا، كلما ذكرنا لغات البرمجة، فنحن نشير إلى لغات البرمجة عالية المستوى.) المفسر (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. لغة Sass لإنشاء ملفات CSS بسرعة وسلاسة. أدوات بناء مثل Gulp الذي يسهِّل تحويل الملفات المصدرية للتطبيق إلى النسخة النهائية التي ستعرَض للمستخدم. لتعلم تطوير واجهات المستخدم، ننصحك بالتسجيل في دورة تطوير واجهات المستخدم المقدمة من أكاديمية حسوب، والتي تحتوي على 34 ساعة فيديو تتوزع على ستة مسارات تعليمية تشرح أمثلة عملية تطبيقية شرحًا مفصلًا. أثناء مشاهدتك للدورة، يمكنك أن تعود إلى موسوعة حسوب لتتعرف على توثيق لغات البرمجة المذكورة، وذلك للاطلاع على تفاصيل وأمثلة أكثر عن كل جزئية من الجزئيات المشروحة في دورة تطوير واجهات المستخدم. اللغات والتقنيات المستخدمة في تطوير واجهات المستخدم: HTML و CSS و JavaScript و Bootstrap و Sass و jQuery و Gulp. تطوير الواجهات الخلفية قد تتساءل: ماذا يعني تطوير الواجهات الخلفية (backend)؟ وما الفرق بينه وبين تطوير واجهات المستخدم (frontend)؟ الفرق بينهما هو أن الواجهات الخلفية هي البرمجيات التي تُنفَّذ على الخوادم وتجري عمليات عليها مثل التعامل مع قواعد البيانات والملفات والخدمات الخارجية، أما واجهات المستخدم فهي الصفحات التي تظهر على شاشة الزائر في متصفحه. سأطرح عليك الخيارات المتاحة أمامك للبدء في مجال تطوير الواجهات الخلفية، وجميع اللغات المذكورة هنا هي لغات ناجحة وقوية ولا يهم أي لغة تختار منها، المهم أن تتطلع على شيفرات بسيطة من كل لغة وتتخذ قرار تعلمها، واحذر من تضييع وقتك في التنقل بين لغات البرمجة والبحث عن أفضلها، فكلُ لغةٍ ممتازةٌ في مجالها. تعلم البرمجة باستخدام لغة PHP بعد تبيان الفرق بين واجهات المستخدم والواجهات الخلفية، يمكن القول بأن أشهر لغة لتطوير الواجهات الخلفية هي لغة PHP، وتتفوق على اللغات المنافسة لها أضعافًا مضاعفة. تعلم البرمجة بلغة PHP أمر سلس، فهي لغة سهلة التعلم وبسيطة الشكل، والمجتمع حولها كبير وتطويرها مستمر. هذه اللغة هي خيار استراتيجي لمن يريد الدخول إلى مجال تطوير الواجهات الخلفية. هنالك عدد من البرمجيات المكتوبة بلغة PHP مثل ووردبريس WordPress ودروبال Drupal وميدياويكي MediaWiki (التي تشغِّل ويكيبيديا وموسوعة حسوب) وغيرها الكثير؛ إضافةً إلى عددٍ كبير من إطارات العمل مثل Laravel و Zend و CodeIgniter و Symfony و CakePHP و Yii وغيرها، وهذا ما يدل على إمكانيات اللغة الكبيرة والمجتمع الكبير الذي يحيط بها. لتعلم تطوير الواجهات الخلفية باستخدام PHP، ننصحك بالتسجيل في دورة تطوير تطبيقات الويب باستخدام PHP المقدمة من أكاديمية حسوب، والتي تحتوي على 19 ساعة فيديو تتوزع على خمسة مسارات تعليمية تبدأ بأساسيات لغة البرمجة 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 المقدمة من أكاديمية حسوب، والتي تحتوي على 13 ساعة فيديو تتوزع على ثلاث مسارات تعليمية تشرح أمثلة عملية تطبيقية شرحًا مفصلًا، وتشرح تطوير الواجهة الخلفية باستخدام Node.js. أثناء مشاهدتك للدورة، يمكنك أن تعود إلى موسوعة حسوب للاطلاع على توثيق لغة JavaScript وبيئة العمل Node.js. اللغات والتقنيات المستخدمة في تطوير تطبيقات الويب باستخدام JavaScript: لغة JavaScript وبيئة Node.js وإطار العمل Express.js وقواعد البيانات (مثل MongoDB و MySQL و PostgreSQL وغيرها). تعلم البرمجة باستخدام لغة بايثون - Python لغة بايثون متعددة الاستعمالات، ويمكن عدّها على أنها أسهل لغة برمجة على الإطلاق، إذ تبدو شيفرتها البرمجية كأنها مقالة مكتوبة باللغة الإنكليزية. إذا أردتَ لغةً سهلةً ومدعومةً دعمًا ممتازًا ولها أطر عمل كثيرة فأنت تبحث عن لغة بايثون. الخيارات المتاحة أمامك هي إطار العمل جانغو (Django) وفلاسك (Flask) وغيرها، يمكنك تعلم لغة البرمجة بايثون لتطوير الواجهات الخلفية من خلال قراءة سلاسل المقالات عن تعلم بايثون في قسم البرمجة في أكاديمية حسوب، ثم الانتقال إلى تعلم إطار العمل جانغو أو فلاسك. يمكنك أن تعود إلى موسوعة حسوب للاطلاع على توثيق لغة بايثون. اللغات والتقنيات المستخدمة في تطوير تطبيقات الويب باستخدام بايثون: لغة بايثون وإطارات العمل المبنية عليها (مثل جانغو وفلاسك) وقواعد البيانات (مثل MySQL و PostgreSQL وغيرها). تعلم تطوير تطبيقات الجوال ازداد عدد تطبيقات الجوال لأنظمة أندرويد و iOS ازديادًا كبيرًا في الفترة الماضية، وأصبح لكل شركة أو خدمة تطبيق خاص بها يسهِّل على مستخدميها الوصول إلى الخدمات التي توفرها. النظامان الرئيسيان المسيطران على سوق الجوال حاليًا هما أندرويد ثم iOS. يمكن برمجة تطبيقات أندرويد بلغة Java أو Kotlin (أو غيرهما) وبرمجة تطبيقات iOS باستخدام Swift (وغيرها). ستكتشف أنَّ عليك تطوير تطبيقين منفصلين تمامًا، واحد لهواتف أندرويد وآخر لهواتف iOS، وذلك يسبب زيادةً في حجم العمل المطلوب وصعوبةً في إدارة التغييرات. بسبب ذلك، ظهر مفهوم "التطبيقات الهجينة"، وهي تطبيقات تعمل على نظام أندرويد و iOS دون أي تعديلات، وذلك باستخدام تقنيات مشتركة وهي تقنيات الويب. أي أصبح بإمكان مطوري الويب الاستفادة من معلوماتهم في تطوير تطبيقات الجوال باستخدام منصة كوردوفا Cordova. تسمح منصة كوردوفا للمبرمجين بالتعامل مع مختلف وظائف الجهاز باستخدام لغة JavaScript، مثل الوصول إلى الموقع الجغرافي، والتقاط صور بالكاميرا، والتعامل مع الملفات وخلاف ذلك. طوِّرت في الفترة الماضية تقنيات أخرى مبنية على JavaScript مثل ReactNative المبنية على مكتبة React.js والتي تسمح للمطورين بكتابة تطبيقات أصيلة (وليست هجينة) باستخدام تقنيات الويب. تستطيع تعلم تطوير تطبيقات الجوال عبر كوردوفا من خلال التسجيل في دورة تطوير تطبيقات الجوال باستخدام تقنيات الويب المقدمة من أكاديمية حسوب، والتي تحتوي على 15 ساعة فيديو تتوزع على أربعة مسارات تعليمية تشرح أمثلة عملية تطبيقية شرحًا مفصلًا. أثناء مشاهدتك للدورة، يمكنك أن تعود إلى موسوعة حسوب للاطلاع على توثيق منصة كوردوفا. اللغات والتقنيات المستخدمة في تطوير تطبيقات الجوال: Java و Swift و Kotlin و Cordova و ReactNative. تطوير الألعاب تطوير الألعاب هو المجال الذي يحلم جميع مستخدمي الحاسوب بالدخول إليه. فالأغلبية تعرفوا على الحاسوب من خلال ألعاب الفيديو ومن ثم بدؤوا برحلة الاستكشاف عن البرمجة والتطوير. أغلب من يجيب عن تطوير الألعاب يقول "عليك بتعلم لغة C++‎" لكن دعني أفصِّل لك الأمر قليلًا. برمجة الألعاب تتطلب عملًا كثيرًا من فريق عمل كبير، مدعوم من شركة تجارية. من الصعب على مطوِّر وحيد أن ينشئ لعبة كاملة من الصفر دون فريق. تُطور أغلبية الألعاب باستخدام محرِّك (engine) والذي يسهِّل الأمر على المطورين ويتيح بيئة تطوير مناسبة للألعاب، ويتيح الميزات الأساسية لجميع الألعاب مثل التحكم بالكاميرا ونمذجة الشخصيات ثلاثية الأبعاد وتحريكها والأمور الفيزيائية الأخرى. هنالك عدد كبير من محركات تطوير الألعاب، ومن المرجح أنك شاهدت شعارها في الألعاب التي لعبتها من قبل، ومن أشهرها: Unreal Engine و Unity. يمكن التعامل مع هذه المحركات باستخدام عدِّة لغات، مثل C++‎ (وهي أشهرها)، وجافا (خصوصًا للألعاب على هواتف أندرويد) وحتى يمكن استخدام JavaScript في التعامل مع بعضها. تذكر أنّ الألعاب غير محدودة بتطبيقات سطح المكتب أو الهواتف، فهنالك ألعاب كثيرة تعمل على المتصفحات باستخدام تقنيات HTML5 و JavaScript. اللغات والتقنيات المستخدمة في تطوير الألعاب: C++‎ و Java و JavaScript ومحركات Unity و Unreal Engine. تطوير الأنظمة المدمجة الأنظمة المدمجة هي أنظمة حاسوبية شبيهة بالحاسوب ولكنها لا تملك كل ميزات الحاسوب الذي تراه أمامك الآن. بعبارة أخرى، النظام المدمج هو حاسوب صغير مبرمج لأداء مهام محددة فقط ومدمج ضمن الجهاز أو البيئة المراد استخدامه فيها. أنت الآن محاط بالكثير من الأنظمة المدمجة الآن مثل جهاز مقياس مستوى المياه وجهاز التحكم بالتلفاز وجهاز إنذار الحريق وأجهزة المراقبة ...إلخ. حتى إشارات المرور وتنظيم السير وألعاب الأطفال الآلية تصنَّف على أنها أنظمة مدمجة. هل سمعت أيضًا بمصطلح "إنترنت الأشياء"؟ إنترنت الأشياء هو نظام مدمج متصل بالإنترنت. نعم، بهذه البساطة! لابد الآن أن يتبادر إلى ذهنك الساعات والثلاجات والغسالات الذكية وطائرات الدرون وأنظمة المراقبة عن بعد وأنظمة البيوت الذكية، إذ كلها أمثلة على إنترنت الأشياء. كيفية برمجة الأنظمة المدمجة أشهر وأكثر لغة برمجة تستعمَل في برمجة الأنظمة المدمجة وإنترنت الأشياء هي لغة 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%. أنصحك بقراءة ومتابعة استراتيجيات تنظيم الوقت ورفع الإنتاجية. لا تنسَ أن تكافئ نفسك في كل مرة تنهي فيها كتابًا أو تكمل العمل على مشروع. لا تنسَ حظك من الاستراحة، لأن طريق البرمجة قد يكون له بداية ولكن النهاية بعيدة ومتعبة -مثله مثل أي مجال آخر-. في النهاية أرجو لك كل التوفيق في مشوارك البرمجي. وأرجو منك أن تشاركنا تجربتك في تعلم البرمجة، لعل غيرك يستفيد منها.
  14. على الرحب والسعة. الحل هو سحب الرصيد ثم شراء الدورة عبر paypal.
  15. وعليكم السلام ورحمة الله وبركاته، أهلًا بك. كانت هذه الخدمة موجودة ولم تعد متاحةً للأسف.