في هذا الجزء، سنكمل مشوارنا في طريق تعلم أساسيات البرمجة بلغة روبي، حيث سنتعرف على المزيد من أنواع وبنى البيانات الضرورية، بالإضافة إلى الجمل الشرطية، حلقات التكرار، والقطع الشيفرية.
بالإضافة إلى هذه التدوينة، تتكون السلسلة من التدوينات التالية:
- تعلم أساسيات البرمجة بلغة روبي – مُقدِّمة
- تعلم أساسيات البرمجة بلغة روبي – أهلاً بالعالم!
- تعلم أساسيات البرمجة بلغة روبي – ثوابت، متغيرات، وأنواع بيانات
ملاحظة: في هذا الدرس، سأستخدم النمط المبسّط من irb باستخدام الأمر التالي:
أولا: المزيد من أنواع وبنى البيانات
لنلقِ نظرة على بعض أنواع وبنى البيانات الموجودة في لغة روبي.
المصفوفات (Arrays)
باستخدام المصفوفات، يمكن تجميع عدة متغيرات (أو كائنات) في سلسلة تحت اسم واحد، ثم القيام بعدد من المهام كفرز عناصر المصفوفة أو تغيير قيم بعضها مثلا. هناك عدة طرق لإنشاء مصفوفة جديدة في روبي. دعونا نجرب طريقتين في irb:
>> greek_alphabet = Array.new
=> []
>> greek_alphabet = []
=> []
بإمكاننا التأكد من أن المصفوفة خالية باستخدام طريقة empty?:
>> greek_alphabet.empty?
=> true
المصفوفة الخالية تبدو مملة بعض الشيء. لننشئ مصفوفة جديدة، لكن دعونا نحدد عدد عناصرها بالإضافة إلى العنصر الافتراضي منذ البداية:
>> greek_alphabet = Array.new(4, '!')
=> ["!", "!", "!", "!"]
ماذا إذا أردنا أن تحتوي المصفوفة عناصر مختلفة؟
>> greek_alphabet = ['alpha', 'beta', 'gamma', 'delta']
=> ["alpha", "beta", "gamma", "delta"]
إذا، لدينا مصفوفة تحوي عدة عناصر. مالذي يمكننا فعله بها؟ هناك الكثير من الطرق الخاصة بمصفوفات روبي، لكننا سنتطرق لبعض منها. يمكننا معرفة حجم مصفوفة ما (عدد عناصرها) باستخدام أحد الطريقتين size وlength:
>> greek_alphabet.size
=> 4
>> greek_alphabet.length
=> 4
كما في العديد من لغات البرمجة الأخرى، يبدأ فهرس (index) أي مصفوفة من الرقم 0، بمعنى أنه إذا أردنا استرجاع العنصر الأول، فنستخدم الفهرس 0، وإذا أردنا استرجاع العنصر الثاني، فنستخدم الفهرس 1 وهكذا. لنجرب هذا في irb:
>> greek_alphabet[0]
=> "alpha"
>> greek_alphabet[2]
=> "gamma"
يمكننا القيام بالعكس، أي معرفة فهرس عنصر معين باستخدام طريقة index:
>> greek_alphabet.index('delta')
=> 3
تتمتع المصفوفات في روبي بمرونة عالية، فمن الممكن مثلا وضع كائنات من أنواع مختلفة في مصفوفة واحدة، استخدام فهرس ذو قيمة سالبة لاسترجاع عنصر ما من نهاية المصفوفة، وإضافة عنصر جديد في مكان ما بعد نهاية المصفوفة. كل هذه العمليات غير ممكنة في لغات برمجة أخرى، خصوصا الساكنة منها. دعونا نجرب العمليات السابقة في irb:
>> mixed_array = ['delicious', 3.14, true]
=> ["delicious", 3.14, true]
>> mixed_array[-1]
=> true
>> mixed_array[-2]
=> 3.14
>> mixed_array[-3]
=> "delicious"
>> greek_alphabet[5] = "zeta"
=> "zeta"
>> greek_alphabet
=> ["alpha", "beta", "gamma", "delta", nil, "zeta"]
مالذي يمكننا فعله أيضا؟ حسنا، يمكننا فرز عناصر المصفوفة أو عكس ترتيبها:
>> primes = [7, 5, 11, 13, 3, 2]
=> [7, 5, 11, 13, 3, 2]
>> primes.sort
=> [2, 3, 5, 7, 11, 13]
>> primes.reverse
=> [2, 3, 13, 11, 5, 7]
ملاحظة: إذا قمنا بالتأكد من ترتيب عناصر المصفوفة بعد تنفيذ المثال أعلاه، سنجد أنه لم يتغير! السبب هو أن طريقتي sort وreverse لاتجريان أي تعديل على المصفوفة الأصلية، بل تعيدان مصفوفات جديدة بالترتيب المطلوب. من الممكن التعديل على المصفوفة الأصلية كما يلي:
>> primes.sort!
=> [2, 3, 5, 7, 11, 13]
>> primes
=> [2, 3, 5, 7, 11, 13]
>> primes.reverse!
=> [13, 11, 7, 5, 3, 2]
>> primes
=> [13, 11, 7, 5, 3, 2]
هذا العرف لا يقتصر على الطرق الخاصة بالمصفوفات فقط، بل هو مستخدم بكثرة في لغة روبي.
جداول التلبيد (Hashes)
[ملاحظة: اعتمدت على قاموس عرب آيز لتعريب مصطلح hash. هل يوجد تعريب آخر أفضل أو أوضح؟]
جداول التلبيد تشبه المصفوفات إلى حد بعيد. الفرق يكمن في أنه يمكن استخدام أي نوع من أنواع البيانات كفهرس لجدول التلبيد، وليس الأرقام فقط كما هي الحال في المصفوفات. يسمى الفهرس في جدول التلبيد بالمفتاح (key).
لنجرب المثال التالي في irb:
>> roman_numerals = { 'I' => 1, 'II' => 2, 'III' => 3, 'IV' => 4 }
=> {"II"=>2, "IV"=>4, "III"=>3, "I"=>1}
>> roman_numerals['IV']
=> 4
هناك العديد من الطرق الخاصة بجداول التلبيد في روبي، لكننا لن نناقشها هنا.
الرموز (Symbols)
الرموز هي أحد أنواع البيانات الخاصة في لغة روبي، وهي مستخدمة بكثرة في إطار Ruby on Rails. كل ما نحتاج معرفته في الوقت الحالي هو أن الرمز يبدأ بنقطتين رأسيّتين (:)، وأنه يمثل “ماسك مكان” أو معرّف. لنجرب مثالا عمليا على استخدام الرموز كمفاتيح في جداول التلبيد:
>> roman_numerals = { :I => 1, :II => 2, :III => 3, :IV => 4 }
=> {:I=>1, :II=>2, :III=>3, :IV=>4}
>> roman_numerals[:II]
=> 2
ثانياً: العوامل المنطقية
تدعم روبي العمليات المنطقية المعروفة والموجودة في معظم لغات البرمجة الأخرى. ناتج هذه العمليات هو إما true أو false (تذكروا: كل شيء في روبي هو عبارة عن كائن، حتى true وfalse!). لنجرب الأمثلة التالية في irb:
>> 1 < 2
=> true
>> 2 < = 3
=> true
>> 'ruby' == 'python'
=> false
>> (1 == 1) and (2 == 2)
=> true
>> (1 == 1) and (2 == 3)
=> false
>> (1 == 1) && (2 == 3)
=> false
>> (1 == 1) or (2 == 3)
=> true
>> (1 == 2) or (2 == 3)
=> false
>> (1 == 2) || (2 == 3)
=> false
>> not (1 == 2)
=> true
>> !(2 == 3)
=> true
>> not true
=> false
يمكن لمفسّر روبي أن ينتج قيمة منطقية من أي عبارة. لذلك، بإمكاننا مزج سلاسل نصية، أعداد، وحتى تواريخ في العبارات المنطقية. القاعدة بسيطة: false وnil ينتجان”خطأ”، وأي شيء آخر ينتج “صواب”. هذا يعني بأن العدد 0 ينتج “صواب”، مما قد يفاجئ المبرمجين بلغة C أو C++.
>> 'googol' == 10**100
=> false
>> 'ruby' and true
=> true
>> nil or false
=> false
>> 0 or nil
=> 0
>> 0 and nil
=> nil
ثالثاً: الجمل الشرطية
كأي لغة برمجة أخرى، تحتوي روبي جملا شرطية تمكّن المبرمج من تنفيذ عبارات معينة بناء على تحقق شرط (أو عدم تحققه). لنطّلع على بعض الأمثلة لعبارة if وelsif:
if number_of_open_tabs < = 5
puts 'Light load.'
elsif number_of_open_tabs <= 10
puts 'Medium load.'
elsif number_of_open_tabs <= 15
puts 'Heavy load.'
else
puts 'Take a bereak!'
end
يجدر ملاحظة أن الكلمة المفتاحية هي elsif – خمسة حروف – وليست elseif.
pi = 3.141; phi = 1.618
if phi < pi
puts 'Keep in mind that Phi is less than Pi.'
end
من الممكن استخدام الصيغة التالية إذا كانت الشفرة التي نريد تنفيذها مكونة من عبارة واحدة:
puts 'Keep in mind that Phi is less than Pi.' if phi < pi
هناك أيضا عبارة unless، التي يمكن النظر إليها على أنها عبارة if منفية:
unless phi >= pi
puts 'Keep in mind that Phi is less than Pi.'
end
puts 'Keep in mind that Phi is less than Pi.' unless phi >= pi
يفضّل عدم إساءة استخدام جملة unless، خصوصاً في الجمل الشرطية المعقدة، لأنها قد تربك الشخص الذي سيقرأ الشفرة البرمجية (بل إنها قد تربك كاتب الشفرة نفسه!).
رابعا: حلقات التكرار
تحتوي لغة روبي أيضاً على عدد من حلقات التكرار. أحدها هي حلقة while التقليدية:
greek_alphabet = ['alpha', 'beta', 'gamma', 'delta']
i = 0
while i < greek_alphabet.length
puts greek_alphabet[i].capitalize
i += 1
end
# Prints the following:
# Alpha
# Beta
# Gamma
# Delta
هناك أيضاً حلقة until. وكما أن عبارة unless هي عبارة if منفية، فحلقة until هي حلقة while منفية:
i = 0
until i > 4
puts i
i += 1
end
# Prints the following:
# 0
# 1
# 2
# 3
# 4
أخيراً، لننظر إلى حلقة times، وهي طريقة موجودة لدى أي كائن من النوع Integer:
5.times { |i| print i, ' ' } # Prints the following: 0 1 2 3 4
فائدة مهمة: القطع الشيفرية (Blocks)
هل لاحظتم شيئاً مميزاً في المثال الأخير أعلاه؟ الشيفرة الموجودة بين القوسين هي مثال لأحد أبرز مزايا لغة روبي: القطع الشفرية أو blocks. قد تبدو القطعة الشيفرية كمجرد مجموعة من العبارات البرمجية، لكنها في الحقيقة أكثر من ذلك بكثير. غالباً، تستخدم القطعة الشفرية كأسلوب لجلب عناصر بنية بيانات ما والمرور عليها عنصراً عنصراً. مثلا، بإمكاننا جلب عناصر المصفوفة greek_alphabet ومعالجة كل عنصر على حدة باستخدام طريقة each كالتالي:
greek_alphabet = ['alpha', 'beta', 'gamma', 'delta']
greek_alphabet.each do |element|
puts element.capitalize
end
# Prints the following:
# Alpha
# Beta
# Gamma
# Delta
element هو الاسم الذي اخترناه للمتغيّر المحلي الذي يمثل العنصر الذي تتم معالجته حالياً. إذا وضعنا كل الشفرة السابقة في سطر واحد، يمكننا استخدام الأقواس بدلاً من do وend:
greek_alphabet.each { |element| puts element.capitalize }
تذكروا: هذا ليس هو الاستخدام الوحيد للقطع الشيفرية. سنعاود زيارة هذا الموضوع في درس قادم بإذن الله.
في الجزء التالي، ستتخذ السلسلة منحىً كائنياً، حيث سنتعرف على جوانب البرمجة غرضية التوجّه الموجودة في روبي، وسنتعلم كيفية إنشاء أنواع بيانات وكائنات خاصة بنا.