البحث في الموقع
المحتوى عن 'البيانات'.
-
مدخل إلى البيانات وأنواعها: أنواع البيانات الأساسية يحتاج أي عمل إبداعي إلى ثلاثة مكونات أساسية هي: الأدوات التي يُنجَز بها، والخامات التي يتكون منها، والأساليب والتقنيات التي تُستغل الأدوات والخامات بها لإنجاز ذلك العمل، فأدوات الرسم مثلًا هي الفرش والأقلام ولوحات الرسم، كما أن من أساليب الرسم الخلط والرش وغيرها، ثم تأتي الخامات التي هي الدهانات والأوراق والماء ونحو ذلك. وبصورة مماثلة نجد أن أدوات البرمجة هي لغات البرمجة ونظم التشغيل وعتاد الحواسيب التي ستعمل عليها البرامج؛ أما الأساليب والتقنيات فهي هياكل البرامج التي تحدثنا عنها في المقال السابق، والخامات هي البيانات التي نعدل فيها ونعالجها، وهي ما سننظر فيه في هذا المقال. هذا المقال طويل بسبب طبيعة البيانات نفسها، لكننا كتبناه بحيث لا تجب قراءته كله من أجل إدراك الفهم الكامل للبياناتوتستطيع البدء فيه من البداية التي نمر فيها على الأنواع البسيطة للبيانات. البيانات Data من المعلوم بالمشاهدة في عالم البرمجة أن البيانات هي أكثر المصطلحات المستخدمة حاليًا، رغم قلة من يفهمها على حقيقتها، وما يهمنا في هذه السلسلة في موضوع تعريف البيانات نفسها؛ هو أن تعلم أنها الحقائق أو الأرقام التي يمكن استخلاص النتائج والمعلومات المفيدة منها، ورغم أن هذا يكاد يكون تسطيحًا لمفهومها، إلا أنه يعطينا نقطةً نبدأ منها على الأقل، وسنرى فيما يلي إن كنا نستطيع توضيح ذلك بالنظر في كيفية استخدام البيانات في البرمجة. البيانات هي تلك المعلومات الخام التي يعدّل فيها البرنامج ويغيرها، فلا تستطيع البرامج تنفيذ أي وظيفة ولا مهمة ذات قيمة بغير تلك البيانات، وتتوقف أنواع التعديلات التي تجريها البرامج على البيانات على تلك الأنواع نفسها، فكل نوع له عدد من العمليات التي يمكن إجراؤها عليه، فقد رأينا مثلًا أننا نستطيع جمع الأعداد معًا، إذ تحدث عملية الجمع على النوع العددي من البيانات، مما يعني أن البيانات تأتي في عدة صور وأشكال، وسننظر في كل نوع من أشهر تلك الأنواع، إضافةً إلى العمليات المتاحة على ذلك النوع. المتغيرات Variables تخزَّن البيانات في ذاكرة الحواسيب، ويمكن تشبيه تلك الذاكرة بذاكرة مكاتب البريد القديمة، إذ توضع صناديق صغيرة على حوائط تلك المكاتب لتصنيف الرسائل البريدية، ويجب كتابة الوجهة التي تُرسَل إليها رسائل كل صندوق على الصندوق نفسه، إذ لا قيمة للرسائل من دون وجهة تُرسل إليها، وهنا نُسقط مثالنا على البرمجة، إذ إن المتغيرات هي العناوين التي تكتَب على الصناديق داخل ذاكرة الحاسوب. بهذا نكون قد عرفنا نوع البيانات الآن، لكننا لا نستطيع تعديلها قبل أن تكون لنا صلاحية الوصول إليها، وهذه هي وظيفة المتغيرات، فنحن ننشئ نسخًا من أنواع البيانات ونسندها إلى المتغيرات، والنسخة instance هي مجرد جزء من البيانات وليكن 3 أو 5 مثلًا، وهي نُسخ لأعداد؛ أما المتغير فهو إشارة إلى منطقة محددة في ذاكرة الحاسوب تحتوي على البيانات. قد تتطلب بعض لغات البرمجة أن يُصرَّح عن المتغير declared ليطابق نوع البيانات التي يشير إليها، فإذا حاولنا إسناد نوع خاطئ من البيانات إليه، فسنحصل على خطأ، ويفضل بعض المبرمجين ذلك النوع المسمى بالكتابة الثابتة static typing، لأنه يحول دون ارتكاب الزلات البرمجية bugs الدقيقة التي يصعب اكتشافها، على أن جميع اللغات التي نستخدمها هنا مرنة إلى الحد الذي نستطيع معه إسناد أي نوع من البيانات إلى المتغيرات، وهو ما يُعرف بالكتابة الديناميكية dynamic typing. تتبع أسماء المتغيرات قواعدًا محددةً تختلف باختلاف لغة البرمجة التي تستخدمها، فلكل لغة قواعدها للأحرف التي يُسمح أو لا يُسمح بها، كما تشترط بعض اللغات مثل جافاسكربت وبايثون أن ينتبه المبرمج إلى حالة الأحرف التي يكتبها، بحيث تختلف python عن Python مثلًا، كما توجد لغات أخرى مثل VBScript لا تهتم لهذا، لكن يفضل أن يُتبَّع أسلوب ثابت في تسمية المتغيرات لتجنب الأخطاء الشائعة عند الانتقال من لغة لأخرى، ومن الأساليب المستخدمة في تسمية المتغيرات هو بدء اسم المتغير بحرف صغير، ثم استخدام حرف كبير لبداية كل كلمة تالية في اسمه، كما يلي: aVeryLongVariableNameWithCapitalisedStyle لن نتحدث هنا عن القواعد التي تحكم المحارف المسموح بها في لغات البرمجة التي سنستخدمها، لكن إذا كنت تستخدم الأسلوب الذي في المثال؛ فستجنب نفسك كثيرًا من المشاكل في هذا الشأن. ومن الأساليب المستخدمة في تسمية المتغيرات أيضًا فصل الكلمات في اسم المتغير بشرطة سفلية، كما في another_variable_name؛ أما في لغة بايثون فيأخذ المتغير نوع البيانات المسندة إليه، ويحتفظ بهذا النوع ويحذرك إذا حاولت خلط البيانات بطرق تختلف عنه، كأن تحاول إضافة سلسلة نصية إلى عدد مثلًا، وهي نفس حالة المثال الذي عرض لنا رسالة الخطأ في المقال السابق، ويمكن تغيير نوع البيانات التي يشير إليها المتغير عن طريق إعادة الإسناد إلى المتغير reassigning. >>> q = 7 # عددًا الآن q صار >>> print( q ) 7 >>> q = "Seven" # ليكون سلسلة نصية q أعد إسناد >>> print( q ) Seven لاحظ أن المتغير q ضُبط ليشير إلى العدد 7 ابتداءً، وقد حافظ على تلك القيمة إلى أن جعلناه يشير إلى سلسلة المحارف "Seven"، وعلى هذا نستنتج أن متغيرات بايثون تحافظ على النوع الذي تشير إليه، لكننا نستطيع تغيير ذلك عن طريق إعادة الإسناد إلى المتغير، وتلك هي الكتابة الديناميكية التي ذكرناها قبل قليل. ويمكن التحقق من نوع المتغير باستخدام الدالة type() كما يلي: >>> print( type(q) ) <class 'str'> تُفقد البيانات الأصلية عند إعادة الإسناد، وتمسحها بايثون من الذاكرة ما لم يكن ثمة متغير آخر يشير إليها، وهذا المحو يُعرف باسم جمع المهملات Garbage Collection، والذي يمكن تشبيهه بموظف غرفة البريد الذي يأتي مرةً واحدةً كل فترة ويزيل أي طرود في صناديق البريد لا توجد عليها ملصقات، فإذا لم يستطع العثور على مالك لها أو لم يوجد عنوان على الطرد؛ فسيرميها في المهملات. لننظر الآن في بعض الأمثلة على أنواع البيانات. متغيرات جافاسكربت و VBSCript تختلف جافاسكربت عن أختها VBScript قليلًا في طريقة استخدام المتغيرات، فكلاهما ترى وجوب التصريح عن المتغير قبل استخدامه، وهي خاصية تشترك فيها اللغات المصرَّفة compiled مع اللغات ذات الكتابة الثابتة statically typed، بل حتى في اللغات الديناميكية مثل تلك التي نستخدمها، حيث نستفيد من التصريح عن المتغيرات في حالة حدوث خطأ إملائي عند استخدام متغير، إذ سسيدرك المترجم أن متغيرًا غير معروف قد استُخدم، وسيرفع خطأً؛ أما عيب هذا الأسلوب فهو الحاجة إلى مزيد من الكتابة من قِبل المبرمج. VBScript يصرَّح عن المتغير في لغة VBScript باستخدام التعليمة Dim، وهي اختصار لكلمة Dimension أو بُعد، وهي إشارة إلى الجذور الأولى للغة VBScript في لغة BASIC ولغات التجميع من قبلها، حيث كان يجب علينا إخبار المجمِّع بكمية الذاكرة التي سيستخدمها المتغير -أو أبعاده-، وظل الاختصار موجودًا حتى الآن. ويبدو التصريح عن المتغير في VBScript كما يلي: Dim aVariable نستطيع بمجرد التصريح عن المتغير أن نعين قيمًا له كما فعلنا في بايثون، ويمكن التصريح عن عدة متغيرات في تعليمة Dim واحدة بسردها جميعًا مع فصلها بفواصل إنجليزية "," كما يلي: Dim aVariable, another, aThird ويبدو الإسناد بعدها كما يلي: aVariable = 42 another = "هذه جملة قصيرة." aThird = 3.14159 قد نرى كلمةً مفتاحيةً أخرى هي Let، وهي من جذور BASIC أيضًا، لكننا لن نراها إلا نادرًا لقلة الحاجة إليها، وهي تُستخدم كما يلي: Let aVariable = 22 ولن نستخدمها على أي حال في هذه السلسلة. جافاسكربت نستطيع التصريح عن المتغيرات مسبقًا في جافاسكربت باستخدام الكلمة var، كما يمكن التصريح عن عدة متغيرات كما في VBScript في تعليمة var واحدة: var aVariable, another, aThird; كما تسمح جافاسكربت بتهيئة -أو تعريف- المتغيرات مثل جزء من تعليمة var، كما يلي: var aVariable = 42; var another = "A short phrase", aThird = 3.14159; يوفر هذا قليلًا من وقت الكتابة، لكنه لا يختلف عن أسلوب VBScript مع المتغيرات والمكون من خطوتين. يمكن التصريح عن متغيرات جافاسكربت وتهيئتها من غير استخدام var، بنفس الطريقة المتبعة في بايثون: aVariable = 42; غير أن المتعصبين لجافاسكربت يرون أن استخدام var أفضل، لذا سيكون هذا أسلوبنا في هذه السلسلة أيضًا. نأمل أن تكون تلك النبذة المختصر عن متغيرات جافاسكربت وVBScript قد أوضحت الفرق بين التصريح عن المتغيرات وبين تعريفها، أما متغيرات بايثون، فتعريفها يعني ضمنيًا التصريح عنها كذلك. أنواع الكتابة: صارمة أم متغيرة أم ثابتة لقد أشرنا إلى الكتابة الثابتة والديناميكية فيما سبق من المقال، وتوجد أنواع أخرى من الكتابة مثل الكتابة الصارمة strict -أو القوية أحيانًا أخرى-، والكتابة الفضفاضة loose أو الضعيفة، وسنحاول هنا أن نوضح الاختلافات بينها. تُنشأ المتغيرات مثل أنواع بيانات مرنة أو ثابتة، ويقال أن النوع الثابت مكتوب كتابةً ثابتةً أي أنه لا يتغير أبدًا-، أما المتغير الذي قد يشير إلى عدة أنواع من البيانات فيُكتب ديناميكيًا، لذا تشير البيانات الثابتة والديناميكية إلى أنواع البيانات التي يمكن تخزينها داخل المتغير. ويمكن دمج المتغيرات في العمليات المختلفة عند البرمجة، حيث ستتطلب كل عملية احتواء متغيراتها على أنواع بعينها، مثل الأعداد أو المحارف، فإذا رفضت اللغة السماح لعملية ما باستخدام أنواع خاطئة؛ فسيقال أن هذا النوع من الكتابة كتابةٌ صارمة. تحاول بعض اللغات إجبار القيم على الأنواع الصالحة المسموح بها، بينما تسمح لغات أخرى بأي نوع ثم تفشل العملية وتخرِج خطأً، وتلك هي الكتابة الفضفاضة، وعلى هذا تشير كل من الكتابة الصارمة والفضفاضة إلى أسلوب التحقق من أنواع المتغيرات المستخدَمة في العمليات، وبالنسبة للغات التي سنستخدمها في هذه السلسلة، فجميعها تستخدم كتابةً ديناميكيةً قويةً. الأنواع الأساسية للبيانات تسمى أنواع البيانات الأساسية primitive data types بهذا الاسم لأنها أبسط أنواع البيانات التي يمكن معالجتها والتعامل معها، أما الأنواع الأعقد من البيانات فما هي في الواقع إلا تجميعات من الأنواع الأساسية، فهي اللبنات الأساسية التي تكوَّن منها جميع الأنواع الأخرى، وبناءً عليه تكون هي أساس الحوسبة كلها، وتشمل الأنواع الأساسية الأرقام والأحرف ونوعًا آخر اسمه النوع البولياني boolean type. سلاسل المحارف Strings لقد رأينا هذه السلاسل من قبل، وهي أي سلسلة من المحارف التي يمكن طباعتها على الشاشة، وقد توجد فيها محارف تحكم لا تُطبع ولا تُرى على الشاشة. تُعرض تلك السلاسل في صورة أحرف، لكن الحاسوب يخزنها في صورة تسلسلات من الأرقام، ويطلق على ربط تلك الأرقام بالمحارف اسم الترميز encoding، وتوجد العديد من الترميزات المختلفة، رغم أن المنتشر حاليًا منها هو واحد من ثلاثة ترميزات معرَّفة بواسطة معيار الترميز الموحد Unicode Standard، وسننظر في الترميز الموحد أو اليونيكود في مقال لاحق، لكن يجب أن تعلم أن غرضه توفير مخطط ترميز يمثل أي حرف من أي أبجدية في أي مكان في العالم، إضافةً إلى المحارف الخاصة التي تحتاجها الحواسيب مثل الأقواس والتحكم والتهريب وغيرها. لن نعير هذا الترميز كثير الانتباه أثناء العمل، لكن قد نحتاج إلى التحويل بين الرموز الرقمية والأحرف، كما في مشكلة الاقتباسات في مثال VBScript من المقال السابق. ويمكن تمثيل السلاسل النصية في بايثون بعدة طرق: باستخدام علامات اقتباس مفردة: 'هذه سلسلة نصية' أو باستخدام علامات اقتباس مزدوجة: "وهذه سلسلة نصية تشبهها" أو بعلامات اقتباس ثلاثية مزدوجة: """ أما هذه فسلسلة نصية أطول ونستطيع إطالتها وتقسيمها على ما نشاء من الأسطر وستحافظ بايثون على هذه الأسطر """ يُستخدم النوع الأخير في إنشاء توثيقات لدوال بايثون التي ننشئها بأنفسنا كما سنرى في مقال تال، ويمكن استخدام علامات اقتباس ثلاثية مفردة، لكننا لا ننصح بهذا، إذ قد يصعب تحديد ما إذا كانت العلامات ثلاثيةً مفردةً أم علامةً مزدوجةً داخل أخرى مفردة. نستطيع الوصول إلى المحارف المنفردة في السلسلة النصية بمعاملتها مثل مصفوفة من المحارف -انظر المصفوفات أدناه-، كما توجد بعض العمليات التي توفرها لغة البرمجة لتساعدنا في التعامل مع السلاسل، مثل العثور على سلسلة فرعية أو ربط سلسلتين معًا، ونسخ واحدة إلى أخرى، وهكذا. تجدر الإشارة إلى أن بعض اللغات فيها نوع منفصل للمحارف، أي لحرف واحد فقط، وفي تلك الحالة تكون السلاسل مجرد مجموعات من قيم تلك الأحرف، لكن بايثون -على نقيض هذا-ـ إذ تستخدم سلسلةً طولها وحدة واحدة لتخزين الحرف الواحد، ولا يلزم وجود صياغة خاصة. العوامل النصية String Operators توجد عدة عمليات يمكن إجراؤها على السلاسل النصية، وبعضها مضمّن مسبقًا في بايثون، لكن يمكن توفير عمليات إضافية باستخدام الوحدات المستورَدة كما فعلنا مع وحدة sys في المقال السابق حول التسلسلات البسيطة. table { width: 100%; } thead { vertical-align: middle; text-align: center; } td, th { border: 1px solid #dddddd; text-align: right; padding: 8px; text-align: inherit; } tr:nth-child(even) { background-color: #dddddd; } العامل الوصف S1 + S2 ضم كل من S1 و S2 S1 * N تكرار قدره N من S1 يمكننا أن نرى ذلك في الأمثلة التالية: >>> print( 'Again and ' + 'again' ) # ضم السلسلة Again and again >>> print( 'Repeat ' * 3 ) # تكرار السلسلة Repeat Repeat Repeat >>> print( 'Again ' + ('and again ' * 3) ) # '*' جمع '+' و Again and again and again and again كما يمكن إسناد سلاسل أحرف إلى متغيرات: >>> s1 = 'Again ' >>> s2 = 'and again ' >>> print( s1 + (s2 * 3) ) Again and again and again and again لاحظ أن المثالين الأخيرين أنتجا نفس الخرج. هناك الكثير من الأمور التي يمكن تنفيذها باستخدام السلاسل النصية، وسننظر فيها بتفصيل في مقالات تالية بعد أن نكتسب خبرةً جيدةً في المواضيع التي تسبقها. ربما تجب ملاحظة أن السلاسل النصية في بايثون لا يمكن تعديلها، أي أننا نستطيع إنشاء سلسلة نصية جديدة مع تغيير بعض الأحرف، لكن لا يمكن تعديل أي حرف داخل سلسلة تعديلًا مباشرًا، ويُعرف نوع البيانات ذاك الذي لا يمكن تغييره باسم النوع غير القابل للتغير immutable. متغيرات السلاسل النصية في VBScript تسمى المتغيرات في VBScript باسم المتغايرات variants، لأنها قد تحتوي على أي نوع من البيانات، وتحاول VBScript أن تحولها إلى النوع المناسب وفق الحاجة، وعليه فقد نسند عددًا إلى متغير ما، لكن إذا استخدمناه مثل سلسلة نصية فستحاول VBScript أن تحوله نيابةً عنا، وهذا يشبه -من الناحية العملية- سلوك أمر print في بايثون، غير أنه هنا يمتد إلى أي أمر VBSCript. ويمكن إعطاء VBScript لإشارة على أننا نريد قيمةً عدديةً في صورة سلسلة نصية من خلال تغليفها بعلامتي اقتباس مزدوجتين: <script type="text/vbscript"> MyString = "42" MsgBox MyString </script> يمكن دمج سلاسل VBScript معًا في عملية تسمى بالضم concatenation، باستخدام العامل &: <script type="text/vbscript"> MyString = "Hello" & "World" MsgBox MyString </script> سلاسل جافاسكربت النصية تحاط سلاسل جافاسكربت النصية بعلامات اقتباس مفردة أو مزدوجة، ويجب أن نصرّح عن المتغيرات قبل استخدامها، وذلك بكلمة var المفتاحية. في المثال التالي سنصرح عن متغيري سلاسل نصية ونعرّفهما: <script type="text/javascript"> var aString, another; aString = "Hello "; another = "World"; document.write(aString + another); </script> كما تسمح لنا جافاسكربت بإنشاء كائنات سلاسل نصية، ويمكن القول بأنها سلاسل نصية مع بعض المزايا الإضافية، والاختلاف الرئيسي بينهما هو طريقة إنشائها، والتي هي بالشكل: <script type="text/javascript"> var aStringObj, anotherObj; aStringObj = String("Hello "); anotherObj = String("World"); document.write(aStringObj + anotherObj); </script> قد تكون تلك كتابةً كثيرةً قياسًا على المثال السابق مع أنهما ينجزان العمل نفسه -وهذا صحيح نوعًا ما-، غير أن كائنات السلاسل تعطينا مزايا في بعض المواقف تجعلنا نفضل استخدامها عما سواها، كما سنرى لاحقًا. الأعداد الصحيحة Integers الأعداد الصحيحة هي الأعداد الكاملة غير الكسرية، من أكبر قيمة سالبة إلى أكبر قيمة موجبة، وهذا التحديد لمجال القيم مهم في البرمجة على عكس الرياضيات، إذ لا نعير اهتمامًا لقيمة العدد غالبًا طالما ينتمي إلى مجموعة أساسية أو المجموعة التي نعمل عليها، أو لم يكن يؤثر في الناتج؛ أما في البرمجة فهناك حدود عظمى وحدود دنيا، ويعرَف الحد الأعلى باسم MAXINT، كما يعتمد على عدد البتات bits المستخدَمة في حاسوبك لتمثيل عدد ما. يكون في أغلب الحواسيب ولغات البرمجة الحالية العدد 64-بت في المعماريات الحديثة، لذا فإن قيمة الحد الأعلى هنا MAXINT تساوي 10 مرفوعة إلى الأس 19، وتستطيع بايثون إخبارنا بقيمة maxint باستخدام وحدة sys، فإذا كانت معمارية النظام 64-بت مثلًا فستكون قيمة sys.maxsize هي 9223372036854775807، وهو رقم كبير؛ أما في VBScript فتكون تلك القيمة محدودةً بـ (+/- 32000) لأسباب تاريخية. تُعرَف الأعداد التي تحمل إشارةً موجبةً أو سالبةً بالأعداد الصحيحة ذات الإشارة signed integers، ومن الممكن استخدام أعداد صحيحة لا إشارة لها، غير أن هذا مقصور على الأعداد الموجبة، بما في ذلك الصفر، وهذا يعني أن الأعداد الصحيحة التي لا إشارة لها ضعف عدد ذوات الإشارة، بما أننا سنستغل المساحة التي كانت مخصصةً للأعداد السالبة لضمها إلى الأعداد التي لا إشارة لها، مما يعني زيادة قيمة الحد الأقصى إلى الضعف أيضًا "2 * MAXINT"، ونظرًا لأن حجم الأعداد الصحيحة مقيد بقيمة MAXINT، فإذا جمعنا عددين صحيحين وكان ناتج الجمع أكبر من الحد الأقصى؛ فسنحصل على خطأ، لكن قد تعيد بعض الأنظمة واللغات تلك القيمة الخاطئة كما هي مع رفع راية سرية secret flag نستطيع التحقق من وجودها إذا كنا نشك في الإجابة، وفي مثل هذه الحالة يكون التحقق عادةً هو برفع حالة خطأ، فإما أن يكون البرنامج حينها قادرًا على معالجة ذلك الخطأ فيعالجه، أو ينهي البرنامج نفسه إذا لم يستطع. تحوّل كل من جافاسكربت وVBScript العدد إلى صيغة تستطيعان التعامل معها، ولا تهتمان بالدقة هنا، إذ قد تختلف الصيغة الجديدة اختلافًا طفيفًا؛ أما لغة بايثون فتستخدم شيئًا اسمه العدد الصحيح الطويل Long Integer، وهي ميزة خاصة لها تسمح بأعداد صحيحة غير محدودة الحجم. >>> import sys >>> x = sys.maxsize * 1000000 >>> print(x) 9223372036854775807000000 >>> print (type(x)) <class 'int'> كما نرى فالنتيجة أكبر بكثير من القيمة التي تتوقعها من الحاسوب في العادة، رغم احتسابها int، أما إذا استخدمنا شيفرةً مكافئةً لهذا المثال في جافاسكربت وVBScript؛ فسيخرج لنا العدد بصيغة تختلف عن العدد الصحيح الذي نتوقعه، وسنتعلم المزيد عن هذا في قسم الأعداد الحقيقية. <script type="text/vbscript"> Dim x x = 123456700 * 34567338738999 MsgBox CStr(x) </script> العمليات الحسابية رأينا معظم العمليات الحسابية التي نحتاج إليها في المقال السابق في التسلسلات البسيطة، ولكن للتلخيص نعيدها مرةً أخرىً: مثال العامل الوصف M + N جمع M و N M - N طرح N من M M * N ضرب M في N M / N قسمة M على N، سيكون الناتج عددًا حقيقيًا كما سنبين لاحقًا M // N القسمة الصحيحة لـ M على N، ستكون النتيجة عددًا صحيحًا M % N باقي القسمة: أوجد ما تبقى من قسمة M على N M**N الأس: M مرفوعًا إلى الأس N بما أننا لم نرى المتغير الأخير من قبل، فلننظر في مثال على إنشاء بعض متغيرات الأعداد الصحيحة ونستخدم عامل الأس: >>> i1 = 2 # i1 أنشئ عددًا صحيحًا وأسنده إلى >>> i2 = 4 >>> i3 = i1**i2 # i3 أسند نتيجة 2 إلى أس 4 إلى >>> print( i3 ) 16 >>> print( 2**4 ) # أكد النتيجة 16 تحتوي الوحدة math في بايثون على بعض الدوال الرياضية الشائعة مثل sin و cos وغيرهما. عوامل الاختصار إحدى العمليات الشائعة في البرمجة هي زيادة قيمة المتغير التدريجية، فإذا كان لدينا المتغير x الذي قيمته 42، ونريد زيادة تلك القيمة إلى 43، فسيكون ذلك كما يلي: >>> x = 42 >>> print( x ) >>> x = x + 1 >>> print( x ) لاحظ السطر التالي: x = x + 1 لا معنىً لهذا السطر من حيث المنطق الرياضي، لكنه في البرمجة يعني أن x الجديدة تساوي قيمة x السابقة مضافًا إليها 1، فإذا كنت معتادًا على المنطق الرياضي؛ فستستغرق بعض الوقت لتعتاد على هذا النمط الجديد، لكن الفكرة هنا أن علامة = تُقرأ "ستكون" وليست "تساوي"، وعليه فإن السطر يعني أن x ستصبح قيمتها الجديدة هي x+1، وهذا النوع من العمليات شائع جدًا في الممارسة العملية إلى حد أن بايثون وجافاسكربت توفران عاملًا مختصرًا له من أجل توفير الكتابة على المبرمج: >>> x += 1 >>> print( x ) وهذان العاملان مساويان تمامًا لتعليمة الإسناد السابقة لكنهما أقصر، توجد عوامل اختصار لباقي العمليات الحسابية بنفس المنطق السابق، نبينها في الجدول التالي: مثال العامل الوصف M += N M = M + N M -= N M = M - N M *= N M = M * N M /= N M = M / N M //= N M = M // N M %= N M = M % N الأعداد الصحيحة في VBScript لقد قلنا إن الأعداد الصحيحة في VBScript محدودة بقيمة عظمى أقل من بايثون وجافاسكربت، وهي تقابل 16-بت على خلاف 64-بت التي رأيناها من قبل، أي حوالي "+/- 32000"، فإذا احتجنا إلى عدد صحيح أكبر من هذا؛ فنستخدم العدد long الذي يساوي حجمه حجم العدد الصحيح من معمارية 32-بت (أي 2 مليار)، كذلك لدينا النوع byte الذي هو عدد ثماني البتات، وحجمه الأكبر يساوي 255، وسيتضح بالممارسة العملية أن النوع القياسي للعدد الصحيح كافٍ لأغلب العمليات، فإذا كانت نتيجة العملية أكبر من MAXINT؛ فستحول VBSCript النتيجة تلقائيًا إلى عدد ذي فاصلة عائمة أو حقيقي، كما سنبين لاحقًا. ورغم دعم VBScript لجميع العمليات الحسابية المعتادة، إلا أن عملية الباقي modulo تمثَّل تمثيلًا مختلفًا، باستخدام العامل MOD، وقد رأينا ذلك في فصل التسلسلات البسيطة، كذلك فإن تمثيل الأسس مختلف، إذ يُستخدم محرف الإقحام ^ بدلًا من ** في بايثون. أعداد جافاسكربت تحتوي جافاسكربت على نوع عددي في صورة كائن كما سنصفه فيما بعد، وهي تسميه Number، لكن أحيانًا قد يكون عدد جافاسكربت "ليس عددًا" أو Not a Number، ويشار إليه بـ NaN اختصارًا، وهو ما نحصل عليه أحيانًا كنتيجة لبعض العمليات المستحيلة رياضيًا، والهدف منه هو السماح لنا بالتحقق من أنواع أخطاء بعينها دون تعطيل البرنامج، كما تحوي لغة بايثون كذلك النوع NaN أيضًا. تحتوي جافاسكربت على نسخ عددية خاصة لتمثيل اللانهاية الموجبة والسالبة، وهي ميزة نادرة نسبيًا في لغات البرمجة، وقد تكون كائنات جافاسكربت العددية إما أعدادًا صحيحةً أو حقيقيةً كما سنرى لاحقًا، وتَستخدم جافاسكربت نفس العوامل التي تستخدمها بايثون، باستثناء الإجراءات الأسية التي تستخدم كائنًا خاصًا بها اسمه Math. الأعداد الحقيقية Real Numbers الأعداد الحقيقية هي نفسها الأعداد الصحيحة لكن مع إضافة الكسور، وقد تمثل أعدادًا أكبر من الحد الأقصى MAXINT لكن مع دقة أقل، وهذا يعني أننا قد نرى عددين حقيقيين على أنهما متطابقين، لكن الحاسوب يراهما غير متطابقين عندما يوازنهما معًا، لأنه ينظر في أدق التفاصيل بينهما، حيث يمكن تمثيل العدد 5.0 في الحاسوب بإحدى الصورتين 4.9999999 أو (5.000000…01) مثلًا، وهذه التقاربات مناسبة لأغلب الاستخدامات، لكن قد نحتاج أحيانًا إلى تلك الدقة، فتذكر هذا الأمر إذا حصلت على نتيجة غريبة لا تتوقعها عند استخدام الأعداد الحقيقية. كما تُعرَف الأعداد الحقيقية أيضًا باسم أعداد الفاصلة العائمة floating point numbers، ولها نفس العمليات الحسابية التي تطبّق على الأعداد الصحيحة، مع زيادة هنا وهي قدرتها على اقتطاع العدد ليكون قيمة صحيحة. تدعم لغات البرمجة الثلاث بايثون وجافاسكربت وVBScript الأعداد الحقيقية، وننشئها في بايثون بتخصيص عدد بعلامة عشرية فيه كما رأينا في المقال السابق للتسلسلات البسيطة؛ أما جافاسكربت وVBScript فلا يوجد تمييز واضح بين الأعداد الصحيحة والحقيقية، فما عليك إلا استخدامها وستتعامل اللغة مع ما تدخله إليها وفق المناسب. الأعداد المركبة أو التخيلية Complex numbers إذا كانت لديك خلفية رياضية فقد تتساءل أين الأعداد المركبة complex في هذا الشرح، والواقع أننا لا نحتاج إليها غالبًا في البرمجة، لكن على أي حال فبعض اللغات مثل بايثون توفر دعمًا مضمّنا فيها للأعداد المركبة، بينما توفر بعض اللغات الأخرى مكتبةً من الدوال التي تستطيع التعامل مع الأعداد المركبة، ونفس الأمر ينطبق على المصفوفات. يمثَّل العدد المركب في بايثون كما يلي: (real+imaginaryj) وبناءً عليه تبدو عملية جمع بسيطة لعدد مركب كما يلي: >>> M = (2+4j) >>> N = (7+6j) >>> print( M + N ) (9+10j) تنطبق جميع عمليات الأعداد الصحيحة على الأعداد المركبة كذلك، كما توجد وحدة cmath التي تحتوي بعض الدوال الرياضية الشائعة التي تعمل على قيم الأعداد المركبة، ولا توفِّر جافاسكربت وVBScript دعمًا للأعداد المركبة. القيم البوليانية - True و False سُمي هذا النوع من القيم بهذا الاسم نسبةً إلى عالم الرياضيات George Boole الذي كان يدرس المنطق في القرن التاسع عشر، ولا يحوي هذا النوع من البيانات إلا قيمتين فقط، هما True عند التحقق أو الصحة، و false عند عدم التحقق أو الخطأ، وتدعم بعض اللغات هذا النوع دعمًا مباشرًا، بينما تستخدم بعض اللغات الأخرى اصطلاحًا تمثل فيه قيمةً عدديةً القيمة false، وتكون تلك القيمة غالبًا هي الصفر؛ بينما تمثَّل القيمة true بالعدد (1) أو (-1)، وقد كان هذا سلوك بايثون حتى الإصدار 2.2، لكنها صارت تدعم القيم البوليانية مباشرةً بعد ذلك باستخدام القيمتين True و False، وهما كما ترى تبدآن بحرف كبير، فتذكر أن بايثون حساسة لحالة الأحرف. تعرَف القيم البوليانية أحيانًا باسم قيم الحقيقة لأنها تُستخدم للتحقق مما إذا كان شيء ما صحيحًا أم لا، فإذا كتبنا برنامجًا يأخذ نسخةً احتياطيةً من جميع الملفات في مجلد ما مثلًا، فنستطيع نسخ كل ملف على حدة ثم نطلب اسم الملف التالي من النظام، وإذا لم تتبق ملفات لنسخها، فسيعيد النظام سلسلةً نصيةً فارغةً، حيث سنتحقق هنا مما إذا كان الاسم هو سلسلةً فارغةً أم لا، ونخزن النتيجة مثل قيمة بوليانية تكون قيمتها True إذا كان الاسم سلسلةً فارغةً، و False إذا لم يكن كذلك، وسنرى كيفية استخدام تلك النتيجة لاحقًا في هذه السلسلة. العوامل البوليانية أو المنطقية العامل الوصف الأثر A and B الإضافة أو AND صحيح إذا كان كل من A و B صحيحًا أو متحققًا، وخطأ في الحالات الأخرى A or B الاختيار أو OR صحيح إذا كان أحدهما أو كلاهما صحيحًا، وخطأ إذا كان كل من A و B خاطئًا A == B التساوي صحيح إذا كان A يساوي B A != B عدم التساوي صحيح إذا كانت A لا تساوي B not B النفي صحيح إذا لم يكن B صحيحًا تدعم VBScript القيم البوليانية كما تفعل بايثون على الصورة True و False، أما جافاسكربت فتدعمها لكن على الصورة true و false -لاحظ حالة الأحرف الصغيرة هنا-. كما ستجد أسماءً مختلفةً لنوع البيانات البولياني أو المنطقي في كل لغة، ففي بايثون ستجدها باسم bool، أما في جافاسكربت وVBScript فاسمه Boolean، ولا تشغل بالك بهذا لأننا لن ننشئ متغيرات من هذا النوع، لكننا سنستخدم نتائجه في الاختبارات. الوقت والتاريخ يُعطى التاريخ والوقت في العادة أنواعًا خاصةً بهما في البرمجة، لكن قد يمثَّلان أحيانًا في صورة عدد كبير (عدد الثواني منذ وقت أو تاريخ قديم، مثل الوقت الذي كُتب فيه نظام التشغيل)، لكن قد يكون نوع البيانات في أحيان أخرى هو ما يُعرف بالنوع المعقد الذي سنذكره في الفصل التالي، وهو يسهل استخراج الشهر واليوم والساعة… سننظر في استخدام بايثون لوحدة time في مقال تالٍ، أما جافاسكربت وVBScript فلديهما آليات خاصة للتعامل مع الوقت، لكننا لن ندرسها. النوع الذي يعرفه المستخدم User Defined Type قد لا تكفي الأنواع التي ذكرناها سابقًا لاحتياجاتنا البرمجية حتى لو دمجناها معًا، فقد نريد جمع عدة أجزاء من البيانات معًا ثم نعاملها مثل عنصر واحد، كما في وصف العنوان مثلًا، حيث يكون على هيئة "رقم المنزل والشارع والمدينة ، ثم الرمز البريدي". تسمح أغلب اللغات بجمع مثل هذه المعلومات معًا في سجل أو هيكل بيانات أو في هيئة صنف Class، وهذا الأخير في البرمجة كائنية التوجه Object Oriented. VBScript تبدو تعريفات السجلات في VBScript كما يلي: Class Address Public HsNumber Public Street Public Town Public ZipCode End Class تعني الكلمة المفتاحية Public أن البرنامج يستطيع الوصول إلى البيانات، وعليه توجد كلمة Private التي يمكن استخدامها لجعل البيانات خاصةً ومقصورةً على أجزاء بعينها من البرنامج، كما سنرى لاحقا. بايثون يختلف الأمر قليلًا في بايثون: >>> class Address: ... def __init__(self, Hs, St, Town, Zip): ... self.HsNumber = Hs ... self.Street = St ... self.Town = Town ... self.ZipCode = Zip ... سنترك شرح معنى كل من self و def __init__(...) إلى حين الحديث عن البرمجة كائنية التوجه، أما الآن فنريد ملاحظة أن هناك شرطتين سفليتين حول init، وهذا أسلوب متبع في بايثون. لاحظ كذلك ضرورة استخدام المسافات الموضحة أعلاه، ذلك أن بايثون دقيقة في تحديد المسافات. قد يواجه البعض مشاكل أثناء كتابة ما يتشابه مع هذا المثال في محث بايثون، وستجد في نهاية هذا المقال شرحًا مختصرًا لهذه المشكلة، ثم سنشرحها بالتفصيل في جزء تالٍ من هذه السلسلة، فإذا أردت كتابة المثال أعلاه في محث بايثون؛ فتأكد من نسخ المسافات البادئة لكل سطر. والأمر الذي نريد التوكيد عليه هنا هو أننا جمعنا عدة أجزاء من بيانات مرتبطة ببعضها بعضًا ثم وضعناها في هيكل واحد هو Address، كما فعلنا في VBScript قبل قليل. جافاسكربت توفر جافاسكربت اسمًا غريبًا لهيكل بيانات العنوان في حالتنا، وهو function، إذ ترتبط الدوال عادةً بالعمليات وليس تجميعات البيانات، غير أن دوال جافاسكربت تستطيع التعامل مع كليهما، فإذا أردنا إنشاء كائن العنوان في جافاسكربت؛ فسنكتب الآتي: function Address(Hs,St,Town,Zip) { this.HsNum = Hs; this.Street = St; this.Town = Town; this.ZipCode = Zip; } نريد مرةً أخرى تجاهل الصياغة واستخدام الكلمة this هنا، والنظر إلى النتيجة التي ستكون مجموعةً من عناصر البيانات التي نستطيع القول بأنها تصف عنوانًا، ويمكننا معاملته على أنه وحدة واحدة. والآن، كيف نصل إلى تلك البيانات بعد إنشاء هياكلها؟ الوصول إلى الأنواع التي يعرفها المستخدم من الممكن إسناد نوع بيانات معقد إلى متغير، لكن لا يمكن الوصول إلى الحقول المنفردة دون استخدام بعض آليات الوصول الخاصة التي تحددها اللغة، وتكون نقطة . في الغالب. VBScript إذا استخدمنا حالة صنف العنوان الذي عرّفناه أعلاه، فسنكتب ما يلي: Dim Addr Set Addr = New Address Addr.HsNumber = 7 Addr.Street = "High St" Addr.Town = "Anytown" Addr.ZipCode = "123 456" MsgBox Addr.HsNumber & " " & Addr.Street & " " & Addr.Town نحدد هنا بُعد متغير جديد هو Addr باستخدام Dim، ثم نستخدم الكلمتين Set و New لإنشاء نسخة جديدة من صنف Address، ثم نسند القيم إلى حقول نسخة العنوان الجديدة، ثم نطبع العنوان في صندوق رسالة. بايثون على فرض أننا كتبنا تعريف الصنف أعلاه: >>> Addr = Address(7,"High St","Anytown","123 456") >>> print( Addr.HsNumber, Addr.Street, Addr.Town ) 7 High St Anytown ينشئ هذا نسخةً من نوع Address ويسند إليه المتغير Addr، ونستطيع تمرير قيم الحقل إلى الكائن الجديد عند إنشائه في بايثون، ثم نطبع حقول HsNumber و Street للنسخة المنشأة حديثًا باستخدام عامل النقطة .. كما يمكن إنشاء نسخ عنوان متعددة لكل منها قيمه المستقلة لأرقام البيوت والشوارع وغيرها. جرب هذا بنفسك لتتدرب عليه، فهل تستطيع معرفة كيفية استخدام هذا في مثال دفتر العناوين؟ جافاسكربت تشبه آلية جافاسكربت هنا ما سبق ذكره لكن مع بعض التعديل، غير أن الآلية الأساسية بسيطة ومباشرة: var addr = new Address(7, "High St", "Anytown", "123 456"); document.write(addr.HsNum + " " + addr.Street + " " + addr.Town); إحدى الآليات التي يمكن استخدامها في جافاسكربت أيضًا هي معاملة الكائن مثل قاموس واستخدام اسم الحقل كمفتاح: document.write( addr['HsNum'] + " " + addr['Street'] + " " + addr['Town']); وما لم نُعط اسم الحقل في هيئة سلسلة نصية كما في حالة قراءة ملف أو إدخال مستخدم للبرنامج، فلا ننصح باستخدام هذا النموذج. العوامل التي يعرفها المستخدم يمكن للأنواع التي يعرِّفها المستخدم أن تكون لها عمليات معرفة لها كذلك، وذلك أساس ما يعرف بالبرمجة كائنية التوجه. الكائن هو عبارة عن تجميعة من عناصر البيانات والعمليات المرتبطة بتلك البيانات، وهي مغلفة جميعًا في وحدة واحدة، حيث تستخدم بايثون الكائنات على نطاق واسع في مكتبتها القياسية للوحدات، كما تسمح لنا كوننا مبرمجين بإنشاء أنواع الكائنات الخاصة بنا. يحصل الوصول إلى عمليات الكائنات بنفس الطريقة التي يمكن الوصول بها إلى أعضاء البيانات لنوع معرَّف من قِبل المستخدم، أي من خلال عامل النقطة؛ أما غير هذا فتكون أشبه بالدوال، وتسمى تلك الدوال الخاصة بالتوابع methods، وقد رأيناها في حالة العملية append() في القوائم، أينما تذكر أنه من أجل استخدامها يجب أن نربط استدعاء الدالة باسم المتغير. >>> listObject = [] # قائمة فارغة >>> listObject.append(42) # استدعاء تابع لكائن القائمة >>> print( listObject ) [42] يجب استيراد الوحدة عند توفير نوع كائن - يُسمى صنفًا- في وحدة بايثون كما فعلنا في sys من قبل، ثم كتابة اسم الوحدة قبل اسم نوع الكائن عند إنشاء نسخة يمكن تخزينها في متغير مع استخدام الأقواس طبعًا، بعد ذلك نستطيع استخدام المتغير دون استخدام اسم الوحدة، ولتوضيح هذا لنَعُد إلى وحدة array التي ذكرناها من قبل، فهي توفر الصنف array، ولنستورد هذه الوحدة ثم ننشئ نسخةً من array، ونسند اسم myArray إليها، ثم نستخدم myArray فقط للوصول إلى عملياتها وبياناتها كما يلي: >>> import array >>> myArray = array.array('d') # array of reals(d), use module name >>> myArray.append(42) # use array operation >>> print( myArray[0]] ) # access array content 42.0 >>> print( myArray.typecode ) # access array attribute 'd' نستورد الوحدة array في السطر الأول إلى البرنامج، ثم نستخدم تلك الوحدة في السطر الثاني لإنشاء نسخة من صنف array باستدعائها مثل دالة، ونحتاج هنا إلى توفير سلسلة الرموز النوعية typecode string التي توضح نوع البيانات التي يجب تخزينها. تذكر أن المصفوفات الخالصة تخزن نوعًا واحدًا من البيانات، حيث سنصل في السطر الثالث إلى إحدى عمليات صنف المصفوفة، وهي append() التي تعامل الكائن myArray كأنه وحدة وكأن العملية داخل الوحدة، ثم نستخدم الفهرسة لجلب البيانات التي أضفناها، نستطيع في الأخير الوصول إلى بعض البيانات الداخلية، إذ يخزن typecode النوع الذي مررناه عند إنشاء المصفوفة، من داخل كائن myArray صياغةً تشبه صياغة الوحدة. لا يوجد فرق كبير بين استخدام الكائنات التي توفرها الوحدات، وبين الدوال الموجودة في الوحدات، باستثناء الحاجة إلى إنشاء نسخة instance، ويمكن النظر إلى اسم الكائن على أنه عنوان يحفظ الدوال والمتغيرات المرتبطة به مجموعةً معًا. إحدى الطرق الأخرى التي ننظر بها للأمر هي أن الكائنات تمثل أشياء من العالم الحقيقي ونحن نستخدمها أو نفعل بها أمورًا تنفعنا، وهذا المنظور على بساطته إلا أنه الفكرة التي نشأت بسببها الكائنات في البرمجة، فنحن نكتب -برمجيًا- محاكاةً لمواقف في العالم الحقيقي. تستطيع كل من جافاسكربت وVBScript أن تتعامل مع الكائنات، وقد كان هذا ما استخدمناه في كل أمثلة العناوين أعلاه، فقد عرفّنا صنفًا ثم أنشأنا نسخةً أسندناها إلى متغير كي نستطيع الوصول إلى خصائص تلك النسخة، ولهذا ارجع إن شئت إلى ما سبق من الشرح عن الأصناف والكائنات، وانظر كيف توفر الأصناف آليات لإنشاء أنواع جديدة من البيانات في برامجنا من خلال الربط بين بيانات النوع الجديد وعملياته معًا. العوامل الخاصة ببايثون إنّ الهدف من هذه السلسلة هو تعليم البرمجة للمبتدئين، ورغم أننا نستخدم بايثون في الشرح؛ إلا أنك تستطيع قراءة لغة برمجة أخرى واستخدامها بدلًا منها، بل إن هذا هو عين ما نتوقعه منك، إذ أنه لا توجد لغة برمجة بما فيها بايثون، تستطيع تنفيذ جميع المهام وحل كل المشاكل التي تواجهك برمجيًا، وبسبب ذات الهدف لن نشرح جميع الخصائص الموجودة في بايثون، بل سنركز على تلك التي يمكن إيجادها في لغات البرمجة الأخرى كذلك، ولهذا سنهمل شرح بعض الخصائص بالغة القوة في بايثون، والتي تميزها عما سواها، مثل العوامل الخاصة special operators، ذلك أن أغلب لغات البرمجة تدعم عمليات بعينها لا تدعمها لغات أخرى، وتلك العوامل الفريدة هي التي تسبب ظهور لغات برمجة جديدة، وهي من العوامل المهمة في تحديد شهرة تلك اللغات من حيث الاستخدام فيما بعد. حيث تدعم بايثون مثلًا عمليات غير شائعة نسبيًا في لغات البرمجة الأخرى، مثل تشريح القائمة spam[X:Y] (إلى شرائح) list slicing من أجل استخراج شريحة من وسط القائمة أو السلسلة النصية أو الصف tuple، كما تدعم إسناد الصف Tuple الذي يسمح لنا بإسناد عدة قيم متغيرات مرةً واحدةً: X, Y = (12, 34). كما تسهل تنفيذ العمليات على كل عضو من أعضاء تجميعة ما باستخدام الدالة map() التي سنشرحها في مقال لاحق من هذه السلسلة، إضافةً إلى الكثير من الخصائص التي جعلت بايثون تشتهر بأنها تأتي "مع البطارية" إشارةً إلى كثرة الملحقات والمزايا التي تأتي معها. ارجع إلى توثيق بايثون للمزيد عن تلك المزايا. تجدر أخيرًا الإشارة إلى أنه رغم قولنا بأن تلك العوامل خاصة ببايثون؛ إلا أن هذا لا يعني أنها لا توجد في لغات برمجة أخرى، بل أنها لن توجد مجتمعةً في كل لغة، فالعوامل التي نشرحها في هذه السلسلة تكون متاحةً بشكل أو بآخر في أغلب لغات البرمجة الحديثة. يلخص هذا نظرتنا على المواد الخام للبرمجة، وسننتقل الآن إلى موضوع آخر نرى فيه كيف نستغل تلك المواد الخام في البرمجة الحقيقية. شرح لمثال العناوين رغم قولنا أننا سنشرح تفاصيل هذا المثال لاحقًا، إلا أننا رأينا صعوبة تنفيذ مثال بايثون من بعض القراء، ولهذا سيشرح هذا القسم شيفرة بايثون سطرًا سطرًا. ستبدو شيفرة المثال كاملة كما يلي: >>> class Address: ... def __init__(self, Hs, St, Town, Zip): ... self.HsNumber = Hs ... self.Street = St ... self.Town = Town ... self.Zip_Code = Zip ... >>> addr = Address(7,"High St","Anytown","123 456") >>> print( addr.HsNumber, addr.Street ) وسنشرح الآن سطرًا سطرًا: >>> class Address: تخبر التعليمة class بايثون أننا على وشك تعريف نوع جديد اسمه Address في هذه الحالة، وتشير النقطتان الرأسيتان إلى أن أي أسطر مزاحة تالية ستكون جزءًا من تعريف الصنف، وينتهي التعريف عند أول سطر غير مزاح، فإذا كنت تستخدم IDLE؛ فسترى أن المحرر قد أزاح السطر التالي تلقائيًا، أما إذا كنت تعمل من محث بايثون في سطر أوامر DOS مثلًا، فسيكون عليك إزاحة الأسطر كما هو موضح أعلاه، ولا تهتم بايثون بمقدار الإزاحة طالما أنها نفسها لكل سطر. أما السطر الثاني: ... def __init__(self, Hs, St, Town, Zip): يطلق على العنصر الأول في الصنف الخاص بنا "تعريف التابع method definition"، ويجب أن يكون حول الاسم شرطتان سفليتان على كل ناحية منه، وهي طريقة بايثون للأسماء التي لها أهمية خاصة، ويسمى ذلك التابع __init__، وهو عملية خاصة تنفذها بايثون حين ننشئ نسخةً من الصنف الجديد الخاص بنا كما سنرى بعد قليل؛ أما النقطتان الرأسيتان فتخبران بايثون أن مجموعة الأسطر المزاحة التالية ستكون هي التعريف الحقيقي للتابع. والسطر الثالث: ... self.HsNumber = Hs يُسنِد هذا السطر -والأسطر الثلاثة التالية- قيمًا إلى الحقول الداخلية للكائن الخاص بنا، وهذه الأسطر مزاحة من تعليمة def لتخبر بايثون أنها تشكل التعريف الحقيقي لعملية __init__، أما السطر الفارغ، فيخبر مفسر بايثون بأن تعريف الصنف قد انتهى، لنعود مرةً أخرى إلى محث بايثون المعتاد >>>. >>> addr = Address(7,"High St","Anytown","123 456") ينشئ هذا نسخةً جديدةً من النوع Address، وتستخدم بايثون عملية __init__ المعرَّفة أعلاه لإسناد القيم التي أعطيناها إلى الحقول الداخلية، وتسنَد النسخة إلى متغير addr كما تسنَد أي نسخة لأي نوع بيانات آخر. >>> print( addr.HsNumber, addr.Street ) نطبع الآن قيم حقلين من الحقول الداخلية مستخدمين عامل النقطة للوصول إليهما. خاتمة يُعَد هذا المقال دسمًا وغنيًا بالمعلومات النظرية التي تؤسس لتعلم البرمجة على أسس سليمة، وسنعود إلى ما فيه مرات كثيرة أثناء الشرح، بما أن البيانات وأنواعها هي اللبنات التي تُصنع منها البرامج، لكن نريد التذكير هنا بأن بايثون تسمح لنا بإنشاء أنواع البيانات التي نريدها بأنفسنا ونستخدمها مثل أي نوع مضمّن فيها، وأن المتغيرات تشير إلى بيانات، وقد نحتاج إلى التصريح عنها قبل تعريفها، وهي تأتي في أنواع شتى، ويتوقف نجاح العملية التي نجريها على نوع البيانات الذي نستخدمه. ومن أنواع البيانات البسيطة سلاسل المحارف والأعداد والقيم البوليانية أو قيم "الحقيقة"، كما تشمل أنوع البيانات المعقدة التجميعات والملفات والبيانات وأنواع البيانات المعرّفة من قِبل المستخدم. توجد أيضًا العديد من العوامل في كل لغة برمجة، وتيُعَد تعلّم تلك العوامل جزءًا من التعود على أنواع بياناتها وعلى العمليات المتاحة لتلك الأنواع، وقد يكون العامل الواحد متاحًا لعدة أنواع، غير أن النتيجة قد لا تكون هي نفسها في كل مرة، بل ربما تختلف اختلافًا كبيرًا. سنتابع مع المقال الموالي مع ذكر نوع أخر مهم من البيانات، وهو التجميعات Collections. ترجمة -بتصرف- للفصل الخامس: The Raw Materials من كتاب Learning To Program لصاحبه Alan Gauld. اقرأ أيضًا المقال التالي: مدخل إلى البيانات وأنواعها: التجميعات Collections المقال السابق: التسلسلات البسيطة في البرمجة مدخل إلى البيانات الوصفية (microdata) في HTML5 هياكل البيانات: الكائنات والمصفوفات في جافاسكريبت
-
أشرنا في المقال السابق إلى عدة أنواع أساسية من البيانات البرمجية، وفي هذا المقال سنتابع مع ذكر نوع هام جدًا منها، وهو البيانات التجميعية collections. التجميعات Collections لقد شكلت علوم الحاسوب نظامًا كاملًا يدرس التجميعات وسلوكياتها المختلفة، والتي تسمى بالحاويات أحيانًا أو التسلسلات، وسننظر أولًا في التجميعات المدعومة في بايثون وجافاسكربت وVBScript، ثم نختم بملخص موجز لبعض أنواع التجميعات الأخرى التي قد نراها في لغات أخرى. القوائم Lists لا شك في أن القوائم غنية عن تعريفها، إذ نستخدمها في حياتنا كل يوم، فهي مجرد سلسلة من العناصر، حيث نستطيع إضافة عناصر إلى تلك السلسلة أو حذفها، وعادةً ما نضيف العناصر في نهاية القائمة إذا كانت مكتوبةً على الورق، فلا نستطيع إدخال عنصر في المنتصف مثلًا، على عكس القوائم الإلكترونية في برنامج معالج النصوص مثلًا، إذ نستطيع وضع العنصر الجديد في أي مكان، كما نستطيع البحث في القائمة لنتحقق من وجود شيء بعينه فيها، لكن يجب التنقل في القائمة من أعلاها لأسفلها عنصرًا عنصرًا متحققين من كل عنصر لنرى إذا كان ما نريده موجودًا أم لا، وتُعَد القوائم أحد أنواع التجميعات التي لا غنى عنها في أغلب لغات البرمجة الحديثة. لا تحتوي جافاسكربت ولا VBScript على قوائم مضمّنة فيها، لكن نستطيع محاكاة مزايا القوائم بواسطة المصفوفات في جافاسكربت، وبتجميعات بيانات أخرى في VBScript كما سنشرح لاحقًا؛ أما لغة بايثون فالقوائم مضمّنة تلقائيًا فيها، وتستطيع تنفيذ العمليات الأساسية التي تحدثنا عنها جميعًا، إضافةً إلى القدرة على فهرسة العناصر فيها، والفهرسة هي الإشارة إلى عنصر في القائمة برقم تسلسله، على فرض أن العنصر الأول يبدأ من الصفر، كما توفر بايثون عدة عمليات أخرى على التجميعات، وتنطبق كلها تقريبًا على القوائم، كما تنطبق مجموعة فرعية منها على أنواع أخرى من التجميعات بما في ذلك السلاسل النصية التي هي مجرد نوع خاص من القوائم، إذ هي قوائم من المحارف. تُستخدم الأقواس المربعة لإنشاء القوائم والوصول إليها في بايثون، ويمكن إنشاء قائمة فارغة باستخدام زوج من تلك الأقواس ليس بينهما شيء، أو إنشاء قائمة تحتوي على قيم تفصل بينها فاصلة إنجليزية ",": >>> aList = [] >>> another = [1,2,3] >>> print( another ) [1, 2, 3] نستطيع الوصول إلى العناصر المنفردة باستخدام رقم الفهرس، حيث يحمل العنصر الأول رقم 0 داخل الأقواس المربعة، فإذا أردنا الوصول إلى العنصر الثالث مثلًا -والذي سيحمل الترتيب 2 داخل الأقواس بما أننا نبدأ من الصفر-، فإننا نصل إليه كما يلي: >>> print( another[2] ) 3 كما نستطيع تغيير قيم العناصر في القائمة بطريقة مماثلة: >>> another[2] = 7 >>> print( another ) [1, 2, 7] لاحظ كيف تغير العنصر الثالث -الذي فهرسه 2- من 3 إلى 7. يمكن استخدام أرقام الفهرس السالبة للوصول إلى عناصر القائمة من نهايتها، فإذا أردنا العنصر الأخير من القائمة نستخدم -1: >>> print( another[-1] ) 7 وتضاف العناصر الجديدة ملحقةً إلى نهاية القائمة باستخدام العملية append(): >>> aList.append(42) >>> print( aList ) [42] كما يمكن الاحتفاظ بقائمة داخل قائمة أخرى، فإذا ألحقنا القائمة الثانية بالقائمة الأولى فستكون على النحو التالي: >>> aList.append(another) >>> print( aList ) [42, [1, 2, 7]] لاحظ كيف تكون النتيجة قائمةً من عنصرين، لكن العنصر الثاني عبارة عن قائمة بحد ذاته كما نرى من القوسين المحيطين به، ونستطيع الآن الوصول إلى العنصر 7 باستخدام فهرس مزدوج: >>> print( aList[1][2] ) 7 حيث يستخرج الفهرس 1 -الفهرس الأول- العنصر الثاني من القائمة الأولى -الذي هو نفسه القائمة الثانية-، ثم يستخرج الفهرس 2 -الفهرس الثاني- العنصر الثالث في القائمة الثانية، وهو العنصر 7. هذا التداخل وإن بدا معقدًا إلا أنه مفيد للغاية، حيث يسمح ببناء جداول من البيانات بفعالية، كما يلي: >>> row1 = [1,2,3] >>> row2 = ['a','b','c'] >>> table = [row1, row2] >>> print( table ) [ [1,2,3], ['a','b','c'] ] >>> element2 = table[0][1] >>> print( element2 ) 2 يمكن استخدام هذه الطريقة في إنشاء دفتر عناوين يكون كل مدخل فيه عبارةً عن قائمة من الاسم والعنوان، ويوضح المثال التالي دفتر عناوين فيه مدخلان: >>> addressBook = [ ... ['Samy', '9 Some St',' Anytown', '0123456789'], ... ['Warda', '11 Nother St', 'SomePlace', '0987654321'] ... ] >>> لاحظ كيف أنه رغم إدخالنا لأربعة أسطر من النص، إلا أن بايثون تعاملت معها على أنها سطر واحد، كما نستنتج من محثات ...، وهذا لأن بايثون ترى أن عدد الأقواس الافتتاحية لا يطابق عدد أقواس الإغلاق، ولهذا تستمر في قراءة المدخلات إلى أن يحدث ذلك التطابق. قد تكون هذه طريقةً فعالةً للغاية في بناء هياكل بيانات معقدة بسرعة مع جعل الهيكل العام -أي قائمة القوائم- واضحًا للقارئ. تدريب استخرج رقم هاتف سامي -العنصر 3 من الصف الأول-، وتذكر أن الفهارس تبدأ من الصفر وليس الواحد، ثم أضف بعض المدخلات من عندك باستخدام عملية append(). لاحظ أننا نفقد بياناتنا عند الخروج من بايثون، لكن سنرى كيفية الاحتفاظ بها عند شرح الملفات في هذا الكتاب. يُستخدم الأمر del لحذف العناصر من القائمة: >>> del aList[1] >>> print( aList ) [42] لاحظ أن del لا يحتاج إلى أقواس حول القيمة، على عكس دالة الطباعة، لأنه أمر وليس دالةً، قد يكون هذا الفرق طفيفًا، لكن إذا وضعت أقواسًا حول القيمة فلا بأس إذ ستظل صالحةً. إذا أردنا ضم قائمتين معًا لجعلهما قائمةً واحدةً، فسنتخدم عامل الضم + الذي رأيناه في ضم السلاسل النصية من قبل: >>> newList = aList + another >>> print( newList ) [42, 1, 2, 7] لاحظ اختلاف هذا المثال عن سابقه الذي ألحقنا فيها القائمتين معًا، فقد كان لدينا عنصران، وكان العنصر الثاني منهما عبارةً عن قائمة؛ أما هنا فلدينا أربعة عناصر لأن عناصر القائمة الثانية أضيفت عنصرًا عنصرًا إلى newList، فإذا أردنا الوصول إلى العنصر 1 هذه المرة فسنحصل على 1، وهو العنصر الثاني هنا، بدلًا من الحصول على قائمة فرعية. >>> print( newList[1] ) 1 يمكن تطبيق إشارة عملية الضرب هنا مثل عامل تكرار لملء القائمة بمضاعفات نفس القيمة: >>> zeroList = [0] * 5 >>> print( zeroList ) [0, 0, 0, 0, 0] كما يمكن إيجاد فهرس عنصر ما في قائمة باستخدام العملية index() كما يلي: >>> print( [1,3,5,7].index(5) ) 2 >>> print( [1,3,5,7].index(9) ) Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: list.index(x): x not in list لاحظ أن محاولة العثور على فهرس لشيء غير موجود في القائمة ينتج خطأً، فإذا أردنا التحقق من وجود شيء في تجميعة ما فسنستخدم العامل in كما يلي: >>> print( 5 in [1,3,5,7] ) True >>> print( 9 in [1,3,5,7] ) False النتائج قيم بوليانية كما نرى، إما True أو False، وسنرى كيف نستخدم تلك النتائج في فصل لاحق. أخيرًا، يمكن معرفة طول القائمة باستخدام الدالة المضمَّنة len(): >>> print( len(aList) ) 1 >>> print( len(newList) ) 4 >>> print( len(zeroList) ) 5 لا تدعم جافاسكربت ولا VBScript نوع القائمة مباشرةً، وسنرى أن لديهما نوع بيانات يحاكي وظيفة القائمة هنا، وهو نوع المصفوفات arrays. الصف Tuple توفر بعض لغات البرمجة بنية بيانات اسمها صف tuple، وما هي إلا تجميعة عشوائية من القيم لكنها تعامَل مثل وحدة واحدة، وهي تشبه القوائم في نواحٍ كثيرة، لكن مع فرق أن وحدات tuple غير قابلة للتغيير، مما يعني أننا لا نستطيع التعديل عليها أو إلحاق شيء إليها بعد إنشائها أول مرة، تمثَّل وحدات tuple في بايثون بقائمة من القيم المفصول بينها بفواصل: >>> aTuple = 1,3,5 >>> print( aTuple[1] ) # استخدم الفهرسة مثل القوائم 3 >> aTuple[2] = 7 # tuple خطأ، لا يمكن تعديل Traceback (innermost last): File "<pyshell#20>", line 1, in ? aTuple[2] = 7 TypeError: object doesn't support item assignment من المعتاد إحاطة التسلسل بأقواس، وهذا يضمن عدم إخراج القيم من السياق، إضافةً إلى التذكير المرئي بأنها وحدة واحدة، أي وحدة صف، توضح البيانات التالية هذا الأمر: >>> t1 = 1,2,3 * 3 >>> t2 = (1,2,3) * 3 >>> print(t1) (1, 2, 9) >>> print( t2 ) (1, 2, 3, 1, 2, 3, 1, 2, 3) يطبَّق الضرب في الحالة الأولى على العنصر الأخير فقط في وحدة Tuple، أما في الحالة الثانية فتضاعَف المجموعة بالكامل، ولتجنب مثل هذا الغموض، سنستخدم الأقواس في كل مرة نصف فيها وحدات tuple. لعل أحد أهم الأمور التي يجب تذكرها هو أن الأقواس المربعة تستخدم لفهرسة وحدات tuple، بينما تُستخدم الأقواس العادية لتعريفها، ولا يمكن تغييرها بعد إنشائها؛ أما في سوى هذه الأمور فما ينطبق من العمليات على القوائم ينطبق أيضًا على الصفوف، ورغم أننا لا نستطيع تعديل وحدة الصف بعد إنشائها؛ إلا أننا نستطيع إضافة أعضاء جديدة إليها باستخدام عامل الإضافة، فهذا سينشئ صفًا جديدًا، كما يلي: >>> tup1 = (1,2,3) >>> tup2 = tup1 + (4,) # وليس عددًا صحيحًا tuple الفاصلة تجعل هذا وحدة >>> print( tup2 ) (1,2,3,4) إذا لم نستخدم الفاصلة اللاحقة بعد العدد 4 فستفسره بايثون على أنه العدد الصحيح 4 داخل أقواس وليس على أنه صف، لكن سنحصل على خطأ بأي حال بما أنه لا يمكن إضافة أعداد صحيحة إلى الصفوف، لذا سنضيف الفاصلة لنخبر بايثون بأن تعامل الأقواس على أنها صف، فبإضافة الفاصلة الزائدة نقنع بايثون بأن وحدة الصف التي فيها مدخل واحد هي صف فعلًا؛ أما بالنسبة للغتي جافاسكربت وVBScript فليس لديهما أي مفهوم للصف tuple. القاموس Dictionary أو Hash يحتوي هذا النوع من البيانات على قيمة ترتبط بمفتاح، كما يربط القاموس اللغوي المعنى بكلمة، وقد تكون تلك القيمة سلسلةً نصيةً أو لا، وتُجلب تلك القيمة بفهرسة القاموس بالمفتاح، ولا يلزم أن يكون المفتاح سلسلةً من الأحرف -رغم أنه غالبًا كذلك-، وهذا على خلاف القاموس اللغوي، فقد يكون أي نوع غير قابل للتغير، بما في ذلك الأعداد والصفوف، كما قد تحتوي القيم المرتبطة بالمفاتيح على أي نوع من البيانات. تُستخدم القواميس داخليًا بواسطة تقنية برمجة متقدمة تسمى بجداول Hash، لهذا يشار أحيانًا إلى القاموس باسم Hash، ونظرًا لأننا نتوصَّل إلى قيم القاموس عبر المفتاح، فلا توضع إلا عناصر ذات مفاتيح فريدة، رغم أن المفتاح قد يشير إلى قائمة من القيم. تُعَد القواميس مفيدةً للغاية في هيئة هياكل للبيانات، ونجدها في بايثون مثل نوع بيانات مضمَّن في اللغة نفسها، لكن قد نحتاج في بعض اللغات الأخرى إلى استخدام وحدة module، أو بناء واحدة بأنفسنا إذا أردنا استخدام القواميس، وتُستخدم القواميس بعدة طرق سنراها كثيرًا في الأمثلة التالية من الكتاب؛ أما الآن فسنرى أولًا كيف ننشئ قاموسًا في بايثون، ثم نملؤه ببعض المدخلات ونقرؤها. >>> dct = {} >>> dct['boolean'] = "A value which is either true or false" >>> dct['integer'] = "A whole number" >>> print( dct['boolean'] ) A value which is either true or false لاحظ أننا نبدأ القاموس بأقواس معقوصة ثم نستخدم الأقواس المربعة لإسناد القيم وقراءتها، كما تُستخدم دالة النوع dict() لإعادة قاموس فارغ، ويمكن بدء القاموس أثناء إنشائه كما فعلنا في حالة القوائم، باستخدام الصيغة التالية: >>> addressBook = { ... 'Samy' : ['Samy', '9 Some St',' Anytown', '0123456789'], ... 'Warda' : ['Warda', '11 Nother St', 'SomePlace', '0987654321'] ... } >>> يُفصل المفتاح والقيمة بنقطتين رأسيتين، وتُفصل الأزواج بفواصل إنجليزية ,، كما يمكن تخصيص القاموس باستخدام صيغة مختلفة -انظر أدناه-، ويمكنك اختيار إحدى الطريقتين لاستخدامها. >>> book = dict(Samy=['Samy', '9 Some St',' Anytown', '0123456789'], ... Warda=['Warda', '11 Nother St', 'SomePlace', '0987654321']) >>> print( book['Samy'][3] ) 0123456789 لاحظ أننا لا نحتاج إلى علامات اقتباس حول المفتاح في التعريف، لأن بايثون تفترض أنها سلسلة نصية، لكننا نحتاج إليها بأي حال من أجل استخراج القيم، وهذا الأسلوب يَحُد من عملية الطريقة الثانية، لهذا يفضِّل المبرمجون استخدام الطريقة الأولى التي يستخدمون الأقواس المعقوصة فيها، وبهذا نكون قد أنشأنا دفتر عناوين من قاموس مفاتيحه هي الأسماء، ويخزن قوائمنا مثل قيم، ونريد أن نستخدم الاسم وحده لجلب كل المعلومات بدلًا من حساب الفهرس العددي، كما يلي: >>> print( addressBook['Warda'] ) ['Warda', '11 Nother St', 'SomePlace', '0987654321'] >>> print( addressBook['Samy'][3] ) 0123456789 في الحالة الثانية فهرسنا القيم المعادة من أجل الحصول على رقم هاتف فقط، لكن يمكن جعل هذا أسهل عبر إنشاء بعض المتغيرات وإسناد قيم الفهرسة المناسبة، كما يلي: >>> name = 0 >>> street = 1 >>> town = 2 >>> tel = 3 نستطيع الآن استخدام تلك المتغيرات لنعرف اسم مدينة Warda: >>> print( addressBook['Warda'][town] ) SomePlace لاحظ أن town ليست داخل علامات اقتباس لأنها اسم متغير، وستحوله بايثون إلى قيمة الفهرس التي أسندناها -وهي 2-، على عكس 'Warda' المحاطة بعلامات اقتباس لأن المفتاح هو سلسلة نصية، وهنا بدأ دفتر العناوين يتحول إلى ما يشبه التطبيق القابل للاستخدام بسبب مزايا القواميس، حيث لن نحتاج إلى كثير من العمل الإضافي لحفظ البيانات واستعادتها وإضافة محث استعلام ليسمح لنا بتحديد البيانات التي نريدها. يمكن استخدام قاموس لتخزين البيانات، وسيكون دفتر العناوين حينها قاموسًا مفاتيحه الأسماء، وستكون القيم قواميس مفاتيحها هي حقول الأسماء، كما يلي: addressBook = { ... 'Samy' : {'name': 'Samy', ... 'street': '9 Some St', ... 'town': 'Anytown', ... 'tel': '0123456789'}, ... 'Warda' : {'name': 'Warda', ... 'street': '11 Nother St', ... 'town': 'SomePlace', ... 'tel': '0987654321'} ... } لاحظ أن هذا التنسيق سهل القراءة رغم أنه يحتاج إلى الكثير من الكتابة، ويُشار إلى مثل هذه البيانات التي تخزَّن بتنسيق يجمع بين المعنى والمحتوى في تنسيق مقروء للبشر باسم البيانات الموثقة ذاتيًا. حين ندخِل هيكل بيانات داخل هيكل آخر مطابق له، كما في حالة إدخال قاموس داخل قاموس آخر هنا، فإنه يُسمى بالتداخل أو التشعب nesting، حيث يكون القاموس الداخلي قاموسًا متشعبًا من الخارجي، ويمكن الوصول إلى تلك البيانات بطريقة تشبه أسلوب القوائم ذات الفهارس المسماة: >>> print( addressBook['Warda']['town'] ) SomePlace وليس ثمة اختلاف إلا في الاقتباس الإضافي حول كلمة town التي هي ليست موجودةً في حالة القوائم، وميزة هذا الأسلوب أننا نستطيع إدخال حقول جديدة دون تعطيل الشيفرة الحالية، بينما نضطر في حالة الفهارس المسماة إلى العودة لتغيير جميع قيم الفهارس، وتبرز أهمية هذا الأسلوب عند استخدام نفس البيانات في عدة برامج، فيكون بذل قليل من الجهد هنا موفرًا لكثير من العمل لاحقًا في المستقبل. لا تدعم القواميس كثيرًا من عوامل التجميعات التي رأيناها من قبل، فلا تدعم عامل الضم ولا التكرار ولا الإلحاق، رغم إمكانية إسناد أزواج من المفاتيح والقيم مباشرةً كما رأينا في بداية هذا القسم. وتُستخدم العملية keys() لتسهيل الوصول إلى مفاتيح القاموس، إذ تسمح لنا بالحصول على قائمة من جميع المفاتيح الموجودة في قاموس ما، فإذا أردنا الحصول على كل الأسماء الموجودة في دفتر العناوين الذي أنشأناه من قبل؛ فسيكون ذلك كما يلي: >>> print( list(addressBook.keys()) ) ['Samy','Warda'] لاحظ أننا استخدمنا list() للحصول على قيم المفاتيح الحقيقية، أما إذا حذفنا list() فسنحصل على نتيجة غريبة بعض الشيء لن نذكرها الآن. لاحظ أن القواميس لا تخزِّن مفاتيحها بنفس ترتيب إدخالها، فقد نرى أن المفاتيح تظهر بترتيب غريب عن ترتيبها الأول، بل قد يتغير الترتيب بتغير الوقت، لكن هذا لا يهم بما أننا نستطيع استخدام المفاتيح للوصول إلى البيانات المرتبطة بها، إذ سنحصل على القيم الصحيحة في كل مرة. يمكن الحصول على قائمة بجميع القيم أيضًا باستخدام العملية values()، فجرب ذلك على دفتر العناوين وانظر إن استطعت تنفيذها بنجاح، استخدم مثال keys() السابق للاسترشاد به، ويمكن بصورة مماثلة استخدام العامل in على القاموس ليخبرنا هل توجد قيمة ما في صورة مفتاح للقاموس أم لا. قواميس VBScript توفر VBScript كائن قاموس يقدم تسهيلات تماثل قاموس بايثون، لكن استخدامه مختلف قليلًا عن حالة بايثون، حيث ننشئ القاموس هنا بالتصريح عن متغير يحمل الكائن، ثم ننشئ ذلك الكائن، وبعد ذلك نضيف المدخلات إلى القاموس الجديد كما يلي: Dim dict ' Create a variable. Set dict = CreateObject("Scripting.Dictionary") dict.Add "a", "Athens" ' Add some keys and items. dict.Add "b", "Belgrade" dict.Add "c", "Cairo" لاحظ أن دالة CreateObject توضح أننا ننشئ كائن "Scripting.Dictionary"، وهو كائن Dictionary من وحدة Scripting الخاصة بلغة VBScript، وسندرس ذلك بالتفصيل حين نأتي على الكائنات، لكننا نأمل بذكرها هنا أن تتذكر مفهوم استخدام كائن من وحدة ما كما ذكرنا في فصل التسلسلات البسيطة. يجب كذلك استخدام كلمة Set المفتاحية عند إسناد كائن إلى متغير في VBScript. نستطيع الآن أن نصل إلى البيانات كما يلي: item = dict.Item("c") ' Get the item. dict.Item("c") = "Casablanca" ' Change the item توجد عمليات أخرى لإزالة عنصر ما، والتحقق من وجود مفتاح ما، والحصول على قائمة بجميع المفاتيح وغير ذلك. كما يمكن تخزين أي نوع من الكائنات في القاموس إذ أن الأمر ليس مقصورًا على السلاسل النصية فقط، وهذا يعني أننا نستطيع إنشاء قواميس متشعبة بما أن القاموس نفسه ما هو إلا كائن. فيما يلي نسخة كاملة مبسطة عن مثال دفتر العناوين السابق، ولكن في VBScript: <script type="text/VBScript"> Dim addressBook Set addressBook = CreateObject("Scripting.Dictionary") addressBook.Add "Samy", "Samy, 9 Some St, Anytown, 0123456789" addressBook.Add "Warda", "Warda, 11 Nother St, SomePlace, 0987654321" MsgBox addressBook.Item("Warda") </script> ما فعلناه هنا هو تخزين جميع البيانات مثل سلسلة نصية واحدة بدلًا من استخدام قائمة -وهذا يصعّب استخراج الحقول المنفردة على عكس القائمة والقاموس-، ثم نستطيع الوصول إلى تفاصيل Warda ونطبعها في صندوق رسالة. قواميس جافاسكربت لا تحتوي جافاسكربت على كائن قاموس فيها، رغم أننا نستطيع الوصول إلى كائن Scripting.Dictionary الذي ذكرناه في VBScript باستخدام إنترنت إكسبلورر، لنتمكن حينئذ من استغلال جميع مزاياه، لكن لن نشرح ذلك هنا مرةً أخرى بما أنه نفس الكائن. تستطيع هنا الانتقال إلى الفصل التالي إذا كنت قد شعرت بالملل من طول هذا الفصل وتريد كتابة شيفرات وتجربتها، لكن تذكر أن تعود وتنهي هذا الفصل حين تتعرض لأنواع بيانات لم نذكرها حتى الآن. المصفوفات أو المتجهات Arrays المصفوفة هي أحد أنواع التجميعات التي يمتد تاريخها موازيًا لتاريخ الحوسبة، وما هي إلا مجموعة من العناصر المفهرسة بحيث يسهل جلبها بسرعة، والغالب أننا يجب أن نحدد عدد العناصر التي نريد تخزينها في المصفوفة مسبقًا، إذ يُخزَّن نوع واحد أيضًا من البيانات في المصفوفة عادةً، وهاتان الخاصيتان للمصفوفة هما اللتان تميزانها عن القوائم مثلًا. لاحظ أننا قد قلنا "عادة"، و"الغالب" للدلالة على غالب الحالات، ذلك أن لغات البرمجة المختلفة لها أساليبها الخاصة التي تصف المصفوفات، لذا يصعب وضع وصف يغطي جميع الحالات. تدعم لغة بايثون المصفوفات من خلال وحدة تسمى array، لكننا لا نحتاج إليها إلا نادرًا مع استثناء حالات الحساب الرياضي عالي الأداء؛ أما غير ذلك فنستخدم القوائم، وفي لغتي جافاسكربت وVBScript؛ تتكون المصفوفات مثل نوع بيانات، لذا سننظر في كيفية استخدامها فيهما. مصفوفات VBScript تكون المصفوفة في VBScript تجميعة بيانات ذات طول ثابت، حيث يمكن التوصَل إليها بواسطة فهرس عددي، ويصرَّح عنها ويوصَل إليها كما يلي: Dim AnArray(42) ' A 43! element array AnArray(0) = 27 ' index starts at 0 AnArray(1) = 49 AnArray(42) = 66 ' assign to last element myVariable = AnArray(1) ' read the value تُستخدم الكلمة المفتاحية Dim لتحديد أبعاد المتغير، وهي الطريقة التي نخبر بها VBScript عن المتغير، فهي تتوقع إذا بدأنا الشيفرة بـ OPTION EXPLICIT؛ أن نحدد أبعاد أي متغير نستخدمه من خلال Dim، وهذا سلوك محمود يؤدي إلى برامج ثابتة قوية الأداء، كما أننا حددنا آخر فهرس صالح 42، وهذا يعني أن المصفوفة فيها 43 عنصرًا بما أنها تبدأ من الصفر. تُستخدم الأقواس في VBScript لحساب أبعاد المصفوفة وفهرستها، وهذا على خلاف الأقواس المربعة المستخدمة في بايثون وجافاسكربت، حيث لا يوجد في VBScript نوع بيانات إلا النوع Variant، والذي يخزِّن أي نوع من أنواع القيم، وبناءً عليه فلا تخزِّن المصفوفات في هذه اللغة إلا المتغيرات بما أن عناصر المصفوفة تكون من نوع واحد، لكن في نفس الوقت، فإن المتغير هنا قد يحمل أي نوع من أنواع القيم، مما يعني مرةً أخرى أن المصفوفات في VBScript تخزن أي شيء، وهذا أمر مربك عند التفكير فيه. نستطيع أيضًا الإسناد إلى أي موضع في المصفوفة بما في ذلك الموضع الأخير، من غير أن نملأ المواضع التي قبله، وسيُضبط أي عنصر غير مهيأ إلى القيمة الخاصة Null. كما يمكن التصريح عن عدة مصفوفات متعددة الأبعاد لنمذجة جداول بيانات كما في قوائم بايثون إذا استخدمنا تدريب دفتر العناوين مثلًا. Dim MyTable(2,3) ' 3 rows, 4 columns MyTable(0,0) = "Samy" ' Populate Samy's entry MyTable(0,1) = "9 Some Street" MyTable(0,2) = "Anytown" MyTable(0,3) = "0123456789" MyTable(1,0) = "Warda" ' And now Warda... ...and so on... لكن لا توجد طريقة سهلة لملء البيانات دفعةً واحدةً كما فعلنا في قوائم بايثون، بل يجب أن نملأ كل حقل على حدة، وعلى الرغم من وجود الدالة Arrayفي VBScript والتي تستطيع ملء مصفوفة دفعةً واحدةً -مما يعني الحاجة إلى جعلها متشعبةً-؛ فستصبح معقدةً وصعبة القراءة. إذا دمجنا قواميس VBScript مع هذه الخاصية لمصفوفاتها، فسنحصل على نفس النتيجة التي حصلنا عليها في بايثون، كما يلي: <script type="text/VBScript"> Dim addressBook Set addressBook = CreateObject("Scripting.Dictionary") Dim Samy(3) Samy(0) = "Samy" Samy(1) = "9 Some St" Samy(2) = "Anytown" Samy(3) = "0123456789" addressBook.Add "Samy", Samy MsgBox addressBook.Item("Samy")(3) ' Print the Phone Number </script> وآخر ملاحظة متعلق بمصفوفات VBScript هو أنها لا تحتاج إلى أن تكون ثابتة الحجم، على أن هذا لا يعني استمرار إضافة العناصر إليها كما في القوائم، بل يجب أن نغير حجم المصفوفة صراحةً، بالتصريح عن مصفوفة ديناميكية، وذلك بإهمال ذكر الحجم ببساطة، كما يلي: Dim DynArray() ' no size specified ثم إذا أردنا تغيير حجمها نفعل ذلك كما يلي: <script type="text/vbscript"> Dim DynArray() ReDim DynArray(5) ' Initial size = 5 DynArray(0) = 42 DynArray(4) = 26 MsgBox "Before: " & DynArray(4) ' prove that it worked ' Resize to 21 elements keeping the data we already stored ReDim Preserve DynArray(20) DynArray(15) = 73 MsgBox "After Preserve: " & DynArray(4) & " " & DynArray(15)' Old and new still there ' Resize to 51 items but lose all data Redim DynArray(50) MsgBox "Without Preserve: " & DynArray(4) & " Oops, Where did it go?" </script> لاحظ أن السلوك الافتراضي هو حذف كل محتويات المصفوفة عند تغيير حجمها، فإذا أردنا الحفاظ على محتوياتها؛ فيجب أن نخبر VBScript أن تحفظ المحتويات باستخدام Preserve. هذا الأسلوب غير مريح موازنةً بالقوائم التي تغير حجمها تلقائيًا، لكنه يعطي المبرمج تحكمًا أفضل في كيفية تصرف البرنامج، مما يحسن الأمان -ضمن أمور أخرى- نظرًا لأن بعض الفيروسات قد تستغل مخازن البيانات القابلة لتغيير حجمها ديناميكيًا. مصفوفات جافاسكربت رغم أنه يطلَق على هذا النوع من البيانات في جافاسكربت مصفوفة؛ إلا أنها تسمية خاطئة، فهو مزيج غريب يجمع بين مزايا القوائم والقواميس والمصفوفات العادية، حيث نستطيع مثلًا التصريح عن مصفوفة من عشرة عناصر من نوع ما كما يلي: var items = new Array(10); لاحظ استخدام كلمة new المفتاحية، إذ تشبه دالة CreateObject() التي استخدمناها في VBScript لإنشاء قاموس، كذلك فقد استخدمنا الأقواس لتحديد حجم المصفوفة، نستطيع الآن أن نملأ المصفوفة ونصل إليها كما يلي: items[4] = 42; items[7] = 21; var aValue = items[4]; وهنا نستخدم الأقواس المربعة للوصول إلى عناصر المصفوفة، وتبدأ الفهارس فيها من الصفر، لكن رغم هذا كله فمصفوفات جافاسكربت ليست مقيدة بتخزين نوع واحد من البيانات، بل يمكن إسناد أي شيء إلى عناصرها: items[9] = "A short string"; var msg = items[9]; كما يمكن إنشاء مصفوفات من خلال توفير قائمة بالعناصر: var moreItems = new Array("one","two","three",4,5,6); aValue = moreItems[3]; // -> 4 msg = moreItems[0]; // -> "one" كذلك يمكن تحديد طول المصفوفة باستخدام خاصية تدعى length، التي تستخدَم كما يلي: var size = items.length; وعلى غرار أسلوب بايثون في استدعاء الدوال في وحداتها، فإن الصياغة name.property هنا هي نفسها، لكن دون أقواس لاحقة. ورغم أن مصفوفات جافاسكربت تبدأ بالصفر كما ذكرنا؛ إلا أن فهارسها ليست مقصورةً على الأرقام وحدها، ولهذا يمكن استخدام السلاسل النصية أيضًا -والتي تكون هنا أشبه بالقواميس-، كما يمكن توسيع مصفوفة بإسناد قيمة إلى فهرس تكون أكثر من الحد الأقصى الحالي، مما يعني أننا لا نحتاج إلى تحديد الحجم عند إنشاء المصفوفة، رغم كون ذلك سلوكًا مستحسنًا في البرمجة، ويوضح المثال التالي خصائص مصفوفات جافاسكربت التي ذكرناها: <script type="text/javascript"> var items = new Array(10); var moreItems = new Array(1); items[42] = 7; moreItems["foo"] = 42; msg = moreItems["foo"]; document.write("msg = " + msg + " and items[42] = " + items[42] ); </script> إذا شغّلنا تلك الشيفرة في المتصفح فسنرى القيم مكتوبةً على الشاشة. تحقق إن كنت تستطيع متابعة الإسنادات في الشيفرة لتتأكد من سبب ظهور القيم التي تراها على الشاشة. لننظر أخيرًا في مثال دفتر العناوين مرةً أخرى، باستخدام مصفوفات جافاسكربت هذه المرة: <script type="text/javascript"> var addressBook = new Array(); addressBook["Samy"] = new Array("Samy", "9 Some St", "Anytown", "0123456789"); addressBook["Warda"] = new Array("Warda", "11 Nother St", "SomePlace", "0987654321"); document.write(addressBook.Warda); </script> يمكن الوصول إلى المفتاح كما لو كان خاصيةً مثل length، ويتبين مما سبق أن مصفوفات جافاسكربت ما هي إلا هياكل بيانات مرنة للغاية. المكدس Stack يكدِّس العاملون في المطاعم الأطباق النظيفة فوق بعضها، ثم تؤخَذ واحدًا تلو الآخرللعملاء بدءًا من الأعلى، فيكون الطبق الأخير هو آخر طبق يُستخدم، وفي نفس الوقت أقل طبق يُستخدم، يشبه هذا المثال مكدسات البيانات بالضبط، إذ نضع العنصر في قمة المكدس أو نأخذ عنصرًا منه، ويكون العنصر المأخوذ هو آخر عنصر مضاف إلى المكدس، ويطلَق على هذه الخاصية للمكدسات "آخرها دخولًا أولها خروجًا" أو LIFO - Last In First Out. من الخصائص المفيدة في المكدسات أننا نستطيع عكس قائمة العناصر عن طريق دفع القائمة في المكدس ثم إخراجها مرةً أخرى، فتكون النتيجة عكس القائمة الأولى. لا تأتي المكدسات مدمجةً في بايثون ولا VBScript ولا جافاسكربت، بل يجب كتابة بعض التعليمات البرمجية في البرنامج من أجل الحصول على سلوك المكدس، والقوائم هي أفضل نقطة بداية في الغالب للمكدسات بما أنه يمكن زيادة حجمها عند الحاجة. تدريب اكتب مكدسًا باستخدام قائمة في بايثون، وتذكر أننا نلحق العناصر في نهاية القائمة باستخدام append()، ونحذفها باستخدام فهارسها عن طريق del()، كما تستطيع استخدام -1 مثل فهرس يشير إلى آخر عنصر في القائمة. يجب أن تكون قادرًا على كتابة برنامج -مستفيدًا من هذه الإرشادات- يدفع 4 محارف إلى قائمة ثم يخرجها مرةً أخرى، ثم اطبعها بعد ذلك. راقب الترتيب الذي تستدعي به print و del، وإذا نجحت في هذا، فجرب طباعتها بعكس الترتيب الذي أدخلتها به، استخدم العملية pop() الخاصة بقوائم بايثون والتي تعيد العنصر الأخير وتحذفه في خطوة واحدة، من أجل تسهيل التدريب. الحقيبة Bag في سياق الحديث عن البيانات؛ تُعَد الحقيبة تجميعةً من العناصر التي ليس لها ترتيب محدد، ويمكن أن تحتوي على عناصر مكررة، وتكون لها عادةً عوامل تمكننا من إضافة تلك العناصر وحذفها وإيجادها، فما هي إلا قوائم في اللغات الثلاث التي نستخدمها في الكتاب. المجموعات Sets تخزن المجموعة نوعًا واحدًا من العناصر، ونستطيع التحقق مما إذا كان العنصر موجودًا في المجموعة أم لا -أي عضوًا فيها-، إلى جانب إضافة تلك العناصر وحذفها، بالإضافة إلى دمج مجموعتين معًا بطرق شتى تتوافق مع نظرية المجموعات في الرياضيات، مثل الاتحاد والتقاطع وغيرها، لكن على هذا فالمجموعات لا تكون مرتبةً ولا تهتم للترتيب. لا تستخدم جافاسكربت ولا VBScript المجموعات مباشرةً، لكن يمكن تنفيذ سلوك قريب باستخدام القواميس التي لا تحتوي إلا على مفاتيح بقيم فارغة؛ أما بايثون فتدعم المجموعات مثل نوع من أنواع البيانات فيها، وأحد الاستخدامات البسيطة لها ما يلي: >>> A = set() # أنشئ مجموعة فارغة >>> B = set([1,2,3]) # مجموعة من قائمة >>> C = {3,4,5} # تهيئة مثل [] في القوائم >>> D = {6,7,8} >>> # جرب الآن بعض عمليات القوائم >>> print( B.union(C) ) {1, 2, 3, 4, 5} >>> print( B.intersection(C) ) {3} >>> print( B.issuperset({2}) ) True >>> print( {3}.issubset(C) ) True >>> print( C.intersection(D) == A ) True لاحظ أننا نستطيع تهيئة المجموعة باستخدام الأقواس المعقوصة، لكن هذا قد يتشابه مع القواميس، لذا نفضل استخدام الصيغة set([...]]) رغم أنها تتطلب كتابةً أكثر، فيما يلي اختصارات لعمليات الاتحاد والتقاطع: >>> print( B & C ) # B.intersection(C) كما في >>> print( B | C ) # B.union(C) كما في نستطيع أخيرًا التحقق من وجود عنصر ما في مجموعة باستخدام العامل in: >>> print( 2 in B ) True هذه العمليات كافية إلى الآن لما نشرحه، رغم وجود عمليات أخرى على المجموعات. الطابور Queue يشبه الطابور أو قائمة الانتظار؛ المكدس، باستثناء أن العنصر الأول فيه الذي يخرج أولًا أيضًا، ويسمى هذا السلوك "الأول دخولًا الأول خروجًا" أو FIFO - First In First Out، ويُنشَأ الطابور باستخدام قائمة أو مصفوفة. تدريب جرب كتابة طابور باستخدام قائمة، وتذكر أنك تستطيع الإضافة إلى القائمة باستخدام append()، وأن تحذف من موضع ما باستخدام del(). أضف أربعة محارف إلى الطابور ثم استخرجها واطبعها، غذ يجب أن تُطبع المحارف بنفس الترتيب الذي دخلت به. توجد أنواع أخرى من تجميعات البيانات، لكن ما ذكرناه هنا يغطي أغلب الحالات التي ستراها أثناء البرمجة، بل لن نستخدم إلا قليلًا مما ذكرناه هنا في الشرح، لكن قد ترى بقية الأنواع في المقالات أو مجموعات النقاشات البرمجية. الملفات Files ينبغي أن يكون مستخدم الحاسوب معتادًا على التعامل مع الملفات بما أنها تمثل أساس كل شيء نفعله على الحواسيب، ولا غرو إذًا أن نكتشف أن أغلب لغات البرمجة توفر نوع file خاص من البيانات، لكن أهمية الملفات ومعالجتها ستجعلنا نرجئ الحديث عنها وشرحها إلى فصل لاحق خاص بها. وبهذا تكون قد تعرفت على أبرز البيانات البرمجية التي قد تحتاج إليها. ترجمة -بتصرف- للفصل الخامس: The Raw Materials من كتاب Learning To Program لصاحبه Alan Gauld. اقرأ أيضًا المقال التالي: بعض التسلسلات النصية المهمة لتعلم البرمجة المقال السابق: مدخل إلى البيانات وأنواعها: أنواع البيانات الأساسية مدخل إلى البيانات الوصفية microdata في HTML5 هياكل البيانات: الكائنات والمصفوفات في جافاسكريبت
-
- 1
-
- collections
- التجميعات
-
(و 1 أكثر)
موسوم في:
-
تُعَد البيانات وجافاسكربت للواجهة الأمامية من الناحية العملية أحداثًا متنافية، إذ يرى سوق العمل أن وظيفة الواجهة الخلفية معالجة البيانات وتجميعها، أما وظيفة جافاسكربت فهي عرض البيانات المُجمّعة مسبقًا، كما يُنظر إلى عرض النطاق الترددي وزمن المعالجة على أنهما عقبتان رئيسيتان لدى التعامل مع البيانات في الواجهة الأمامية وهذا صحيح إلى حدٍ كبيرٍ، ولكن توجد حالاتٌ من المجدي فيها معالجة البيانات ضمن المتصفح. إذًا فكيف يمكن النجاح بمعالجة البيانات في هذه الحالات؟ التفكير في البيانات يتطلب العمل على البيانات باستخدام جافاسكربت امتلاكًا كاملًا للبيانات وفهمًا للأدوات المتوفرة دون اتصالٍ غير ضروريٍ بالخادم، حيث من المفيد التمييز بين البيانات التابعة لثلاثة أطرافٍ والبيانات المُلّخصة. تتألف البيانات ثلاثية الأطراف من بيانات معاملاتٍ أولية، وهي بيانات منخفضة المستوى يصعب تحليلها لوحدها؛ وتوجد من ناحيةٍ أخرى البيانات المُلخصة وهي البيانات الممكن عرضها بطريقةٍ مفيدةٍ ومنطقية، وتُدعى أيضًا بالبيانات المكوّنة. يهتم المطورون بهيكلية البيانات الكامنة بين تفاصيل المعاملات والبيانات المكوّنة، وهذه هي النقطة التي سنُركز عليها، فهذه البيانات مُجمّعة ولكنها تتضمن أكثر مما نحتاج للعرض النهائي، كما تمتلك بُعدين أو أكثر (متعددة الأبعاد)، وهذا يوفّر مرونةً في طريقة عرضها، فهي تسمح للمستخدمين النهائيين بتشكيل البيانات واستخراج المعلومات لمزيدٍ من التحليل، وبالرغم من أنها صغيرة وذات أداء جيدٍ، إلا أنها توفّر تفاصيلًا تسمح لنا وللمؤلف بالاطلاع على معلوماتٍ لم نكن نتوقعها. يجب أن لا يكون الهدف هو الوصول بالبيانات إلى هيئةٍ مثالية، وذلك لتجنب أي تلاعبٍ بها في الواجهة الأمامية، بل يجب تقليص البيانات إلى مجموعة بياناتٍ متعددة الأبعاد، كما يجب استخدام أبعادٍ متعددة يهتم بها العميل المُستهدف مثل الأشخاص والمنتجات والأماكن والوقت، وقياساتٍ متعددة مثل المجموع والتعداد والمتوسط وأصغر قيمة وأعظم قيمة، وبالتالي فعلينا في النهاية عرض هذه البيانات على صفحةٍ مع عناصر نموذج تسمح بتقسيم البيانات لتحليلها بصورةٍ أعمق. يُعَد إنشاء مجموعات البيانات عمليةً دقيقة جدًا، حيث لا بد من امتلاك بياناتٍ كافية ليكون تحليلنا ذو قيمة وبدون إجهاد جهاز العميل سواءً كان حاسوبًا أو هاتفًا ذكيًا أو غير ذلك، وهذا يعني الوصول لمتطلباتٍ واضحة وموجزة، وسوف نحتاج لاستخدام أبعاد متنوعة وقياسات مختلفة، وهذا يعتمد على مدى اتساع مجموعة البيانات التي نتعامل معها. سنذكر فيما يلي بعض الأمور الواجب أخذها بالحسبان: هل غالبية المحتوى هو محتوى مميز أو سوف يُستخدم مرارًا؟ حيث نعتمد على قاعدة 80/20 التي تنص على أن 80% من المستخدمين عامةً يحتاجون إلى 20% مما هو متوفرٌ. هل كل الأبعاد منتهية؟ حيث يجب أن تمتلك الأبعاد مجموعةً من القيم المُحددة مسبقًا، فمثلًا مخزون منتج ما يزداد باستمرار سوف يتضخم بسرعة، لذلك فإن العمل على تصنيفات المنتج ربما سيكون أفضل. العمل على تجميع البيانات ما أمكن وبالأخص التواريخ، وإن كنت تستطيع الاكتفاء بعملية التجميع باستخدام الأعوام فافعل ذلك، وإن كنت بحاجة للتجميع بالاعتماد على الشهر فلا بأس بذلك، ولكن تجنب المجالات الأصغر من ذلك. اعتماد مبدأ "القلة أفضل"، فكلما كان عدد القيم ضمن البعد أقل، كان هذا أفضل من ناحية الأداء. لنأخذ مجموعة بيانات مؤلفةً من 200 صفٍ على سبيل المثال، فإذا أضفنا بعدًا آخر بأربع قيمٍ ممكنة، فسوف تتضخم مجموعة البيانات هذه إلى 200x4 = 800 صفٍ كحدٍ أعظمي؛ أما إذا أضفت بعدًا بخمسين قيمة، فعندها سوف تتضخم مجموعة البيانات إلى 200x50 = 10000 صفًا، وهذا سوف يتضاعف مع كل بعد نُضيفه. تجنُب القياسات المُلخصة في مجموعات البيانات متعددة الأبعاد لأنها تحتاج إعادة الحساب في كل مرةٍ تتغير فيها مجموعة البيانات، إذ يجب عليك مثلًا تضمين المجموع الكلي والعدد الكلي وحساب المتوسطات ديناميكيًا إذا كنت تخطط لعرض المتوسط، ونتمكن بهذه الطريقة من إعادة حساب المتوسطات باستخدام القيم المُلخصة عند تلخيص البيانات. تأكد من فهمك الجيد للبيانات التي تعمل عليها قبل البدء بتنفيذ أيٍ من المذكور سابقًا، فمن المحتمل أن تفترض افتراضاتٍ خاطئةٍ تقود إلى قراراتٍ مبنيةٍ على معلوماتٍ خاطئة، لذلك فإن جودة البيانات هي أولويةٌ هامة وهذا ينطبق على البيانات التي تطلبها أو تُنشئها. لا تأخذ أي مجموعة بيانات وتبني افتراضاتٍ عن بعد أو قياسٍ فيها، ولا تتردد بالسؤال دائمًا عن فهرس البيانات أو أي توثيقٍ خاصٍ بها يُمكن أن يساعد على فهم ما تتعامل معه، حيث لا يعتمد تحليل البيانات على الحدس أو التنبؤ، فربما طُبقت قواعد خاصة بعملٍ تجاريٍ أو حُذفت بيانات في خطوةٍ سابقة، وبالتالي فإن كنت لا تملك هذه المعلومات فسينتهي بك الأمر بإنتاج مجموعات بياناتٍ وتصوراتٍ لا معنى لها أو مضللة بأسوء الأحوال. سوف يساعد المثال التالي على فهم ما سبق. حالة الاستخدام المدروسة سوف نستخدم مجموعة بيانات BuzzFeed المختصة بتحليل البيانات المتعلقة بالأماكن التي يأتي منها اللاجئون إلى الولايات المتحدة والأماكن التي يذهبون إليها، حيث سنبني تطبيقًا صغيرًا يعرض لنا عدد اللاجئين الواصلين لولايةٍ محددة في عامٍ محدد، وسنعرض على وجه الخصوص واحدةً مما يلي بناءً على طلب المستخدم: العدد الكلي للواصلين إلى ولايةٍ معينة في عامٍ معينٍ. العدد الكلي للواصلين خلال كل الأعوام لولايةٍ معينة. العدد الكلي للواصلين لكل الولايات في عام معيّن. سوف تكون واجهة المستخدم عبارة عن نموذجٍ بسيطٍ لاختيار الولاية والعام، وسوف تعمل الشيفرة على: إرسال طلبٍ بالبيانات. تحويل النتيجة إلى JSON. معالجة البيانات. تسجيل أي خطأ في الطرفية. عرض النتائج للمستخدم. لن نمرّر مجموعات بياناتٍ ضخمة الحجم للمتصفح لسببين هما عرض الحزمة، وقدرات وحدة المعالجة المركزية CPU، لذلك ستُجمع البيانات في الخادم باستخدام Node.js. بيانات المصدر [{"year":2005,"origin":"Afghanistan","dest_state":"Alabama","dest_city":"Mobile","arrivals":0}, {"year":2006,"origin":"Afghanistan","dest_state":"Alabama","dest_city":"Mobile","arrivals":0}, ... ] البيانات متعددة الأبعاد: [{"year": 2005, "state": "Alabama","total": 1386}, {"year": 2005, "state": "Alaska", "total": 989}, ... ] كيفية ضبط هيكلية البيانات في المكان الصحيح واجهة AJAX وFetch API توجد عدة طرق في جافاسكربت من أجل جلب البيانات من مصدرٍ خارجيٍ، وكان علينا قديمًا استخدام طلب XHR لذلك، لأنه مدعوم على نطاقٍ واسعٍ ولكنه معقد ويتطلب استخدام عدة طرقٍ مختلفة، كما توجد مكتبات تُساعد على تخفيض التعقيد، مثل Axios أو jQuery's Ajax API، كما أنها توفّر دعمًا عبر المتصفحات، ولهذا فهي خيارٌ متاحٌ إن كنت تستخدم أحدها، لكن لا بد من اختيار الحلول الأصيلة ما أمكن. توجد أيضًا طريقة Fetch API وهي الأحدث، ولذلك فهي مدعومةٌ على نطاقٍ أضيق من سابقاتها، ولكنها أسهل وقابلة للتسلسل، وستُحوّل الشيفرة البرمجية إلى مكافئٍ مدعومٍ على نطاقٍ أوسع إن كنت تستخدم ناقلًا مثل Babel. ستُستخدم واجهة Fetch API في الحالة المدروسة لجلب البيانات للتطبيق. window.fetchData = window.fetchData || {}; fetch('./data/aggregate.json') .then(response => { // when the fetch executes we will convert the response // to json format and pass it to .then() return response.json(); }).then(jsonData => { // take the resulting dataset and assign to a global object window.fetchData.jsonData = jsonData; }).catch(err => { console.log("Fetch process failed", err); }); الشيفرة البرمجية السابقة هي جزءٌ من ملف main.js الموجود في مستودع GitHub، حيث ترسل الطريقة fetch() طلبًا بالبيانات ثم نحوّل النتائج إلى JSON، وستُستخدم الطريقة then() من أجل ضمان عدم تنفيذ التعليمة التالية إلا بعد جلب كامل مجموعة البيانات، وستُنفذُ جميع عمليات معالجة البيانات ضمن هذه الكتلة، كما ستُسجل الأخطاء باستخدام console.log(). الهدف هنا هو تحديد الأبعاد الأساسية المطلوبة لعمل تقريرٍ بالعام والولاية قبل تجميع عدد الواصلين المرتبطين بهذه الأبعاد، وإزالة الدولة الأم والمدينة المتوجهين لها، ويمكنك الاطلاع على النص البرمجي Node.js في الملف preprocess/index.js/ في مستودع GitHub لفهم كيفية إنجاز العمليات السابقة بصورةٍ أكبر، حيث يُنشئ هذا النص البرمجي ملف aggregate.json الذي يجلبه التابع fetch(). البيانات متعددة الأبعاد الهدف من الصيغة متعددة الأبعاد هو تحقيق المرونة بحيث تكون البيانات مُفصلةً جيدًا، حتى لا يُضطر المستخدم لطلب استعلام للخادم في كل مرةٍ يحتاج فيها إجابةً عن سؤالٍ مختلف، وتكون مُلخصةً بنفس الوقت بحيث لا يُضطر التطبيق للبحث في كامل مجموعة البيانات مع كل جزءٍ جديدٍ من البيانات. يجب علينا توقع الأسئلة وتقديم بيانات تُجيب عن هذه الأسئلة لأن المستخدم يحتاج إلى أن يكون قادرًا على إجراء بعض التحليلات دون أن يشعر بأنه مُقيدٌ أو مغمورٌ بكمٍ هائلٍ من المعلومات. ستُستخدم بيانات JSON كما هو الحال مع معظم واجهات برمجة التطبيقات APIs، فهي معيارٌ مُستخدمٌ لإرسال البيانات للتطبيقات على أنها كائناتٌ تتألف من أزواج (اسم وقيمة). ألقِ نظرةً على العينة التالية من مجموعة بياناتٍ متعددة الأبعاد قبل العودة إلى الحالة المدروسة: const ds = [{ "year": 2005, "state": "Alabama", "total": 1386, "priorYear": 1201 }, { "year": 2005, "state": "Alaska", "total": 811, "priorYear": 1541 }, { "year": 2006, "state": "Alabama", "total": 989, "priorYear": 1386 }]; تستطيع استخدام جافاسكربت على نحوٍ أكبر لتحليل مجموعة البيانات عندما تكون مُجمّعةً بطريقة صحيحة. لنُلقِ نظرةً على بعض من توابع مصفوفات جافاسكربت المُستخدمة لتكوين البيانات. كيفية العمل بفعالية مع البيانات من خلال جافاسكربت ترشيح المصفوفة تأخذ طريقة filter() الخاصة بالنموذج الأولي للمصفوفة Array.prototype.filter()، وظيفة اختبار كل عنصرٍ ضمن المصفوفة، وتُعيد مصفوفةً أخرى تحتوي على القيم التي تجاوزت الاختبار، وهذا يسمح بإنشاء مجموعة بياناتٍ فرعيةٍ ذات معنى عند استخدام القائمة المنسدلة أو مرشحات النص، كما سيتمكن المستخدم من الاطلاع على المعلومات من خلال عرض أقسامٍ من البيانات، وهذا صحيحٌ أيضُا عند استخدام أبعادٍ ذات معنى ومنفصلة لمجموعة البيانات متعددة الأبعاد. ds.filter(d => d.state === "Alabama"); // Result [{ state: "Alabama", total: 1386, year: 2005, priorYear: 1201 },{ state: "Alabama", total: 989, year: 2006, priorYear: 1386 }] ربط بيانات المصفوفة تأخذ طريقة map() الخاصة بالنموذج الأولي للمصفوفة Array.prototype.map() وظيفة تمرير كل عنصرٍ في المصفوفة وإعادة مصفوفةً جديدةً بنفس عدد العناصر القديم، حيث يسمح ربط البيانات بإنشاء مجموعات بياناتٍ مرتبطة، وأحد استخدامات هذه الطريقة هو ربط بياناتٍ غامضةٍ مع بياناتٍ ذات معنى وموصوفة، أو استخدامها لأخذ قياسات وإجراء حسابات على عناصر المصفوفة لتحقيق تحليلٍ أكثر عمقًا. 1. ربط البيانات ببيانات ذات معنى ds.map(d => (d.state.indexOf("Alaska")) ? "Contiguous US" : "Continental US"); // Result [ "Contiguous US", "Continental US", "Contiguous US" ] 2. ربط البيانات مع النتائج المحسوبة ds.map(d => Math.round(((d.priorYear - d.total) / d.total) * 100)); // Result [-13, 56, 40] تلخيص بيانات المصفوفة تأخذ طريقة reduce()الخاصة بالنموذج الأولي للمصفوفة Array.prototype.reduce() وظيفة معالجة كل عنصرٍ ضمن المصفوفة وإعادة بياناتٍ مُجمعةٍ، وتُستخدم لإنجاز حساباتٍ رياضيةٍ مثل إضافة أو ضرب كل رقمٍ ضمن المصفوفة، ويُمكن استخدامها أيضًا لضم المحارف والعديد من الأمور الأخرى، ولا بد من تعلُّم هذه الدالة من خلال مثالٍ نظرًا لصعوبتها. عليك التأكد من تلخيص البيانات عند عرضها، بحيث تُقدم فكرةً مُلخصةً للمستخدمين، ولذلك يُمكن تجميع البيانات اعتمادًا على ما يحتاجه المُستخدم حتى عند تطبيق عمليات تلخيصٍ عامة على البيانات من جهة الخادم، وبالنسبة للتطبيق الذي ننشئه، فعليّنا إضافة المجموع الكلي لكل مُدخلٍ وعرض النتيجة المُجمّعة باستخدام طريقة reduce() للمرور على كل سجلٍ وإضافة القيمة الحالية إلى المجمّع، وبهذا تكون النتيجة النهائية هي المجموع الكلي لقيم كل مصفوفة. ds.reduce((accumulator, currentValue) => accumulator + currentValue.total, 0); // Result 3364 تطبيق هذه الدوال على الحالة المدروسة حالما نحصل على البيانات سنعيّن حدثّا للزر "Get the Data" من أجل عرض المجموعة الفرعية من البيانات المناسبة، حيث توجد عدة مئاتٍ من العناصر ضمن بيانات JSON الخاصة بالتطبيق، وتتواجد الشيفرة المسؤولة عن دمج البيانات مع الزر في الملف main.js الخاص بنا: document.getElementById("submitBtn").onclick = function(e){ e.preventDefault(); let state = document.getElementById("stateInput").value || "All" let year = document.getElementById("yearInput").value || "All" let subset = window.fetchData.filterData(year, state); if (subset.length == 0 ) subset.push({'state': 'N/A', 'year': 'N/A', 'total': 'N/A'}) document.getElementById("output").innerHTML = `<table class="table"> <thead> <tr> <th scope="col">State</th> <th scope="col">Year</th> <th scope="col">Arrivals</th> </tr> </thead> <tbody> <tr> <td>${subset[0].state}</td> <td>${subset[0].year}</td> <td>${subset[0].total}</td> </tr> </tbody> </table>` } سوف يأخذ حقل الولاية أو العام القيمة الافتراضية "All" عندما يكون فارغًا. ويمكن الاطلاع على الشيفرة البرمجية التالية المُتاحة في الملف js/main.js/ وإلقاءِ نظرةٍ على الدالّة ()filterData التي نسجّل ضمنها حصة الأسد من عمليتي الترشيح والتجميع. // with our data returned from our fetch call, we are going to // filter the data on the values entered in the text boxes fetchData.filterData = function(yr, state) { // if "All" is entered for the year, we will filter on state // and reduce the years to get a total of all years if (yr === "All") { let total = this.jsonData.filter( // return all the data where state // is equal to the input box dState => (dState.state === state) .reduce((accumulator, currentValue) => { // aggregate the totals for every row that has // the matched value return accumulator + currentValue.total; }, 0); return [{'year': 'All', 'state': state, 'total': total}]; } ... // if a specific year and state are supplied, simply // return the filtered subset for year and state based // on the supplied values by chaining the two function // calls together let subset = this.jsonData.filter(dYr => dYr.year === yr) .filter(dSt => dSt.state === state); return subset; }; // code that displays the data in the HTML table follows this. See main.js. كما ذُكر سابقًا فإن القيمة الافتراضية لحقل الولاية أو العام هي "All" في حال بقي فارغًا، حيث تُرشح مجموعة البيانات وتُلخص قياسات جميع الأسطر حسب هذا البعد، كما ستُرشح قيمة كلٍ من العام والولاية عند إدخالهما. لدينا الآن مثال مجرب، حيث: بدأنا بمجموعة بياناتٍ أوليةٍ ذات معاملات. ثم أنشأنا مجموعة بياناتٍ متعددة الأبعاد وشبه مُجمعة. وبنينا نتيجةً كاملةً التكوين ديناميكيًا. يُمكن التلاعب بالبيانات حالما تصل للمستخدم بعدة طرقٍ دون الحاجة للاتصال المتكرر بالخادم، وهذا مفيد جدًا في حال فقد المستخدم الاتصال، لأنه بذلك لن يفقد القدرة على التفاعل مع البيانات، وهذه ميزةٌ رائعةٌ خصوصًا عند بناء تطبيق ويب تقدُّمي Progressive Web App أو اختصارًا PWA، يحتاج أن يعمل دون اتصالٍ بالانترنت. يمكن إنشاء أي تحليلٍ لأي مجموعة بيانات حالما تُحكم قبضتك على هذه الطرق الثلاث، لذلك اربط أحد الأبعاد ضمن مجموعة البيانات مع تصنيفٍ أكثر شمولية، ولخّص البيانات باستخدام reduce. تستطيع أيضًا استخدام مكتبة D3 من أجل ربط البيانات مع جداول ورسوم بيانية تسمح بتصوُّر مرئي مخصص بالكامل لهذه البيانات. الخلاصة يُطلعنا هذا المقال على الإمكانيات الكامنة في جافاسكربت عند التعامل مع البيانات، وكما ذُكر سابقًا فإن جافاسكربت للواجهة الأمامية ليست بديلًا لتفسير وتحويل البيانات على الخادم حيث تُنجز المهمات الصعبة، كما يجب بنفس الوقت عدم استبعادها عند معالجة مجموعات البيانات بالطريقة الصحيحة. ترجمة -وبتصرّف- للمقال Taming Data with JavaScript لصاحبه Brian Greig. اقرأ أيضًا هياكل البيانات: الكائنات والمصفوفات في جافاسكريبت ماهي جافاسكريبت؟ أمثلة على الاستخدامات برمجة الكائنات Objects في جافاسكريبت
-
الآن وقد تعلّمنا كيف نُجرِي أبحاث المُستخدمين، ونحدّد أهدافنا، ونضع هندسة المعلومات، ونلفت انتباه المُستخدمين، ونضع الرّسوم التّخطيطيّة، ونفهم عقليّة المُستخدم، حان وقت إطلاق الخدمة! وهذا يعني أنّنا سنحتاج إلى إجراء بعض القياسات. فهرس سلسلة مدخل إلى تجربة المستخدم: مدخل إلى تجربة المستخدم User Experience فهم ودراسة المستخدمين في مجال تجربة المستخدم دراسة الشريحة المستهدفة في مجال تجربة المستخدم كيفية التصميم للأجهزة المختلفة هندسة المعلومات في تجربة المستخدم تعرف على أنماط التصميم في مجال تجربة المستخدم أشياء لا يمكن اعتبارها رسوما تخطيطية (Wireframes) في مجال تجربة المستخدم تعرف على الرسوم التخطيطية (Wireframes) في مجال تجربة المستخدم مفهوم الثقل المرئي (Visual Weight) والألوان في مجال تجربة المستخدم التكرار ومخالفة الأنماط في مجال تجربة المستخدم المحاذاة والقرب في مجال تجربة المستخدم تعرف على أساليب مسح الواجهة والتراتب المرئي في مجال تجربة المستخدم أساليب الإطلاع في مجال تجربة المستخدم: التصفح، البحث والاكتشاف تصميم هيكل صفحة الويب والعناصر الأساسية في مجال تجربة المستخدم الأزرار، النماذج والدعوات إلى الإجراء في مجال تجربة المستخدم استخدام علم النفس في مجال تجربة المستخدم لتكييف المستخدم وإقناعه كيف تغير الخبرة من تجربة المستخدم؟ تصميم تجربة المستخدم من خلال بيانات وإحصائيات المستخدمين (هذا الدرس) تعرف على أنواع المخططات الإحصائية في مجال تجربة المستخدم اختبارات أ/ب (A/B Test) في مجال تجربة المستخدم البيانات موضوعية تحدّثنا في الدّروس الأولى من السّلسلة عن أبحاث المُستخدمين. البيانات مختلفة عن الأبحاث، فهي تقيس سلوك المُستخدم، أي ما يفعله، وكم مرّة، وكم دام هذا الفعل، وهكذا... تُجمع البيانات بواسطة حاسوب، فهي حياديّة لا تؤثّر في المُستخدم، وهامش الخطأ فيها صغير لأنّها ذات مقاييس مُعرَّفة بدقّة؛ ويمكن أن نقيس بها سلوك ملايين النّاس دون عناء، ويمكن أن تخبرنا بمتصفّح المُستخدم وبلده. البيانات لا تكذب، فهي علمٌ. لكنّها أيضًا لا تخبر شيئًا عن السياق، لذا كُن حذرًا. للأسف يقع عاتق تفسير هذه البيانات علينا نحن المُصمّمين، وهنا تقع الأخطاء. البيانات قائمة على الناس سيُغريك قول أنّ البيانات "ليست إلّا أرقامًا مُجرَّدة"، وهذا يعني أنّك ستفسرها كما تشاء. تذكّر أنّ هذه الأرقام تُمثّل أفعال أناسٍ حقيقيّن ذوي حياة مُعقّدة. لا تختزل ملايين النّاس في عدد مُفرد وتتوقّع أن تعتمد عليها في كلّ موقف. قد يغريك أيضًا أن تبحث عن أرقام "تُثبت" رأيك، لا تفعل ذلك! ولا تسمح لأحد أن يطلب منك ذلك! كلما زادت البيانات كانت أفضل إن قست نقرات 5 أشخاص، فلن تكون بياناتك تمثيليّة بما يكفي، أمّا إن قست بيانات 5 ملايين زائر، فلا بدّ أنّها تغطّي شرائح كبيرة من الزّوار. كلّما زادت أهمّيّة القرار الّذي تريد بناءه على أساس البيانات، احتجت إلى بيانات أكبر قبل اتّخاذه. طرق جمع بيانات موضوعية تتوفّر طرق كثير لجمع بيانات موضوعيّة: التحليلات (Analytics) توفّرها Google وغيرها بأسعار رخيصة أو مجّانًا، وتسمح بمتابعة ما يفعله المُستخدمون دون كشف هويّتهم. كلّما نقر المُستخدم زرًّا أو انتقل إلى صفحة جديدة، سيظهر لديك ذلك، ويمكنك تصميم قياسات خاصّة بك، ولا حدود لإمكانيّاتها! اختبارات أ/ب صمّم إصدارين من عنصر ما وأطلقهما معًا، ستعلم أيّ الاثنين أفضل، لأنّك تُجرّبهما على أناس حقيقيّن في فترة مناسبة، سيُخبرك البرنامج متى توقف الاختبار، لأنّه زيادة عدد الخاضعين للاختبار بعد حدّ معين أمرٌ غير مُجدٍ. متابعة العين يُجرى هذا الاختبار في مُختبر خاصّ، ولكن لا يستطيع المُستخدم التّحكّم به، لذا يُعتبر موضوعيًّا، وتُستخدم فيه برامج وأدوات خاصّة لقياس موضع نظر المُستخدمين أثناء استخدامهم لتصميم، لتستفيد من ذلك في معرفة سلامة الأساليب الّتي استخدمتها للفت نظرهم. ClickTale هذا مثال عن استخدام الخرائط الحراريّة لمتابعة النّقرات وتمرير الصّفحات والانتقال بينها، ولكنّ هناك أمثلة أخرى. يسمح برنامج ClickTale بجمع بيانات استخدام الواجهة للمُستخدمين الحقيقيّين دون كشف هوّيتهم، وبطريقة مخفيّة، ويسمح لك بمشاهدة مواضع نقراتهم وتحرّك مؤشّر الفأرة ومدى تمرير الصّفحة، وأيّ الصّفحات شاهدوها، وهو غايةٌ في الفائدة. سجلات البحث لا يُدرك كثيرٌ من النّاس أن حقل البحث في موقعك يمكنه أن يحتفظ بكلّ كلمة تُكتب فيها، فلو كان المُستخدمون يبحثون عن شيءٍ ما، فهذا يعني أنّهم لا يجدونه، وعندها تكون سجلّات البحث قيّمة جدًّا لتحسين هندسة معلوماتك وتخطيط الواجهة. التصميم اعتمادا على البيانات المجموعة إن كنت تُخطّط لتحليل استخدام موقعك، فلن يطول الأمر قبل أن تحتاج إلى فهم ما تعنيه إحصاءات Google، وتُجرى دراسة هذه الإحصاءات بأسلوب مُختلف عن خبراء التّسويق. هناك 7 إحصاءات تحتاج لفهمها قبل فعل أيّ شيء، لا أقصد فهم ما تقيسه فقط، بل ما تعنيه أيضًا. لا يمكن أن نقول عن الأرقام أنّها "جيّدة" أو "سيّئة"، فالأمر نسبيّ، ومليون مستخدم لا تعتبر جيّدة إلّا إن كانت أعلى من الشّهر الماضي، لو كنت فيسبوك وكان زوّارك هذا الشّهر مليونًا، فأنت في ورطة. قبل تحليل أي رقم، فكّر بما يجب أن يفعله موقعك، وافهم ما يجب أن تُشير إليه الأرقام عن سلوك المُستخدمين. الجلسات (الزيارات الكلية) والمستخدمون (الزيارات الفريدة) عبارة الزّيارات الكلّيّة تعني مجموع عدد زيارات الموقع (يا للمفاجأة!) أمّا الزّيارات الفريدة (Unique Visits) فهي مختلفة، فلو زرتُ أنا موقعك 50 مرّة، لاحتُسبتُ مُستخدمًا فريدًا واحدًا (unique visitor)، وللدّقّة، فإنّها من النّاحية التّقنيّة تقيس الأجهزة الفريدة وليس النّاس. معنى كلّ منهما: مقارنة هذين الرّقمين سيُقودك إلى استنتاج بعض الحقائق عن الزّيارات: جودة عالية: الكلّيّة أكبر بكثير من الفريدة. كميّة عالية: الكلّيّة مساوية تقريبًا للفريدة، والفريدة أكبر من الشّهر الماضي. كلاهما: الفريدة أعلى من الشّهر الماضي والكلّيّة أعلى بكثير من الفريدة. ليس أيّ منهما: الفريدة أقلّ من الشّهر الماضي والكُلّيّة مساويةً تقريبًا للفريدة. الزّيارات الفريدة تمثيل أكثر صدقًا للزّيارات، ولكنّني أفضّل أن يزور موقعي 1000 شخص كلّ يوم على أن يزوره 10 آلاف شخص مرّةً في الشّهر، ولكن مع ذلك، إن زار شخصٌ واحد موقعي مليون مرّة، فلن يكون هذا مُفيدًا، وربّما يُعاني هذا الشّخص من مشكلة ما! عدد مرات مشاهدة الصفحات (Pageviews) ما تقيسه: تزداد بمقدار 1 في كلّ مرّة يحمّل فيها أيّ زائر أيّ صفحة. ما تعنيه: يمكنك اعتبارها "مؤشِّرًا عامًّا" على الزّيارات، لأنّها تصف المقدار الكليّ للمحتوى المُشاهدة وتتجاهل معظم العوامل الأخرى. لو كان موقعك يعتمد على الإعلانات في أعلى الصّفحات فهذا رقم مهمّ. لو كان موقعك مُعتمدًا على المحتوى، كالأخبار، فقد تكون زيادة هذا الرّقم أكثر أهمّية. معدل عدد الصفحات في كل جلسة (Pages-per-Visit) ما يقيسه: متوسّط عدد الصّفحات الّتي يُشاهدها كلّ زائر، في كلّ زيارة، يمكن اعتبارها عدد "النقرات" في كلّ زيارة (ولكن هذا غير دقيق من النّاحية التّقنيّة). ما يعنيه: إن كان موقعك مُركّزًا على المّهامّ أو التّفاعلات الاجتماعيّة، فقد يكون هذا الرّقم أهمّ من مرّات مشاهدة الصّفحات (pageviews). خلافًا لذلك، إن كنت مُحرَّك البحث Google، فسترغب في تخفيض هذا الرّقم قدر الإمكان، لأنّ نتائج البحث الأكثر جودةً يجب أن تكون في الصّفحة الأولى. متوسط مدة الجلسة (Time-per-Visit) ما يقيسه: مدّة كلّ زيارة المتوسّط، قد تكون مقارنته مع الصّفحات/الجلسة مهمّة جدًا. ما يعنيه: في عالم مثالي، من المُفترض أن يقرأ الزّائر المقال بكامله في الموقع المُعتمد على المُحتوى، وأن يقرأ مقالات كثيرة، وهذا يعني رقمين كبيرين لكلّ من "متوسّط مدّة الجلسة" و"الصّفحات/الجلسة". لو كان الرّقم "الصّفحات/الجلسة" كبيرًا والآخر ("متوسّط مدّة الجلسة") صغيرًا، فقد يعني هذا أنّ الزّوّار يبحثون عن شيء ما ولا يجدون (وهذا سيّئ) أو أنّهم يُنجزون مهمّاتهم بسرعة شديدة (وهذا جيّد)، فالأمر نسبيّ كما ترى. لو كان "متوسّط مدّة الجلسة" كبيرًا والآخر ("متوسط عدد الصّفحات في كل جلسة") صغيرًا، فقد تكون عناصر التّنقّل في الموقع غير فعّالة (وهذا سيّئ)، أو أنّ المقالات طويلة والمُستخدمون مُهتمّون بقراءتها (وهذا جيّد). لو كان الرّقمان مُنخفضين فهذا مؤشّر سيّئ، إلّا إن كان هدف موقعك هو الدّخول والخروج بسرعة، مثل Google. معدل الارتداد (Bounce Rate) ما يقيسه: الزّوّار الّذين يُشاهدون صفحة واحدة ويُغادرون دون أن ينقروا أيّ شيء. ما يعنيه: بشكل عامّ يُعتبر هذا الرّقم رفضًا من الزّوّار لموقعك، ولكن هناك بعض الاستثناءات. تميل المُدوّنات إلى معدّل ارتداد عالٍ لأنّها مُصمّمة لمُشاهدة صفحة واحدة: إمّا مشاهدة صفحة آخر التّدوينات أو زيارة تدوينة مُعيّنة. يمكن أن يتأثر مُعدّل الارتداد بشدّة بِبُنية موقعك ومصدر زياراتك، فحتّى إن بدا الرّقم بسيطًا، فهو مؤشّر مُعقَّد. النسبة المئوية للزيارات الجديدة (New vs. Return Visitors) ما يقيسه: لو أنّ زائرًا (أو جهازًا) زار موقعك من قبل، فإنّه يُعتبر "عائدًا" (returning)، وإلّا فهو جديد. ما يعنيه: يعرف العائدون موقعك أكثر، ولهذا "يرتدّون" أقل ويشاهدون صفحات أكثر، فإن كانوا يعودون فهذا لأنّه ما تقدّمه يُعجبهم، ولهذا يقضون وقتًا أطول عادةً. أمّا الزّوّار الجدد فهم مؤشّر جيّد، لأنّ ذلك يعني أنّ موقعك يصل إلى أناس أكثر. الفكرة الأساسيّة هي نسبة الجُدد إلى العائدين، فلو لم يكن لديك زوّار عائدون فهذا يعني أنّ موقعك جديد، أو سيّئ، ولو لم يكن لديك إلّا زوّار عائدون فهذا يعني أنّ مُستخدميك مُخلصون، ولكنّ الموقع يحتضر. بشكل عامّ، كلّما كان الموقع أكثر "نُضجًا"، سيكون العائدون أكثر (كنسبة مئويّة)، لأنّ العائدين إلى موقعك بصورة مُتكرّرة أفشل من الزّيارات من خلال مُحرّكات البحث والحملات الإعلانيّة. ترجمة بتصرّف للدّرسين What is Data وSummary Statistics من سلسلة Daily UX Crash Course لصاحبها Joel Marsh. اقرأ أيضًا النسخة العربية الكاملة من كتاب مدخل إلى تجربة المستخدم (User Experience - UX) 1.0.0 تجربة المستخدم للهواتف المحمولة وتوقعات المستخدمين تجربتنا في إنشاء أداة تسجل تفاعلات المستخدم 150 خدعة للعمل بذكاء أكثر في مجال تجربة المستخدم UX كيف تصمم أسلوب عمل مرن في مجال تجربة المستخدم