في هذا المقال، نقدم لكم لمحة سريعة عن أساسيات لغة البرمجة روبي، ويعتبر هذا المقال مُخصص بالدرجة الأولى للمبتدئين في تعلم اللغة ولديهم أرضية مسبقة عن مفاهيم البرمجة بشكل عام. سنتناول في هذا المقال المواضيع التالية:
- كتابة التعليقات.
- المصفوفات.
- جمل التحكم.
- معالجة الخطأ.
- بناء الوظائف.
- جملة yield.
- الفئات.
ملاحظة: ناتج تنفيذ الأمثلة والأوامر الموجودة في هذا المقال تقع بعد علامة #=> من كل نتيجة أو أمر.
كتابة التعليقات
# هذا تعليق =begin تعليق من أكثر من سطر =end
قبل البدء، لا بد من التنويه أن كل شيء عبارة عن كائن في لغة روبي. الأرقام عبارة عن كائنات، والوظيفة class في السطر التالي تعيد نوع الكائن:
3.class #=> Fixnum
3.to_s #=> "3"
العمليات الحسابية والثنائية
1 + 1 #=> 2
8 - 1 #=> 7
10 * 2 #=> 20
35 / 5 #=> 7
2**5 #=> 32
5 % 3 #=> 2
3 & 5 #=> 1
3 | 5 #=> 7
3 ^ 5 #=> 6
العمليات الرياضية سهلة الاستدعاء على مستوى الكائن:
1.+(3) #=> 4
10.* 5 #=> 50
بعض القيم تُعتبر كائنات مثل:
nil # تشبه القيمة الفارغة في اللغات الأخرى
true # صحيح منطقي
false # خطأ منطقي
nil.class #=> NilClass
true.class #=> TrueClass
false.class #=> FalseClass
المساواة:
1 == 1 #=> true
2 == 1 #=> false
اللامساواة:
1 != 1 #=> false
2 != 1 #=> true
تُعتبر القيمة الفارغة مرادفة للقيمة المنطقية الخاطئة:
!nil #=> true
!false #=> true
!0 #=> false
المقارنات:
1 < 10 #=> true
1 > 10 #=> false
2 <= 2 #=> true
2 >= 2 #=> true
عمليات المقارنة المُجمعة:
1 <=> 10 #=> -1
10 <=> 1 #=> 1
1 <=> 1 #=> 0
العمليات المنطقية:
true && false #=> false
true || false #=> true
!true #=> false
يوجد نسخة أخرى من العمليات المنطقية ولكن بتطبيق مفهوم الأولوية المنخفضة، مما يعني استخدامها كبناء للتحكم في التدفقات (Flow Control) وربط الجمل ببعضها حتى تقوم أحدها بإرجاع قيمة منطقية صحيحة أو خاطئة.
فمثلا، في السطر التالي تُستدعى do_something_else في حال كان استدعاء do_something ناجحاً.
do_something() and do_something_else()
وهنا يُستدعى log_error في حال فشل استدعاء: do_someting
do_something() or log_error()
النصوص
النصوص عبارة عن كائنات:
'I am a string'.class #=> String
"I am a string too".class #=> String
placeholder = 'use string interpolation'
"I can #{placeholder} when using double quoted strings"
#=> "I can use string interpolation when using double quoted strings"
يُفضل استخدام علامة التنصيص المنفردة على المزدوجة وذلك قدر الإمكان. علامة التنصيص المزدوجة تُضيف بعض الحسابات الداخلية الزائدة، ومن الممكن جمع النصوص ببعضها بشرط عدم استخدام الأرقام.
'hello ' + 'world' #=> "hello world"
'hello ' + 3 #=> TypeError: can't convert Fixnum into String
'hello ' + 3.to_s #=> "hello 3"
دمج النصوص مع العمليات:
'hello ' * 3 #=> "hello hello hello "
الإضافة لنص:
'hello' << ' world' #=> "hello world"
لطباعة نص وسطر في النهاية نستخدم وظيفة puts:
puts "I'm printing!"
#=> I'm printing!
#=> nil
طباعة نص دون سطر في النهاية:
print "I'm printing!"
#=> I'm printing! => nil
المتغيرات
تعريف المتغيرات:
x = 25 #=> 25
x #=> 25
استخدام عملية المساواة تُرجع القيمة المستخدمة وهذا يعني أنك تستطيع إجراء عمليات مساواة متعددة كما المثال التالي:
x = y = 10 #=> 10
x #=> 10
y #=> 10
من المتعارف عليه استخدام طريقة snake_case في تسمية المتغيرات:
snake_case = true
حاول أن تستخدم أسماء متغيرات ذات دلالة:
path_to_project_root = '/good/name/'
path = '/bad/name/'
الرموز (Symbols) في لغة روبي عبارة عن كائنات، وهي ثابتة.
وتُمَثِّل الرموز ثوابت من الممكن إعادة استخدامها ويتم تمثيلها داخليا بأرقام. وغالبا يتم استخدامها بدلا من النصوص لتوصيل قيم ذات معنى ومحددة:
:pending.class #=> Symbol
status = :pending
status == :pending #=> true
status == 'pending' #=> false
status == :approved #=> false
المصفوفات
لتعريف مصفوفة نقوم بالتالي:
array = [1, 2, 3, 4, 5] #=> [1, 2, 3, 4, 5]
من الممكن أن تحتوي المصفوفة على عناصر ذات أنواع مختلفة:
[1, 'hello', false] #=> [1, "hello", false]
يتم فهرسة المصفوفات بطريقة أمامية:
array[0] #=> 1
array.first #=> 1
array[12] #=> nil
ومن الممكن فهرستها بطريقة عكسية:
array[-1] #=> 5
array.last #=> 5
ومن الممكن تحديد فهرس البداية والنهاية للحصول على جزء أو شريحة من المصفوفة:
array[2, 3] #=> [3, 4, 5]
نستخدم وظيفة reverse لإجراء عملية عكس المصفوفة:
a=[1,2,3]
a.reverse! #=> [3,2,1]
من الممكن أيضا أن نستخدم نطاقًا لإجراء عملية الاقتطاع من المصفوفة بالطريقة التالية:
array[1..3] #=> [2, 3, 4]
لإجراء عملية الإضافة على المصفوفة نقوم بالتالي:
array << 6 #=> [1, 2, 3, 4, 5, 6]
أو :
array.push(6) #=> [1, 2, 3, 4, 5, 6]
للتأكد من وجود قيمة في المصفوفة نستخدم الوظيفة include :
array.include?(1) #=> true
هاش Hash
الهاش Hash في لغة روبي هو القاموس الرئيسي باستخدام المفتاح والقيمة، ولتعريف الهاش نستخدم الأقواس المزخرفة:
hash = { 'color' => 'green', 'number' => 5 }
hash.keys #=> ['color', 'number']
يتم البحث في الهاش باستخدام المفتاح بالطريقة التالية:
hash['color'] #=> 'green'
hash['number'] #=> 5
في حالة البحث في الهاش باستخدام مفتاح غير موجود فإن النتيجة المرجعة هي nil :
hash['nothing here'] #=> nil
بعد نسخة روبي 1.9 يوجد طريقة خاصة لاستخدام الرموز كمفاتيح للهاش:
new_hash = { defcon: 3, action: true }
new_hash.keys #=> [:defcon, :action]
لفحص وجود مفتاح أو قيمة في الهاش نستخدم الطريقة التالية:
new_hash.key?(:defcon) #=> true
new_hash.value?(3) #=> true
ملاحظة/ المصفوفات والهاش في الروبي قابلة للعد (Enumerable) ، وكلاهما يحتوي على مجموعة من الوظائف المفيدة.
جمل التحكم
جملة الشرط:
if true
'if statement'
elsif false
'else if, optional'
else
'else, also optional'
end
جملة التكرار for:
for counter in 1..5
puts "iteration #{counter}"
end
#=> iteration 1
#=> iteration 2
#=> iteration 3
#=> iteration 4
#=> iteration 5
على الرغم من وجود جملة التكرار وشيوعها، إلا أنه لا يوجد من يستخدمها، وبدلا من ذلك يجب عليك استخدام جملة each وتمرير كتلة من الشفرة البرمجية لها.
هذه الكتلة من الشفرة البرمجية تُرادف lambdas أو الوظائف الوهمية.
عند استخدام وظيفة each مع نطاق من الأرقام، فإن كتلة الشفرة البرمجية المُمَرَرَة لها تُنفذ مرة واحدة مع كل عنصر من النطاق.
يُمرَّر عداد كمعامل لكتلة الشفرة البرمجية،وتُكتَب جملة each بالطريقة التالية:
(1..5).each do |counter|
puts "iteration #{counter}"
end
#=> iteration 1
#=> iteration 2
#=> iteration 3
#=> iteration 4
#=> iteration 5
نستطيع إحاطة كتلة الشفرة البرمجية بأقواس مزخرفة:
(1..5).each { |counter| puts "iteration #{counter}" }
نستطيع استخدام each للمرور على محتويات التراكيب مثل المصفوفات والهاش:
array.each do |element|
puts "#{element} is part of the array"
end
hash.each do |key, value|
puts "#{key} is #{value}"
end
إذا كنت تريد الحصول على فهرس العنصر الذي تمر عليه في جملة each تستطيع استخدام جملة each_with_index وتعريف متغير الفهرس من خلالها.
انظر المثال التالي:
array.each_with_index do |element, index|
puts "#{element} is number #{index} in the array"
end
counter = 1
while counter <= 5 do
puts "iteration #{counter}"
counter += 1
end
#=> iteration 1
#=> iteration 2
#=> iteration 3
#=> iteration 4
#=> iteration 5
توجد مجموعة من الوظائف الأخرى لتنفيذ الحلقات Loops في لغة الروبي، فمثلا توجد map، reduce ، inject والقائمة تطول.
Map تأخذ مصفوفة كمعامل، وتقوم بالمرور على عناصرها وإجراء عمليات عليها وترجعها في مصفوفة جديدة، كما المثال التالي:
array = [1,2,3,4,5]
doubled = array.map do |element|
element * 2
end
puts doubled
#=> [2,4,6,8,10]
puts array
#=> [1,2,3,4,5]
جملة case :
grade = 'B'
case grade
when 'A'
puts 'Way to go kiddo'
when 'B'
puts 'Better luck next time'
when 'C'
puts 'You can do better'
when 'D'
puts 'Scraping through'
when 'F'
puts 'You failed!'
else
puts 'Alternative grading system, eh?'
end
#=> "Better luck next time"
نستطيع استخدام نطاق مع جملة case بالطريقة التالية:
grade = 82
case grade
when 90..100
puts 'Hooray!'
when 80...90
puts 'OK job'
else
puts 'You failed!'
end
#=> "OK job"
معالجة الخطأ
begin
raise NoMemoryError, 'You ran out of memory.'
rescue NoMemoryError => exception_variable
puts 'NoMemoryError was raised', exception_variable
rescue RuntimeError => other_exception_variable
puts 'RuntimeError was raised now'
else
puts 'This runs if no exceptions were thrown at all'
ensure
puts 'This code always runs no matter what'
end
بناء الوظائف والدوال
def double(x)
x * 2
end
الوظائف ضمنيا تعيد قيمة آخر جملة في الوظيفة:
double(2) #=> 4
الأقواس تُعتبر إضافية، ومن الممكن استدعاء الوظيفة من دونهم:
double 3 #=> 6
double double 3 #=> 12
def sum(x, y)
x + y
end
معاملات الوظائف يتم الفصل بينها بواسطة الفاصلة.
sum 3, 4 #=> 7
sum sum(3, 4), 5 #=> 12
جملة yield
كل الوظائف تمتلك ضمنيا معامل كتلة إضافي خاص بها، وتُستدعى بواسطة كلمة yield :
def surround
puts '{'
yield
puts '}'
end
surround { puts 'hello world' }
# {
# hello world
# }
تستطيع تمرير كتلة من الشفرة البرمجية للوظيفة، ونستخدم رمز & لحفظ عنوان كتلة الشفرة البرمجية المُمَرَرَة.
def guests(&block)
block.call 'some_argument'
end
تستطيع تمرير أكثر من معامل للوظيفة بشكل غير محدد باستخدام رمز *، وهذه المجموعة من المعاملات تتحول إلى مصفوفة والتي بدورك تستطيع المرور عليها باستخدام جملة each:
def guests(*array)
array.each { |guest| puts guest }
end
إذا كانت الوظيفة تُرجع مصفوفة، فإنك تستطيع استخدام المساواة المتعددة لأكثر من متغير في نفس الوقت (unpacking):
def foods
['pancake', 'sandwich', 'quesadilla']
end
breakfast, lunch, dinner = foods
breakfast #=> 'pancake'
dinner #=> 'quesadilla'
من المتفق عليه أن كل الوظائف التي تعيد قيمة منطقية لابد أن تنتهي بعلامة استفهام عند استدعائها:
5.even? # false
5.odd? # true
إذا كانت الوظيفة تنتهي بعلامة تعجب، فهذا يعني أن التغيير الذي يتم على المتغير أو العنصر يكون مباشرا على قيمته، أما بدون علامة تعجب، فإن العملية لا تؤثر على العنصر، ويتم إعادة التغيير في عنصر جديد. انظر للمثال التالي:
company_name = "Dunder Mifflin"
company_name.upcase #=> "DUNDER MIFFLIN"
company_name #=> "Dunder Mifflin"
company_name.upcase! # we're mutating company_name this time!
company_name #=> "DUNDER MIFFLIN"
الأصناف Classes
تُعرَّف الأصناف باستخدام الكلمة المحجوزة class:
class Human
نعرّف في ما يلي متغيرًا على مستوى الصنف، وهو مُشارَك بين الكائنات المتولدة الخاصة بهذا الصنف:
@@species = 'H. sapiens'
الطريقة الأساسية للاستهلال Initialization :
def initialize(name, age = 0)
إعطاء قيمة المعامل “الاسم” للمتغير الخاص بالكائن المتولد من الفئة بنفس الاسم
@name = name
في حالة عدم تمرير معامل باسم “العمر” فإن القيمة التلقائية هي التي ستمرر للمتغير الخاص بالكائن المتولد:
@age = age
end
وظيفة التعديل الأساسية (Setter):
def name=(name)
@name = name
end
وظيفة الاسترجاع الأساسية (Getter):
def name
@name
end
نستطيع تنفيذ وظيفتي التعديل والاسترجاع بواسطة attr_accessor كالتالي:
attr_accessor :name
ويمكن فصل العمليتين عن بعضهما.
المُسترجِع :getter
attr_reader :name
المعدِّل setter:
attr_writer :name
للتمييز بين الوظائف الخاصة بالصنف، والوظائف الخاصة بالكائن المتولد من الصنف، نستخدم كلمة self، وهي خاصة لتعريف الوظائف على مستوى الفئة.
def self.say(msg)
puts msg
end
def species
@@species
end
end
تعريف كائنين من الصنف Human:
jim = Human.new('Jim Halpert')
dwight = Human.new('Dwight K. Schrute')
استدعاء بعض الوظائف:
jim.species #=> "H. sapiens"
jim.name #=> "Jim Halpert"
jim.name = "Jim Halpert II" #=> "Jim Halpert II"
jim.name #=> "Jim Halpert II"
dwight.species #=> "H. sapiens"
dwight.name #=> "Dwight K. Schrute"
استدعاء الوظيفة على مستوى الصنف:
Human.say('Hi') #=> "Hi"
يُعرَّف مجال المتغيرات في المكان التي عُرِّف فيه المتغيّر، والمتغيرات التي تبدأ ب علامة $ تكون على مستوى النطاق الواسع Global Variable:
$var = "I'm a global var"
defined? $var #=> "global-variable"
المتغيرات التي تبدأ بعلامة @ تكون على مستوى الكائن المتولد:
@var = "I'm an instance var"
defined? @var #=> "instance-variable"
المتغيرات التي تبدأ ب @@ تكون على مستوى الصنف:
@@var = "I'm a class var"
defined? @@var #=> "class variable"
المتغيرات التي تبدأ بحرف كبير تكون ثوابتا:
Var = "I'm a constant"
defined? Var #=> "constant"
المتغير الخاص بالصنف يتشاركه الصنف وكل الأصناف التي تريث منه:
# base class
class Human
@@foo = 0
def self.foo
@@foo
end
def self.foo=(value)
@@foo = value
end
end
# derived class
class Worker < Human
end
Human.foo # 0
Worker.foo # 0
Human.foo = 2 # 2
Worker.foo # 2
المتغير الخاص بالكائن المتولد من الصنف غير مشارك أو مرئي في الأصناف التي ترث من الصنف الرئيسي:
class Human
@bar = 0
def self.bar
@bar
end
def self.bar=(value)
@bar = value
end
end
class Doctor < Human
end
Human.bar # 0
Doctor.bar # nil
عند استخدام عملية include لتضمين وحدة Module داخل صنف، فإن الوظائف الخاصة بالوحدة تكون مضمنة في الكائنات المتولدة من الصنف.
وعند استخدام عملية extend للوحدة داخل صنف، فإن الوظائف الخاصة بالوحدة تكون مضمنة في نفس الصنف.
module ModuleExample
def foo
'foo'
end
end
class Person
include ModuleExample
end
class Book
extend ModuleExample
end
Person.foo # => NoMethodError: undefined method `foo' for Person:Class
Person.new.foo # => 'foo'
Book.foo # => 'foo'
Book.new.foo # => NoMethodError: undefined method `foo'
تُنفَّذ دوال استرداد Callbacks عندما تُطبَق include أو extend على الوحدة :
module ConcernExample
def self.included(base)
base.extend(ClassMethods)
base.send(:include, InstanceMethods)
end
module ClassMethods
def bar
'bar'
end
end
module InstanceMethods
def qux
'qux'
end
end
end
class Something
include ConcernExample
end
Something.bar # => 'bar'
Something.qux # => NoMethodError: undefined method `qux'
Something.new.bar # => NoMethodError: undefined method `bar'
Something.new.qux # => 'qux'
ترجمة – بتصرّف – للمقال Learn X in Y minutes Where X=ruby.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.