تعريف الدالة واستدعاؤها
الدالة في R قيمة، مثل الرقم أو السلسلة النصية. وتبني واحدة بالكلمة المفتاحية function وتثبّتها على اسم بالرمز <-، بالطريقة نفسها التي تخزّن بها أي شيء آخر في متغير:
والأجزاء هي: function(x) يعلن معاملًا واحدًا اسمه x، والأقواس المعقوفة تحتوي الجسم، واستدعاء square(4) ينفّذ الجسم مع ضبط x على القيمة 4. وللجسم ذي السطر الواحد تكون الأقواس المعقوفة اختيارية - فالتعبير square <- function(x) x^2 هو الدالة نفسها - لكن الأقواس أفضل للقراءة حالما يكبر الجسم.
ولأن الدالة ليست إلا قيمة، ينطبق عليها كل ما تعرفه عن المتغيرات: إذ يمكنك إعادة إسنادها، أو تمريرها إلى دالة أخرى، أو تخزينها في قائمة. وتلك الحقيقة وحدها تحرّك نصف R الاصطلاحية، بما في ذلك عائلة apply بأكملها.
القيم المعادة: آخر تعبير هو الفائز
تعيد دالة R ما ينتجه آخر تعبير قيّمته. ولا وجود لتعليمة return إلزامية:
والدالة return() موجودة فعلًا، وتعمل كما تتوقع - إذ توقف الدالة فورًا وتعيد وسيطها. وR الاصطلاحية تحفظها لـ الخروج المبكر: أي الانسحاب في المستهل حين يجعل مدخل ما بقية العمل بلا جدوى. اقرنها بفحوص if-else:
وكتابة return(a / b) في السطر الأخير لن تكون خطأ، بل مجرد ضجيج. احفظ return() لحالات الخروج المبكر، ودع آخر تعبير يتحدث عن المسار الاعتيادي.
القيم الافتراضية والوسائط المسماة
يمكن للمعامل أن يحمل قيمة افتراضية، تُعلن بالرمز = في قائمة المعاملات. ومن يرضى بالقيمة الافتراضية من المستدعين يحذف الوسيط ببساطة:
ويعرض الاستدعاء الثالث النصف الآخر من القصة: الوسائط المسماة. فحين تكتب name = "Ada" في موضع الاستدعاء، يكف الموضع عن الأهمية - إذ تطابق R بالاسم أولًا، ثم تملأ ما تبقى من المعاملات بالموضع. وتنبثق عن ذلك قاعدتان عمليتان:
- ضع المعاملات المطلوبة أولًا وذات القيم الافتراضية بعدها، كي تبقى الاستدعاءات الموضعية طبيعية.
- وفي موضع الاستدعاء، سمِّ أي وسيط لا يتضح من موضعه. فالتعبير
greet("Ada", punctuation = "?")أوضح من إجبار القارئ على عدّ الفواصل.
تمرير الوسائط عبر النقاط الثلاث ...
المعامل الخاص ... (ويُنطق "dots") يعني "أي عدد من الوسائط الإضافية". والدالة التي تعلن ... تستطيع قبول وسائط لم تسردها قط وتمريرها إلى دالة أخرى:
فالدالة shout() لا تعرف كم كلمة تمرر ولا تعبأ بذلك - بل تسلّمها كلها إلى paste(). ولاحظ أن punctuation يأتي بعد النقاط: فأي معامل يُعلن بعد ... لا يمكن ضبطه إلا بالاسم، وهو بالضبط ما تريده للخيارات. ونمط التمرير هذا موجود في كل مكان في R الأساسية - فالتعبير sapply(x, round, digits = 2) يعمل لأن sapply يمرر digits = 2 إلى round عبر النقاط الثلاث الخاصة به.
وثمن النقاط هو أن الأخطاء المطبعية تمر بصمت: فأخطئ في كتابة punctuation لتصير punctation فتُبتلع في ... بدل أن تطلق خطأ. أبقِ الدوال المستخدمة للنقاط صغيرة.
الدوال المجهولة
حين لا تحتاج إلى الدالة إلا مرة واحدة - وهو ما يحدث نمطيًا حين تكون وسيطًا لدالة أخرى - يمكنك تخطي الاسم كليًا:
يؤدي السطران الشيء نفسه. فالصيغة function(x) x^2 هي الكتابة الكلاسيكية؛ والصيغة \(x) x^2 هي الاختصار المضاف في الإصدار R 4.1، حيث يحل الرمز \ محل الكلمة function. وهما متطابقان في كل شيء عدا ضغطات المفاتيح - فالاختصار ليس نوعًا مختلفًا من الدوال.
استخدم دالة مجهولة حين تكون العملية سطرًا واحدًا مرتبطًا بموضع استدعاء واحد. وفي اللحظة التي تلصق فيها التعبير نفسه في موضعين، أو حين يتجاوز سطرًا واحدًا، امنحه اسمًا - فالدالة المسماة قابلة للاختبار وموثقة ذاتيًا.
النطاق: ما تستطيع الدالة رؤيته
تستخدم R النطاق المعجمي: فحين تحتاج دالة إلى متغير لا تجده بين معاملاتها وإسناداتها المحلية، تبحث عنه في البيئة التي عُرِّفت فيها الدالة - لا التي استُدعيت فيها. وعمليًا يعني ذلك أن الدالة تستطيع قراءة متغيرات من النص البرمجي المحيط بها:
والوجه الآخر: أن الإسناد داخل الدالة لا يتسرب خارجها أبدًا. فالتعبير rate <- 0.5 داخل change_inside ينشئ متغيرًا محليًا جديدًا يموت حين تعود الدالة؛ ويظل rate الخارجي يطبع 0.1. وهذه ميزة - إذ لا تستطيع الدوال أن تدهس مساحة عملك عرضًا - لكنها تفاجئ المبتدئين الذين يتوقعون أن يثبت الإسناد الداخلي. وإذا احتاجت دالة إلى تسليم نتيجة للعالم الخارجي، فأعِدها وأسندها في موضع الاستدعاء. (وهناك المعامل <<- الذي يمتد إلى الخارج؛ عامله رائحة شيفرة سيئة حتى تحتاج إليه فعلًا.)
مثال مشروح
إليك النمط الذي يؤتي ثماره: تلاحظ أنك تكرر ثلاثة أسطر من التحليل، فتغلّفها في دالة وتعيد استخدامها. لنقل إنك تواصل تلخيص متجهات من القياسات:
كل ما في هذه الصفحة موجود هناك: معامل digits بقيمة افتراضية، واستدعاء مسمى يتجاوزها، وتعبير أخير (round(out, digits)) يتولى الإعادة. والدالة تُقرأ جملةً، وتعمل على أي متجه عددي، وتقيم في موضع واحد حين تقرر إضافة الوسيط.
ما تخرج به
- الصيغة
name <- function(args) { body }تنشئ دالة؛ وآخر تعبير مقيَّم هو قيمتها المعادة. - الدالة
return()للخروج المبكر، لا للسطر الأخير الاعتيادي. - تُعلن القيم الافتراضية بالرمز
=في قائمة المعاملات؛ وتجعل الوسائط المسماة مواضع الاستدعاء مستقلة عن الترتيب ومقروءة. - تقبل النقاط
...أي وسائط إضافية وتمررها - وهي قوية لكنها تخفي الأخطاء المطبعية. - الصيغتان
function(x)و\(x)شيء واحد؛ واستخدم الدوال المجهولة للأسطر المفردة العابرة. - تقرأ الدوال المتغيرات من حيث عُرِّفت؛ والإسنادات داخلها تبقى داخلها.
التالي: عائلة apply - الدوال التي تأخذ دوالك أنت وتشغّلها عبر متجهات وقوائم كاملة.
الأسئلة الشائعة
كيف تنشئ دالة في R؟
أسند كائن function إلى اسم: square <- function(x) { x^2 }. وتوضع المعاملات بين القوسين، ويوضع الجسم بين الأقواس المعقوفة، وتستدعيها بالصيغة square(4). وآخر تعبير مقيَّم هو ما تعيده الدالة.
هل تحتاج إلى return() في R؟
لا. فدالة R تعيد تلقائيًا قيمة آخر تعبير تقيّمه. والدالة return() موجودة، لكن R الاصطلاحية تحفظها للخروج المبكر - أي الانسحاب في مستهل الدالة حين يكون مدخل ما غير صالح أو حين تُعالج حالة حدية.
ما هي الدالة المجهولة في R؟
دالة تعرّفها دون أن تمنحها اسمًا، وتمررها عادة مباشرة إلى دالة أخرى: sapply(x, function(v) v^2). ومنذ الإصدار R 4.1 صار هناك أيضًا الاختصار \(v) v^2 الذي يعني الشيء نفسه تمامًا بضغطات مفاتيح أقل.