Menu
flag Ar iconالعربيةdown icon

معامل الأنبوب في R: ‏%>% والأنبوب الأصلي |>

ماذا يعني %>%، وكيف يعمل الأنبوب الأصلي |> في R الأساسية، وقواعد كل منهما ورموزه النائبة، وأيهما تستخدم في الشيفرة الجديدة.

تحتوي هذه الصفحة على محررات قابلة للتشغيل - حرّر، شغّل، وشاهد النتيجة فوراً.

المشكلة التي تحلها الأنابيب

يأخذ الأنبوب القيمة التي تسبقه ويمررها إلى استدعاء الدالة الذي يليه - فالتعبير x |> f() يعني f(x). ويبدو ذلك إعادة كتابة تافهة حتى تسلسل ثلاث خطوات أو أربعًا. فبدون أنبوب، تتداخل التحويلات متعددة الخطوات، وتُقرأ الاستدعاءات المتداخلة بترتيب معكوس: إذ يقع أول ما يحدث في الموضع الأعمق.

فلقراءة round(sqrt(x), 1) تبدأ من المنتصف (x)، وتتجه نحو الخارج (sqrt ثم round)، وتتتبع أي , 1) ختامية تخص أي استدعاء. وعند ثلاثة مستويات يكون ذلك مزعجًا؛ وعند خمسة مستويات، مع أفعال إطارات البيانات وعدة وسائط لكل منها، يصير صعب المتابعة فعلًا. والبديل الذي يلجأ إليه الناس - حفظ كل خطوة وسيطة في tmp1 وtmp2 وtmp3 - يزحم مساحة العمل بأسماء لا يحتاجها أحد.

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

الأنبوب الأصلي |>

منذ الإصدار R 4.1، صار الأنبوب مدمجًا في اللغة نفسها - دون حاجة إلى حزمة. ويدرج |> القيمة السابقة وسيطًا أول للاستدعاء التالي:

فالتعبير c(1, 4, 9, 16) |> sqrt() |> round(1) هو بالضبط round(sqrt(c(1, 4, 9, 16)), 1) - إذ تعيد R حرفيًا كتابة أحدهما إلى الآخر أثناء التحليل، فلا تكلفة زمن تشغيل على الإطلاق. لكن الشيفرة صارت الآن تُقرأ وصفةً، خطوة تلو خطوة: البيانات، ثم كل تحويل بترتيبه. وهذا يعمل مع أي دالة لا مع أدوات البيانات وحدها - فالتعبير mtcars |> head(3) هو R أساسية بحتة من أوله إلى آخره.

ولاحظ كم تلائم الأنابيب الدوالَ التي يكون معاملها الأول هو "البيانات": فالدوال head وsort وunique وsummary وكل أفعال dplyr مصممة بهذه الطريقة، ولهذا تبدو خطوط المعالجة طبيعية جدًا في R.

قواعد المعامل |>

الأنبوب الأصلي صارم عن قصد. وثلاث قواعد تغطي كل شيء تقريبًا:

1. يجب أن تكون الخطوة التالية استدعاءً بأقواس. فالتعبير x |> sqrt خطأ نحوي؛ ويجب أن تكتب x |> sqrt():

c(1, 4, 9) |> sqrt     # Error: The pipe operator requires a function call as RHS
c(1, 4, 9) |> sqrt()   # correct

2. تستقر القيمة في موضع الوسيط الأول. وتبقى الوسائط الإضافية التي تكتبها في مواضعها بعده - فالتعبير x |> round(1) هو round(x, 1).

3. ولتستقر في أي موضع آخر، استخدم الرمز النائب _ مع وسيط مسمى (‏R 4.2 فما فوق). والحالة الكلاسيكية: أن lm تأخذ صيغة أولًا والبيانات ثانيًا، فتمرير إطار بيانات إليها بالأنبوب يحتاج إلى data = _:

ويمكن أن يظهر الرمز _ مرة واحدة، وبوصفه وسيطًا مسمى فقط. وحين يكون ذلك مقيدًا أكثر مما ينبغي، مرر بالأنبوب إلى دالة مجهولة - فهي تستطيع وضع القيمة حيث تشاء:

فالدالة المجهولة مغلفة بأقواس ثم مستدعاة بالقوسين () - وهذا يبقي القاعدة الأولى محققة. وإذا وجدت نفسك تفعل ذلك كثيرًا في خط معالجة واحد، فتلك الخطوة تريد على الأرجح أن تصير دالة مسماة.

أنبوب magrittr ‏%>%

قبل أن تملك اللغة أنبوبًا، وفّرت حزمة magrittr واحدًا، وصار %>% توقيع شيفرة tidyverse - إذ يمنحك تحميل dplyr إياه تلقائيًا. وستراه في كل درس ونص برمجي لـ dplyr كُتب في العقد الأخير تقريبًا:

library(dplyr)

mtcars %>%
  filter(mpg > 30) %>%
  select(mpg, wt)

ولأن %>% يأتي من حزمة، فإن هذه الأمثلة غير قابلة للتشغيل في جلسة R مجردة - والرسالة Error: could not find function "%>%" هي بالضبط ما تحصل عليه إن نسيت library(dplyr) (أو library(magrittr)).

والسلوك في الحالة الشائعة مطابق للمعامل |>: تمرير القيمة السابقة إلى الوسيط الأول في الاستدعاء التالي. لكن magrittr أكثر تساهلًا:

c(1, 4, 9) %>% sqrt                 # bare function name - no parentheses needed

mtcars %>% lm(mpg ~ wt, data = .)   # . placeholder, usable anywhere

