تُصنف البيانات في لغات البرمجة إلى عدة أنواع يستدل الحاسوب بها للتعامل مع تلك البيانات أثناء تنفيذ البرامج، مثل تحديد العمليات الممكن تنفيذها على تلك البيانات، وهي تشبه إلى حد كبير أنواع البيانات التي نتعامل معها في حياتنا العادية، مثل أنواع الأعداد في الرياضيات، إذ توجد الأعداد الطبيعية مثل 0 و1 و2، والأعداد الصحيحة مثل -1 و0 و1، والأعداد غير الكسرية أو غير النسبية irrational numbers مثل π، بحيث يمكن جمع الأعداد من تلك الأنواع المختلفة مع بعضها لنحصل على نتيجة معينة، فمثلًا يمكن جمع العدد الطبيعي 5 مع العدد غير الكسري π:
5 + π
يمكننا إبقاء المعادلة كما هي لإظهار العدد غير الكسري π، أو يمكننا تقريبه بعدد محدود من الأرقام العشرية ثم جمع تلك القيمتين معًا على النحو التالي:
5 + π = 5 + 3.14 = 8.14
لكن لا يمكن جمع نوعين مختلفين من البيانات، مثل الكلمات والأعداد مثلًا معًا:
sky + 8
لا يمكن حل مثل تلك المعادلات وبالتالي لا يستطيع الحاسوب حلها أيضًا، فلا يمكن جمع بيانات من نوع كلمة مثل الكلمة "Sky" مع عدد طبيعي مثل العدد 8، لذا يجب الانتباه لنوع البيانات عند إسنادها لمتغيرات أو التعديل عليها وتنفيذ العمليات بينها مثل الجمع والطرح والضرب وغيرها. سنتعرف في هذا المقال على أهم أنواع البيانات الموجودة في لغة روبي Ruby، مثل الأعداد الصحيحة والعشرية والسلاسل النصية والرموز والمصفوفات وجداول Hash، وسنتعرف بعدها على مفهوم ديناميكية الأنواع dynamic typing والذي تعتمد عليه لغة روبي لتحديد نوع البيانات المُخزّنة ضمن المتغيرات والذي من الضروري فهمه لتجنب الأخطاء التي قد تقع خصوصًا أن المتغيرات يمكن أن تحتوي على قيمة من أي نوع من أنواع البيانات. سنتعرف في الفقرة التالية على أول نوع من البيانات وهو الأعداد الصحيحة وطرق التعامل معها في لغة روبي.
الأعداد الصحيحة integers
تشبه الأعداد الصحيحة integers في لغة روبي الأعداد الصحيحة في الرياضيات تمامًا، بحيث يمكن أن تحتوي على قيم أعداد موجبة أو سالبة أو تساوي الصفر، مثل الأعداد"1-" و"0" و"1"، ويمكن طباعتها على النحو التالي:
print -25
ليكون الخرج ببساطة:
-25
ويمكن تخزين العدد الصحيح ضمن متغير بإسناد قيمته إليه ثم طباعة ذلك المتغير على النحو التالي:
my_int = -25 print my_int
ويكون الخرج:
-25
يمكن تنفيذ العمليات الحسابية على الأعداد الصحيحة، مثل الجمع بين عددين وطباعة النتيجة على النحو التالي:
sum = 116 - 68 print sum
ويكون الخرج على النحو التالي:
48
ندرج عادةً الفاصلة "," بعد كل ثلاث خانات من الأعداد الكبيرة لتسهيل قراءتها فمثلًا نكتب العدد مليون "1000000" بإدراج الفواصل كما يلي: "1,000,000"، أما في لغة روبي لا يمكن استخدام هذه الفاصلة ضمن العدد، وتُستخدم علامة الشرطة السفلية _
التي توفرها روبي لنفس الغرض وهو تسهيل قراءة الأعداد الكبيرة على النحو التالي:
large_number = 1_234_567 print large_number
نلاحظ عند طباعة قيمة ذلك العدد لن تُطبع الشرطة السفلية _
، وستُطبع قيمة العدد الصحيح فقط:
1234567
سنتعرف في الفقرة التالية على نوع جديد من البيانات وهو الأعداد العشرية وطريقة التعامل معها.
الأعداد العشرية floating-point number
تُمثل الأعداد العشرية floating-point number أعدادًا حقيقية مثل الأرقام الكسرية أو غير الكسرية، وهي التي تحتوي على جزء عشري مثل العدد "9.0" أو "116.42-". يمكن طباعة الأعداد العشرية في لغة روبي تمامًا مثل طريقة طباعة الأعداد الصحيحة على النحو التالي:
print 17.3
ويكون الخرج على النحو التالي:
17.3
يمكن أيضًا تعريف متغير وإسناد قيمة عشرية إليه كما يلي:
my_float = 17.3 print my_float
ليعطي الخرج التالي:
17.3
وكما هو الحال مع الأعداد الصحيحة، يمكن تنفيذ العمليات الحسابية على الأعداد العشرية أيضًا على النحو التالي:
sum = 564.0 + 365.24 print sum
ويكون الخرج:
929.24
ناتج جمع عدد عشري مع عدد صحيح في لغة روبي هو عدد عشري:
sum = 564 + 365.24 print sum
ويكون الخرج:
929.24
سنتعرف في الفقرة التالية على نوع مهم من أنواع البيانات في لغة روبي وهي البيانات البوليانية.
البيانات البوليانية boolean
تُستخدم أنواع البيانات البوليانية boolean لتمثيل القيم المتعلقة بالمنطق الرياضي والتي تُستخدم لتطوير الخوارزميات في علم الحاسوب، إذ يُمثّل هذا النوع من البيانات في لغة روبي إما بالقيمة الصحيحةtrue
أو القيمة الخاطئةfalse
، إذ تنتج تلك القيم عن العديد من العمليات الرياضية مثل العمليات البوليانية التالية:
-
أكبر من
-
500 > 100 صحيح
true
-
1 > 5 خاطئ
false
-
500 > 100 صحيح
-
أصغر من
-
200 < 400 صحيح
true
-
4 < 2 خاطئ
false
-
200 < 400 صحيح
-
المساواة
-
5 = 5 صحيح
true
-
500 = 400 خاطئ
false
-
5 = 5 صحيح
كما هو الحال أنواع البيانات السابقة، يمكننا تخزين القيم المنطقيةtrue
أو false
ضمن متغير:
result = 5 > 8
نستخدم التابع()print
كما فعلنا سابقًا لطباعة قيمة المتغير البولياني:
print result
بما أن العدد 5 ليس أكبر من العدد 8، ستظهر النتيجة التالية:
false
البيانات البوليانية من أنواع البيانات الشائع استخدامها في لغة روبي ومعظم اللغات البرمجية الأخرى، وسنتعامل معها في هذه السلسلة وسنرى كيف يمكن أن تعيد التوابع والعمليات المختلفة القيم إما true
أو false
وكيف نستخدم هذه القيم للتحكم بمسار تنفيذ البرنامج، وسنتعرف في الفقرة التالية على السلاسل النصية وطريقة التعامل معها.
السلاسل النصية strings
تتكون السلاسل النصية strings من سلسلة من محرف واحد أو أكثر، والمحرف هو أي حرف أو رقم أو رمز، ويمكننا تعريف سلسلة نصية في لغة روبي بكتابة قيمتها ضمن علامتي اقتباس مفردة '
أو مزدوجة "
على النحو التالي:
"This is a string in double quotes."
استخدمنا في مقال سابق السلاسل النصية عند كتابة أول برنامج بلغة روبي لطباعة العبارة "!Hello, World" على الشاشة، إذ أن تلك القيمة هي سلسلة نصية تتكون من مجموعة من المحارف:
print "Hello, World!"
كما هو الحال مع الأنواع الأخرى من البيانات، فيمكن تخزين السلاسل النصية أيضًا في متغيرات:
output = "Hello, World!"
ويمكن طباعة النص المخزن في المتغير باستخدام اسم المتغير على النحو التالي:
print output
ليكون الخرج:
Hello, World!
يمكن تنفيذ العديد من العمليات على السلاسل النصية -كما هو الحال بالنسبة للأرقام- للتعديل عليها، وتكمن أهمية هذا النوع من البيانات بأنه طريقة للتواصل النصي بين المستخدم والبرنامج. سنتعرف في الفقرة التالية على نوع جديد من البيانات وهو المصفوفات التي تفيدنا في التعامل مع قائمة من البيانات.
المصفوفات arrays
المصفوفة array هي نوع من البيانات يُمكّننا من تخزين مجموعة من القيم داخله نسميها عناصر تلك المصفوفة، ويمكن تعريف المصفوفة بكتابة قيم عناصرها بين أقواس مربعة [ ]
والفصل بينها بالفاصلة ,
، فمثلًا لتعريف مصفوفة من الأرقام الصحيحة نكتب التالي:
[-3, -2, -1, 0, 1, 2, 3]
أو مصفوفة من الأعداد العشرية:
[3.14, 9.23, 111.11, 312.12, 1.05]
أو مصفوفة من السلاسل النصية:
['shark', 'cuttlefish', 'squid', 'mantis shrimp']
وكما هو الحال مع أنواع البيانات الأخرى يمكننا تخزين قيمة المصفوفة ضمن متغير:
sea_creatures = ['shark', 'cuttlefish', 'squid', 'mantis shrimp']
وعند طباعة قيمة أي مصفوفة:
print sea_creatures
سيظهر شكلها تمامًا كما هي عند تعريفها:
['shark', 'cuttlefish', 'squid', 'mantis shrimp']
يمكن الوصول إلى عنصر من عناصر أي مصفوفة باستخدام رقم الفهرس لذلك العنصر، وهو عدد يشير لترتيب ذلك العنصر ضمن المصفوفة ويبدأ من الصفر:
puts sea_creatures[0] # shark puts sea_creatures[2] # squid
للوصول إلى قيمة آخر عنصر من المصفوفة نستخدم رقم الفهرس السالب 1-
، كما توفر لغة روبي التابعfirst.
، الذي يعيد أول عنصر من المصفوفة، والتابع last.
الذي يعيد آخر عنصر منها:
puts sea_creatures.first # shark puts sea_creatures.last # mantis shrimp
يمكن لعناصر المصفوفة الواحدة أن تكون بأنواع مختلفة من البيانات، فمثلًا يمكن تخزين سلاسل نصية ورموز وحتى مصفوفات أخرى ضمن مصفوفة واحدة:
record = [ :en, "Sammy", 42, [ "coral", "reef" ] ]
تتمتع المصفوفات في لغة روبي بخاصية التعديل؛ أي يمكن إضافة عناصر جديدة إليها، أو حذف عناصر منها، أو تعديل قيم العناصر المخزنة فيها. سنتعرف في الفقرة التالية على نوع جديد من البيانات وهو الرموز والتي تساعدنا في تسمية الأشياء ضمن البرنامج.
الرموز Symbol
الرمز Symbol هو نوع خاص من البيانات يُستخدم مثل اسم أو معرّف داخل برنامج روبي، وتتميز الرموز بأن قيمتها ثابتة ولا يمكن تعديلها بعد تعريفها، وتشبه طريقة تعريف الرمز طريقة تعريف أي متغير ولكن دون تعيين قيمة له وإضافة محرف النقطتين :
قبل اسمه على النحو التالي:
:time_zone
نستخدم الرموز في لغة روبي لتسمية بعض الأشياء الهامة ضمن البرنامج، بينما نستخدم السلاسل النصية للنصوص التي نحتاج لتعديلها أو العمل عليها، إذ أن كل سلسلة نصية في روبي هي كائن جديد منفصل مخزن ضمن موقع منفصل في الذاكرة حتى لو تطابقت قيمها مع بعضها، بينما تتشارك الرموز المتطابقة بالقيمة بكونها كائن واحد مخزن في نفس المكان في الذاكرة. سنتعرف أكثر على استخدامات الرموز في الفقرة التالية عندما نتعرف على جداول Hash والتي تسمح لنا بربط مفاتيح بقيمها.
جداول Hash
جدول Hash هو مجموعة من أزواج المفاتيح والقيم تفيد في تخزين البيانات وتسمية كل منها وتسهيل الوصول إليها، ونستخدمها عادةً لتخزين بيانات متعلقة بشيء ما مثل بيانات شخص أو مستخدم، وتكون صيغة تعريفها على النحو التالي:
{"first_name" => "Sammy", "last_name" => "Shark"}
يمكن تخزين جدول Hash ضمن متغير بنفس طريقة إسناد الأنواع الأخرى من البيانات:
user = {"first_name" => "Sammy", "last_name" => "Shark"}
للوصول لقيمة ما من جدول Hash، نستخدم اسم المفتاح الخاص بتلك القيمة كما يلي:
print user["first_name"] # "Sammy" print user["last_name"] # "Shark"
يمكن استخدام الرموز مثل مفاتيح للقيم ضمن جدول Hash بدلًا من السلاسل النصية على النحو التالي:
user = {:first_name => "Sammy", :last_name => "Shark"}
يفضل دائمًا استخدام الرموز مثل مفاتيح للجداول لتقليل استخدام الذاكرة وتحسين الأداء، نظرًا لأن كل الرموز المتساوية بالقيمة تتشارك الكائن نفسه والقيمة نفسها ضمن الذاكرة، بينما سيولد لكل سلسلة نصية كائن جديد وفريد وبمكان جديد في الذاكرة، ويمكن الوصول للقيمة المقابلة لرمز ما ضمن جدول Hash باستخدام الرمز نفسه على النحو التالي:
print user[:first_name] # "Sammy" print user[:last_name] # "Shark"
يمكن تعريف جدول Hash بالصيغة التالية أيضًا:
user = {first_name: "Sammy", last_name: "Shark"}
تشبه هذه الصيغة الصيغة المستخدمة في لغة جافا سكريبت ولغات أخرى لتعريف الكائنات، وتُعرَّف المفاتيح في هذه الصيغة مثل رموز، أي يمكن الوصول للقيم في المثال السابق باستخدام الرموز first_name:
و last_name:
بدلًا من السلاسل النصية "first_name"
و "last_name"
. الآن، بعد أن تعرفنا على العديد من أنواع البيانات في لغة روبي سنتعرف في الفقرة التالية كيف يتعامل روبي معها.
لغة روبي ديناميكية الأنواع
تمتاز لغة روبي بأنها ديناميكية الأنواع dynamic typing، أي لا يُعرّف نوع البيانات صراحةً عند إسناد قيمتها إلى متغير، بل يُحدّد نوع البيانات تلقائيًا بحسب القيمة التي خُزّنت فيه، مما يعني أن عملية فحص النوع ستجري خلال وقت التشغيل للبرنامج وليس في وقت الترجمة كما هو الحال في اللغات التي تكون ثابتة الأنواع static typing، وهناك عدة لغات شهيرة تستخدم نفس الطريقة مثل لغة بايثون ولغة جافا سكريبت.
تتيح ديناميكية الأنواع إمكانية إسناد قيمة للمتغير t
التالي مثلًا من أي نوع من أنواع البيانات المتاحة:
t = 42 # t is an Integer t = "Sammy" # t is a String t = :sammy # t is a Symbol t = true # t is a boolean (true) t # t is nil
كما تتيح هذه الميزة إمكانية تعديل القيمة المُخزنة ضمن المتغير نفسه وإسناد قيمة جديدة من نوع بيانات مختلف، ففي المثال التالي سنطلب من المستخدم إدخال قيمة عددية:
print "Please enter the length of the room: " length = gets.chop
وبما أن القيمة التي سيدخلها المستخدم من لوحة المفاتيح ستكون من نوع سلسلةً نصيةً دائمًا، فيجب تحويلها إلى عدد أولًا قبل إجراء العمليات الحسابية عليها، ففي اللغات ثابتة الأنواع static typing سنضطر لتعريف نوع البيانات ضمن المتغير قبل إسناد القيمة له، وبذلك علينا إنشاء متغير جديد بنوع بيانات مناسب لتخزين قيمة البيانات بعد تحويلها ولن نتمكن من إعادة استخدام نفس المتغير، ولكن في لغة روبي وبما أنها ديناميكية الأنواع فذلك ممكن على النحو التالي:
# Convert the amount to a Float. length = length.to_f
استخدمنا التابع to_f
لتحويل قيمة السلسلة النصية إلى عدد عشري، كما يوجد التابع to_i
لتحويل قيمة السلسلة النصية إلى عدد صحيح، ويمكن تحويل أغلب الكائنات في روبي إلى سلاسل نصية باستخدام التابعto_s
:
42.to_s # "42" (42.5).to_s # "42.5" ["Sammy", "Shark"].to_s # "[\"Sammy\", \"Shark\"]"
لا تعني ديناميكية الأنواع إمكانية تنفيذ العمليات بين أنواع البيانات المختلفة دون تحويلها إلى نفس النوع أولًا، فمثلًا سينتج عند جمع سلسلة نصية مع عدد صحيح:
print 5 + "5"
الخطأ التالي في الخرج:
TypeError: String can't be coerced into Integer
حتى لو نفذنا نفس العملية بعكس ترتيب القيم:
print "5" + 5
سينتج خطأ أيضًا:
TypeError: no implicit conversion of Integer into String
لحل هذه المشكلة يجب تحويل السلسلة النصية إلى عدد صحيح أولًا ثم جمعها للحصول على النتيجة 10
، أما إذا أردنا دمجهما معًا مثل سلاسل نصية للحصول على السلسلة النصية "55" فيجب تحويل العدد إلى سلسلة نصية قبل جمعه. من مزايا اللغات ديناميكية الأنواع المرونة في استخدام البيانات والتعامل مع المتغيرات، ولكن العيب الوحيد في ذلك هو عدم القدرة على تحديد نوع البيانات المخزن ضمن متغير ما قد جرى التعديل عليه، ولحل هذه المشكلة توفر روبي طرقًا لتحديد نوع بيانات أي قيمة مستخدمة سنتعرف عليها في الفقرة التالية.
تحديد أنواع البيانات
معظم القيم في لغة روبي هي كائنات، فالأعداد الصحيحة والعشرية والمصفوفات والرموز وجداول Hash كلها كائنات في روبي، ويحتوي كل منها على تابع يسمى class
يعيد عند استدعائه نوع ذلك الكائن، وحتى القيم المنطقية true
و false
والقيمة nil
كائنات أيضًا:
42.class # Integer (42.2).class # Float ["Sammy", "Shark"].class # Array true.class # TrueClass nil.class # NilClass
كما يمكن استخدام تابع ?kind_of
للتحقق من نوع البيانات على النحو التالي:
42.kind_of?(Integer) # true
نستفيد من ذلك للتعرف على نوع البيانات المُخزنة ضمن متغير ما:
# نوع القيمة المسندة واضح عند التعريف sharks = ["Hammerhead", "Tiger", "Great White"] ... # لكنه غير واضح في أماكن أخرى من البرنامج sharks.kind_of?(Hash) # false sharks.kind_of?(Array) # true
يمكن استخدام هذه الطريقة أيضًا للتحقق من صحة البيانات القادمة من مصدر خارجي:
if data.kind_of? String data = data.to_f end
يوجد أيضًا التابع ?is_a
والذي له نفس وظيفة التابع ?kind_of
ولكنه أسهل بالقراءة:
if data.is_a? String data = data.to_f end
يساعد استخدام التوابع class
و ?kind_of
و ?is_a
في التأكد من النوع الصحيح للبيانات الذي نتعامل معه، وهناك طرق أخرى سنتعلمها لاحقًا تغني عن الحاجة للتحقق من نوع البيانات يدويًا.
الخاتمة
تعرفنا في هذا المقال على العديد من أنواع البيانات التي توفرها روبي، إذ يمكّننا فهم أنواع البيانات تلك ووظائفها من التعامل معها وإجراء العمليات عليها لتطوير البرامج المختلفة، وهي من الأفكار الأساسية في لغة روبي والعديد من لغات البرمجة الأخرى.
ترجمة -وبتصرف- للمقال Understanding Data Types in Ruby لصاحبه Brian Hogan.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.