الفرق بين المراجعتين لصفحة: «Ruby/methods»

من موسوعة حسوب
ط تنسيق بعض الأخطاء في الأكواد
لا ملخص تعديل
سطر 367: سطر 367:
</syntaxhighlight>
</syntaxhighlight>
إذا أردت تفادي استثناء فقط في جزء محدد من التابع فاستخدم حينها begin - end. لمزيد من التفاصيل ارجع إلى الصفحة الخاصة بالتعامل مع الاستثناءات.
إذا أردت تفادي استثناء فقط في جزء محدد من التابع فاستخدم حينها begin - end. لمزيد من التفاصيل ارجع إلى الصفحة الخاصة بالتعامل مع الاستثناءات.
= المصادر =
* [https://ruby-doc.org/core-2.5.1/doc/syntax/methods_rdoc.html صفحة Methods في توثيق روبي الرسمي.]

مراجعة 16:47، 16 نوفمبر 2018

التوابع:

التوابع في لغة روبي تتضمّن الوظائف التي يقوم بها برنامجك. إليك هذا المثال لتعريف تابع بسيط:

def one_plus_one
 1 + 1
end 

تعريف التابع يتكوّن من الكلمة المحجوزة def يتبعها اسم التابع، ثمّ جسم التابع، فالقيمة المعادة وفي النهاية الكلمة المحجوزة end. فعند تنفيذ التابع في المثال  السابق ستُرجع القيمة 2. هذا القسم سيغطّي تعريف التّوابع. ارجع إلى توصيف استدعاء التوابع لتتعرف على الصيغ المستخدمة لذلك.

تسمية التوابع

يمكن أن تستخدم لاسم التابع أحد المعاملات، وإلا فعليك أن تبتدئه بحرف أبجديّ أو أيّ محرف من محارف لوحة المفاتيح (أيضًا تعرف باسم محارف البتات الثمانية). ويمكن للاسم أن يتضمن أحرفًا وأرقامًا وشرطة سفليّة _، أو أيّ محرف من محارف البتات الثمانية. ومن المعتاد أن تستخدم الشرطة السفليّة لتفصل بين الكلمات في أسماء التوابع التي تتضمن كلمات عدّة.

def method_name
 puts "use underscores to separate words"
end

يجب أن تكتب البرامج بلغة روبي باستخدام مجموعات محارف متوافقة مع مجموعة المحارف US-ASCII مثل UTF-8 و ISO-8859-1. فإذا كان البتات الثمانية مضبوطة في مجموعات المحارف هذه، فهذا يدل على أنّ المحرف هو من المحارف الموسّعة، ولغة روبي تسمح أن تحتوي أسماء التوابع والمعرّفات الأخرى على تلك المحارف. إلا أنّ لغة روبي لا تسمح باستخدام بعض المحارف مثل ASCII NUL الذي رمزه ‎\x00. إليك بعض الأمثلة عن التوابع التي تصلح تسميتها في لغة روبي:

def hello
 "hello"
end
def こんにちは
 puts "means hello in Japanese"
end

وعادة ما تكون أسماء التوابع متوافقة مع نظام US-ASCII كون الحروف الخاصة به متوفرة على جميع لوحات المفاتيح. ويمكن لأسماء التوابع أن تنتهي بعلامة التعجّب ! أو علامة الاستفهام ? أو علامة المساواة = . إن التوابع المنتهية بعلامة تعجب تستدعى وتُنفّذ مثل أيّ تابع آخر، إلّا أنّها عادة ما تصنّف كتوابع خطرة. ففي مكتبة نواة لغة روبي يُعدّ التّابع خطيرًا إذا كان يغيّر من قيمة المتغيّر الذي يستقبله، ولذلك يُميّز بعلامة التعجّب في آخره. وفي هذه المكتبة يوجد لكلّ تابع من هذا النّوع تابعًا آخر مقابلًا ليس خطيرًا أي أنّه لا يغيّر من قيمة المتغيّر الذي يستقبله، وهذه التّوابع يكون لها نفس التسمية إلا أنّها لا تنتهي بإشارة التّعجب. أمّا المتعارف عليه بالنّسبة للتوابع التي تنتهي تسميتها بإشارة استفهام فهي التوابع التي تُرجع قيمة منطقيّة، لكنّها ليست بالضّرورة تعيد true أو false، بل غالبًا ما تعيد كائنًا يعبّر عن true أو قيمة الإيجاب. والتوابع التي تنتهي بإشارة مساواة فهي توابع الإسناد، وبالنسبة لهذا النوع من التوابع تُتجاهل القيمة المعادة وتُرجع معاملات التابع بدلًا منها. فيما يلي أسماء التوابع المعبرة عن مختلف العمليات في لغة روبي، كلّ واحدة من هذه العمليات تقبل معاملًا واحدًا فقط. تجد بعد رمز العملية الاستخدام الشهير لها أو اسم العملية. لاحظ أنّك إن غيّرت ما تقوم به كلّ عمليّة فستسبّب لبسًا لدى المستخدم، إذ أنّ المتوقّع من إشارة الجمع أن تجمع، ومن إشارة الطرح أن تطرح، وهكذا. أضف إلى ذلك أنّك لن تتمكّن من تغيير أولويّة أيّ من هذه العمليات. 

+
جمع

-
طرح

*
ضرب

**
قوة

/
قسمة

%
باقي القسمة، أو سلسلة %

&
AND

^
OR الحصرية

>>
إزاحة لليمين

<<
إزاحة لليسار، أو إضافة

==
اختبار المساواة

!=
اختبار عدم المساواة

===
المساواة التامة

=~
مطابقة النمط (ليس خاصًّا بالتعبيرات النظامية فحسب)

!~
عدم مطابقة النمط

<=>
المقارنة والمعروف أيضًا بمعامل سفينة الفضاء

<
أصغر من

<=
أصغر من أو يساوي

>
أكبر من

>=
أكبر من أو يساوي

لأجل تعريف توابع أحادية تغيّر سلوك إشارة الجمع والطرح والضرب والنفي المنطقي، عليك أن تُتبع رمز العملية بالرمز @ مثل: ‎+@‎ أو ‎!@‎ كما في المثال التالي:

class C
 def -@
   puts "you inverted this object"
 end
end
obj = C.new
-obj # "you inverted this object" يطبع

التوابع الأحادية لا تقبل أي معاملات. بالإضافة لذلك يمكن تعريف التوابع التي تستخدم للإشارة إلى العناصر وإسناد قيمٍ إليها بالشّكل [] أو ‎[]=‎ على التوالي وبالترتيب. وكلاهما بالإمكان أن يأخذ معاملًا واحدًا أو أكثر. ويمكن لتابع الإشارة للعناصر ألا يأخذ أيّ معامل. 

class C
 def [](a, b)
   puts a + b
 end
 def []=(a, b, c)
   puts a * b + c
 end
end
obj = C.new
obj[2, 3]     # "5" يطبع
obj[2, 3] = 4 # "10" يطبع

القيم المعادة

تُعيد التوابع بشكل افتراضيّ آخر تعبير برمجيّ يصل إليه التنفيذ في جسم التابع، ففي المثال أعلاه، فالتعبير الأخير (والوحيد) الذي نُفّذ كان عمليّة جمع بسيطة 1+1. ويمكن أيضًا استخدام الكلمة المحجوزة return للتأكيد بأنّ التابع يعيد قيمة.

def one_plus_one
 return 1 + 1
end

كما يمكن استخدام كلمة return لإنهاء تنفيذ التابع دون إكمال التعبيرات البرمجيّة التي تليها.

def two_plus_two
 return 2 + 2
 1 + 1  # لا يتمّ تنفيذ هذا السطر أبدًا
end

لاحظ أنّه - كما أشرنا سابقًا - في حالة توابع الإسناد فإنّ القيمة المعادة تُتجاهل ويعيد التابع بدلًا منها المعامل الذي مُرّر إليه:

def a=(value)
 return 1 + value
end
p(self.a = 5) # تطبع5

أمّا القيمة المعادة فترجع فقط في حالة استدعاء التابع بشكل مباشر:

p send(:a=, 5) # تطبع 6

النطاق

الصيغة النظامية لتعريف تابع:

def my_method
 # ...
end

وبهذه الطريقة تضيف تابعًا إلى صنف class. كما يمكنك تعريف تابع لأغراض صنف معين وذلك بإضافة كلمة class:

class C
 def my_method
   # ...
 end
end

كما يمكن تعريف تابع لكائن آخر. وتستطيع تعريف تابع للصنف نفسه (وليس للكائنات المتفرعة عنه) كالتالي:

class C
 def self.my_method
   # ...
 end
end

وهذه الطريقة تُعد حالة خاصة من إحدى قواعد الصيغة في لغة روبي، وهي القدرة على إضافة تابع لأيّ كائن، وبما أنّ الأصناف بحدّ ذاتها هي نوع من الكائنات فبالإمكان ببساطة إضافة تابع إلى كائن الصنف وفيما يلي صيغة إضافة تابع إلى كائن ما:

greeting = "Hello"
def greeting.broaden
 self + ", world!"
end
greeting.broaden # تعيد "Hello, world!"

كلمة self هي كلمة محجوزة في اللغة تشير إلى الغرض الحالي من وجهة نظر مفسّر اللغة، فهي تسهّل تعريف تابع الصنف في المثال السابق.

def String.hello
 "Hello, world!"
end

تعريف التابع بهذا الشكل يسمى "التابع المنفرد singleton method". فالتابع broaden في المثال السابق موجود في الكائن greeting فحسب، ولن تجده في أيّ متغيّر آخر من نوع السلسلة النصية string.

إعادة التعريف

عندما يصادف مفسّر لغة روبي كلمة def قبل اسم تابع معرّف مسبقًا فإنّه لا يعدّ ذلك خطأ وإنّما يعيد تعريف هذا التابع. وهذا ما يسمّى بإعادة التعريف أو Overriding. إلّا أنّ هذه الإمكانيّة في لغة روبي تُعدّ خطرة ولا يستحسن استخدامها بشكل متكّرر، إلا في حالة توسيع أصناف اللغة الأساسية (core classes)، لأنّها قد تؤدي إلى نتائج غير معروفة. خذ على سبيل المثال تسلسل الأوامر التالي:

>> "43".to_i
=> 43
>> class String
>>   def to_i
>>     42
>>   end
>> end
=> nil
>> "43".to_i
=> 42

هذا سيسبب بكلّ تأكيد تخريبًا لأيّ شيفرة تستخدم التابع to_i من الصنف String الذي يحوّل سلسلة المحارف إلى أرقام.

معاملات التوابع

يمكن للتابع أن يقبل معاملات وتكون مكتوبة على التتالي بعد اسم التابع:

def add_one(value)
 value + 1
end

عند استدعاء التابع add_one يجب على المستخدم أن يمرّر له معاملًا، وهذا المعامل هو متغيّر محليّ ضمن جسم التابع. ففي المثال السابق يضيف التابع واحدًا إلى هذا المعامل الذي مُرّر إليه، ويُعيد الناتج. فإذا أُعطي التابع 1 سيعيد 2. والأقواس المحيطة بالمعاملات اختيارية ويمكن حذفها:

def add_one value
 value + 1
end

وإذا كان لديك أكثر من معامل فافصل بينها بفواصل:

def add_values(a, b)
 a + b
end

فعند الاستدعاء يجب تمرير المعاملات بنفس الترتيب، وبكلمات أخرى يمكن القول أنّ المعاملات حساسة للترتيب.

القيم الافتراضية

يمكن أن تحصل المعاملات على قيم افتراضية:

def add_values(a, b = 1)
 a + b
end

ولا يشترط أن تكون القيمة الافتراضية في بداية أو نهاية المعاملات، إلّا أن المعاملات ذات القيم الافتراضية يجب أن تجمّع معًا، فالمثال التالي مقبول:

def add_values(a = 1, b = 2, c)
 a + b + c
end

أما هذا المثال فسيعطي خطأ في الصيغة SyntaxError

def add_values(a = 1, b, c = 1)
 a + b + c
end

تفكيك المصفوفة يمكنك تفكيك مصفوفة (أوس استخراج القيم منها) باستخدام أقواس مضاعفة في معاملات التابع:

def my_method((a, b))
 p a: a, b: b
end
my_method([1, 2])

هذا المثال يطبع:

{:a=>1, :b=>2} 

ويُتجاهل كلّ عنصر في المصفوفة زائد على عدد المعاملات.

def my_method((a, b))
 p a: a, b: b
end
my_method([1, 2, 3])

وهذا المثال له نفس نتيجة المثال السابق. يمكنك استخدام الرمز * لتحصيل المعاملات المتبقية أيًا كان عددها في المصفوفة، فالمثال التالي يفصل المصفوفة فيسند أول عنصر منها في المعامل الأول، والباقي يسند كمصفوفة في المعامل الثاني:

def my_method((a, *b))
 p a: a, b: b
end
my_method([1, 2, 3])

هذا المثال يطبع:

{:a=>1, :b=>[2, 3]}

أيّ معامل يستجيب للتابع to_ary يمكن استخدامه في عملية التفكيك هذه، فإذا كان لديك كائن آخر تريد أن تفكّكه بنفس الطريقة فعليك أن تعرّف له تابع to_ary الخاص به. يمكنك استخدام أقواس ضمنية لتفكيك مصفوفة مُرّرت كأحد المعاملات، لكن إذا لم يكن المعامل المقابل له من نوع مصفوفة Array فسوف يُسند هذا المعامل إلى المعامل الأول ضمن قوسي التفكيك، بينما تأخذ بقية المعاملات قيمة nil:

def my_method(a, (b, c), d)
 p a: a, b: b, c: c, d: d
end
my_method(1, 2, 3)

هذا المثال يطبع:

{:a=>1, :b=>2, :c=>nil, :d=>3}

كما بإمكانك تضمين عمليات التفكيك داخل بعضها بأي شكل:

def my_method(((a, b), c))
 # ...
end

معاملات المصفوفة \ المصنّف

إضافة الرمز * في بداية اسم المعامل يسبب تحويل جميع المعاملات الإضافية إلى مصفوفة:

def gather_arguments(*arguments)
 p arguments
end
gather_arguments 1, 2, 3 # [1, 2, 3] تطبع

ويجب أن يأتي هذا المعامل في آخر المعاملات الموضعية وعليه أن يسبق أيّ معامل قيم مفتاحيّة. كما يمكن للمصفوفة الناتجة أن تتضمّن مصنِّفًا في نهايتها إذا مُرّر هذا المصنّف عند الاستدعاء بعد جميع المعاملات الموضعي. 

gather_arguments 1, a: 2 # [1, {:a=>2}] تطبع

إلّا أنّ هذه الحالة تحصل فقط عندما لا يعرّف التابع أيّ معاملات من نوع القيم المفتاحية.

def gather_arguments_keyword(*positional, keyword: nil)
p positional: positional, keyword: keyword
end
gather_arguments_keyword 1, 2, three: 3
#=> unknown keyword: three (ArgumentError):تسبب ظهور خطأ
لاحظ أيضًا أنّ استخدام الرمز * وحده يسبب تجاهل أيّ معاملات.
def ignore_arguments(*)
end

معاملات القيم المفتاحية

معاملات القيم المفتاحية تشبه في آلية عملها المعاملات الموضعية ذات القيم الافتراضية:

def add_values(first: 1, second: 2)
 first + second
end

يمكن قبول أيّ عدد من معاملات القيم المفتاحية باستخدام الرمز ** قبل اسم المعامل:

def gather_arguments(first: nil, **rest)
 p first, rest
end
gather_arguments first: 1, second: 2, third: 3
# {:second=>2, :third=>3} تطبع 1 ثم

عند استدعاء تابع باستخدام القيم المفتاحية فمكن لها أن تأتي ضمن أيّ ترتيب، إلّا أنّ المعاملات غير المعرفة مسبقًا ستسبب ظهور خطأ من نوع ArgumentError وفي حال رغبتك باستخدام معاملات قيم مفتاحية مع معاملات موضعية فعليك أن تكتب جميع المعاملات الموضعية قبل أيّ معامل قيمة مفتاحية.

معاملات الكتلة البرمجية

يُميّز معامل الكتلة البرمجيّة باستخدام الرمز &، ويجب أن يكون الأخير ضمن تسلسل المعاملات:

def my_method(&my_block)
 my_block.call(self)
end

في أغلب الأحيان يستخدم هذا النوع من المعاملات لتمرير كتلة برمجيّة إلى تابع آخر:

def each_item(&block)
 @items.each(&block)
end

إذا أردت فقط أن تنفّذ الكتلة البرمجيّة ولن تقوم بالمقابل بالتعديل عليها أو إرسالها إلى تابع آخر، فيفضّل حينها أن تستخدم الكلمة المحجوزة yield دون تحديد معامل معيّن. فالطريقة التالية تكافئ التابع الأوّل في هذه الفقرة:

def my_method
 yield self
end

وهناك فائدة أخرى على صعيد تحسين الأداء عند استخدام yield عوضًا عن الاستدعاء المباشر للكتلة، فعند إسناد معامل كتلة برمجية إلى متغيّر فسيسبّب ذلك إنشاء كائن من نوع Proc والذي سيبّب إيقاف تنفيذ الكتلة مؤقتًا. أمّا عند استخدام yield فلن يُنشأ كائن Proc هذا بالأساس. إذا كانت حاجتك إلى استخدام هذه الكتلة قليلة فبإمكانك حينئذٍ أن تستخدم Proc.new لإنشاء proc من الكتلة البرمجيّة والذي يمكن تمريره إلى التابع. انظر التوثيق الخاصّ بـ Proc.new لمزيد من التفاصيل.

التعامل مع الاستثناءات

تحتوي التوابع على آلية تعامل مع الاستثناءات ضمنية، فلست بحاجة لاستخدام begin - end للتعامل مع الاستثناء. فهذا المثال: 

def my_method
 begin
   # شيفرة قد تسبب ظهور استثناءات
 rescue
   # التعامل مع الاستثناء
 end
end

يمكن كتابته بالشكل التالي:

def my_method
 # شيفرة قد تسبب ظهور استثناء
rescue
 # التعامل مع الاستثناء
end

إذا أردت تفادي استثناء فقط في جزء محدد من التابع فاستخدم حينها begin - end. لمزيد من التفاصيل ارجع إلى الصفحة الخاصة بالتعامل مع الاستثناءات.

المصادر