c(-2, 3) %>% { max(abs(.)) }        # . can even appear several times, in nested calls

‏|> مقابل %>%: الفروق التي تهم

بالنسبة إلى خط معالجة يعتمد الوسيط الأول - وهو الغالبية العظمى من الشيفرة الواقعية - يمكن تبادل الاثنين. والفروق تقيم في الحواف:

  • التوفر: المعامل |> جزء من R الأساسية 4.1 فما فوق؛ بينما يحتاج %>% إلى magrittr (عبر dplyr عادة).
  • الأقواس: يطلب |> الصيغة sqrt()؛ بينما يقبل %>% الاسم المجرد sqrt.
  • الرمز النائب: يستخدم |> الرمز _ مرة واحدة وبوصفه وسيطًا مسمى فقط (‏4.2 فما فوق)؛ بينما يستخدم %>% الرمز . في أي موضع وبأي عدد من المرات، بما في ذلك داخل الاستدعاءات المتداخلة.
  • السرعة: يُعاد كتابة |> وقت التحليل إلى الاستدعاء المتداخل - بتكلفة صفرية. أما %>% فهو استدعاء دالة اعتيادي بتكلفة إضافية صغيرة (ومهملة عمليًا).

ولا شيء هنا يجعل %>% خاطئًا - فهو مجرّب في الميدان وأكثر مرونة قليلًا. لكن كل ما يفعله زيادة على |> يمكن تحقيقه بدالة مجهولة، والأنبوب الأساسي لا يحتاج إلى أي اعتمادية.

أيهما ينبغي أن تستخدم؟

للشيفرة الجديدة: استخدم |>. فهو جزء من اللغة، ويعمل أينما عمل الإصدار R 4.1 فما فوق، ولا يحتاج إلى حزمة، وصرامته تُقرأ ميزة - إذ تبقى خطوط المعالجة بسيطة أو يُعاد تشكيلها إلى دوال مسماة.

لكن يجب أن تكون قادرًا على قراءة %>% بطلاقة، لأن قواعد شيفرة tidyverse وإجابات Stack Overflow ومعظم كتب R المكتوبة منذ 2014 مليئة به. والفرق التي لديها أسلوب dplyr قائم كثيرًا ما تبقي على %>% حفاظًا على الاتساق، وذلك قرار معقول - وأسوأ اختيار هو خلط المعاملين في ملف واحد. وأيًا كان ما تكتبه، فثمة عرف ينتقل معه: في خطوط المعالجة الطويلة، ضع كل خطوة في سطر مستقل مع الأنبوب في نهاية السطر، كي تُقرأ الوصفة قائمةَ خطوات.

وينطبق هنا الذوق نفسه المنطبق على عائلة apply: فالأنابيب للتحويلات الخطية خطوة بعد خطوة. وحين تتفرع حسبة ما أو تحتاج إلى نتائج وسيطة مرتين، أسند متغيرًا حسن التسمية بدل إجبار كل شيء على المرور في سلسلة واحدة.

ما تخرج به

  • يمرر الأنبوب القيمة السابقة إلى الاستدعاء التالي: فالتعبير x |> f() |> g() هو g(f(x)) مكتوبًا بالترتيب الذي يحدث به.
  • المعامل |> جزء من R الأساسية (‏4.1 فما فوق): يتطلب أقواسًا، ويستهدف الوسيط الأول، ويستخدم الرمز النائب _ مع وسيط مسمى (‏4.2 فما فوق).
  • المعامل %>% من magrittr وtidyverse: يسمح بالأسماء المجردة، ويقبل الرمز النائب . في أي موضع - لكنه يحتاج إلى حزمة.
  • فضّل |> في الشيفرة الجديدة؛ واقرأ %>% في كل مكان آخر دون أن يرف لك جفن.
  • تلائم الأنابيب الوصفات الخطية؛ أما المنطق المتفرع فيستحق متغيرات مسماة.

التالي: الحزم - من أين يأتي فعلًا كل من %>% وdplyr وسائر امتدادات R العشرين ألفًا.

الأسئلة الشائعة

ماذا يعني %>% في R؟

المعامل %>% هو أنبوب magrittr: يأخذ القيمة التي تسبقه ويمررها وسيطًا أول للاستدعاء الذي يليه، فيكون x %>% f() %>% g() بمعنى g(f(x)). وهو يأتي من حزمة magrittr ويُحمَّل تلقائيًا مع dplyr ومنظومة tidyverse - وهو ليس جزءًا من R الأساسية.

هل أحتاج إلى حزمة لاستخدام |> في R؟

لا. فالمعامل |> هو الأنبوب الأصلي المدمج في R الأساسية منذ الإصدار 4.1 - ويعمل في جلسة R عادية دون تثبيت أي شيء. أما %>% فهو الذي يحتاج إلى حزمة (‏magrittr أو أي شيء يعيد تصديره مثل dplyr).

ما الفرق بين |> و%>% في R؟

كلاهما يمرر القيمة السابقة إلى الاستدعاء التالي. لكن |> جزء من R الأساسية، ويتطلب أقواسًا صريحة (x |> sqrt())، ويستخدم الرمز النائب _ مع وسيط مسمى فقط. أما %>% فيحتاج إلى magrittr، ويقبل أسماء الدوال المجردة (x %>% sqrt)، ويمكن لرمزه النائب . أن يظهر في أي موضع وبأي عدد من المرات. والسلوك متطابق في حالة الوسيط الأول الشائعة.

Coddy programming languages illustration

تعلّم البرمجة مع Coddy

ابدأ الآن