R: دوال R: الدوال المخصصة والبرمجة الوظيفية

آخر تحديث: 2026-08-26

في الدروس الستة السابقة، كنا نستخدم الدوال المدمجة في لغة R (sum() mean() c()). لكن ما يجعل لغة R قوية حقًّا هو أنه يمكنك كتابة دوالك الخاصة. في هذا الدرس، سنتعرف على «الدوال المخصصة» في لغة R — أي تحويل الأكواد المتكررة إلى أدوات قابلة لإعادة الاستخدام.

لغة R هي لغة برمجة وظيفية، حيث تُعتبر الدوال «عناصر من الدرجة الأولى». وهذا يعني أنه يمكن تعيين الدوال إلى متغيرات، وتمريرها كمعاملات، وإرجاعها كقيم. في هذا الدرس، سنتعرف على جميع ميزات الدوال في لغة R.

1. ما ستتعلمه



2. قصة حول إعادة استخدام الكود

(1) المشكلة: تكرار الكود

تشين هو محلل بيانات يتعين عليه «تحويل بيانات المبيعات إلى تقارير» 10 مرات في اليوم. وفي كل مرة، يكتب نفس الشيء مرارًا وتكرارًا:

R
# Repeat 10 The code for this
sales <- c(120, 150, 80, 200, 175)
total <- sum(sales)
avg <- mean(sales)
top <- max(sales)
cat("Total Sales:", total, "Average:", avg, "Highest:", top, "\n")

10 عمليات نسخ ولصق = 200 سطر من الكود المكرر. إذا اضطررت إلى تغيير المنطق في مكان واحد، فسيتعين عليك تغييره في 10 أماكن — وعاجلاً أم آجلاً، ستفوتك بعض التغييرات.

(2) حل الدوال

R
# Write a function (just write 1 time)
report <- function(x) {
  cat("Total Sales:", sum(x), " Average:", mean(x), " Highest:", max(x), "\n")
}

# Call 10 times (Every time 1 row)
report(c(120, 150, 80, 200, 175))
report(c(80, 90, 100))
report(c(1000, 2000, 3000))

5 أسطر من كود الدالة + 10 أسطر من الاستدعاءات = 15 سطرًا من الكود. ولا يتطلب تغيير المنطق سوى تعديل واحد. هذه هي قوة الدوال.



3. function(){}: القواعد الأساسية

(1) تعريف الدالة

R
# The Simplest Function
greet <- function() {
  cat("Hello, World!\n")
}

# Call
greet()
# Output:Hello, World!

(2) الدوال ذات المعلمات

R
# Single parameter
greet <- function(name) {
  cat("Hello,", name, "!\n")
}

greet("Alice")
# Output:Hello, Alice !

# Multiple parameters
greet_full <- function(name, age) {
  cat("I am", name, ",This year", age, "yrs\n")
}

greet_full("Bob", 25)
# Output:I am Bob ,This year 25 yrs

(3) الاختلافات بين الدوال في لغتي R و Python

ميزة R بايثون
التعريف function(x) { ... } def func(x):
كتلة كود {} المسافة البادئة
قيمة الإرجاع يتم إرجاع السطر الأخير تلقائيًا يتطلب return
الوثائق تبدأ بـ #' (roxygen2) docstring
إعلان النوع لا يوجد (نوع ديناميكي) اختياري (بايثون 3.5+)
R
# R the special:The last line wraps automatically(Not necessary return)
add <- function(a, b) {
  a + b   # The last line wraps automatically
}

result <- add(3, 4)
result
# [1] 7


4. 4 أنواع من المعلمات

(1) المعلمات الموضعية مقابل المعلمات المسماة

R
# Function Definition:3 parameter
power <- function(base, exp, mod) {
  result <- (base ^ exp) %% mod
  return(result)
}

# Location Parameters(In order)
power(2, 10, 1000)
# [1] 24  ← 2^10=1024, 1024 %% 1000 = 24

# Named parameters(By Name,Out of order)
power(mod = 1000, base = 2, exp = 10)
# [1] 24  ← The same result
💡 نصيحة: عندما تكون هناك معلمات متعددة، يُنصح باستخدام المعلمات المسماة لتحسين قابلية القراءة.

(2) المعلمات الافتراضية

R
# The second parameter has a default value.
power <- function(base, exp = 2, mod = 1000) {
  result <- (base ^ exp) %% mod
  return(result)
}

# Do not pass a second parameter,Use the default exp = 2
power(10)
# [1] 100  ← 10^2=100, 100 %% 1000 = 100

# Partial Parameter Passing
power(10, mod = 100)
# [1] 0  ← 10^2=100, 100 %% 100 = 0

(3) قواعد ترتيب المعلمات

قواعد مطابقة المعلمات في لغة R:

100%
graph TB
    A[Function Call power 10 exp=3] --> B[1. Exact Match for Named Parameters]
    B --> C[2. Prefix Matching]
    C --> D[3. Location Matching]
    
    style A fill:#fff3cd
    style B fill:#d4edda
    style C fill:#d4edda
    style D fill:#d4edda
  1. المطابقة التامة: exp = 3 (الأكثر تحديدًا)
  2. مطابقة البادئة: e = 3exp (عدد أقل من ضغطات المفاتيح)
  3. مطابقة الموضع: المعلمة الأولى غير المسماة → base (الأقل أمانًا)
💡 نصيحة: استخدم المطابقات الدقيقة في كود الإنتاج (exp = 3). قد لا يوفر كتابة بضعة أحرف إضافية الكثير من الوقت، لكنه يساعد في تجنب الغموض.

(4) المعلمات المتغيرة ...

... تعني «أي عدد من الوسيطات» وتُستخدم عادةً في الدوال المُغلفة:

R
# Use ... to accept any parameters
my_sum <- function(...) {
  args <- list(...)
  result <- sum(unlist(args))
  cat("Summed up", length(args), "parameter:", result, "\n")
  return(result)
}

my_sum(1, 2, 3)
# Summed up 3 parameter: 6

my_sum(10, 20, 30, 40, 50)
# Summed up 5 parameter: 150
💡 نصيحة: يُستخدم ... بشكل شائع جدًّا عند تغليف دوال أخرى (مثل عندما تقوم دالة مخصصة my_mean() بتغليف بعض معلمات mean()).



5. 3 أنواع قيم الإرجاع

(1) يتم توزيع السطر الأخير تلقائيًا

التعبير الأخير في دالة R يُرجع تلقائيًا:

R
add <- function(a, b) {
  a + b   # Last line,Auto-Return
}

add(3, 4)
# [1] 7

(2) صريح return()

R
# Use return() to return early
check_age <- function(age) {
  if (age < 0) {
    return("Age of Invalidity")  # Return Early
  }
  if (age >= 18) {
    return("Adulthood")
  }
  "Minor"  # Implicit Return
}

check_age(-5)   # [1] "Age of Invalidity"
check_age(20)   # [1] "Adulthood"
check_age(15)   # [1] "Minor"

(3) invisible(): إخفاء قيمة الإرجاع

invisible() منع دالة من الطباعة التلقائية لقيمة الإرجاع الخاصة بها (مع السماح بتعيينها):

R
# Ordinary Functions
add_print <- function(a, b) {
  a + b  # It will print automatically
}

add_print(3, 4)
# [1] 7  ← Automatic Printing

# invisible Function
add_silent <- function(a, b) {
  invisible(a + b)  # Do not print automatically
}

add_silent(3, 4)
# No output  ← Do not print

# But it can still be assigned a value
result <- add_silent(3, 4)
result
# [1] 7  ← You can see it after the assignment.
💡 نصيحة: <- تعيين قيمة باستخدام invisible()x <- 5 لا يعرض النتيجة.



6. الدوال المجهولة: تعبيرات لامدا

يدعم لغة R الدوال المجهولة (الدوال التي لا تحمل أسماء):

R
# Ordinary Functions
square <- function(x) x ^ 2
square(5)
# [1] 25

# Anonymous Functions(Use directly)
(function(x) x ^ 2)(5)
# [1] 25

# Common Use Cases for Anonymous Functions:lapply/sapply
numbers <- list(1, 2, 3, 4, 5)
result <- lapply(numbers, function(x) x ^ 2)
result
# [[1]] 1
# [[2]] 4
# [[3]] 9
# [[4]] 16
# [[5]] 25
💡 نصيحة: تُعد الدوال المجهولة شائعة جدًّا في عائلة دوال apply — وسنتناول هذا الموضوع بالتفصيل في الدرس 8.



7. الدوال ككائنات (جوهر البرمجة الوظيفية في لغة R)

تُعتبر الدوال في لغة R «دوالًا من الدرجة الأولى»، مما يعني أن الدوال يمكنها:

(1) تعيين قيمة لمتغير

R
f <- function(x) x * 2
g <- f  # A function has two names

f(5)  # [1] 10
g(5)  # [1] 10  ← Equivalent

(2) يتم تمريرها كمعلمة

R
# Functions that take other functions as arguments
apply_twice <- function(f, x) {
  f(f(x))
}

# Passing Different Functions
apply_twice(function(x) x + 1, 5)  # 5+1+1=7
apply_twice(function(x) x * 2, 5)  # 5*2*2=20

(3) كقيمة مرتجعة

R
# A function that returns a function(Closure)
make_multiplier <- function(k) {
  function(x) x * k
}

# Creating Functions with Different Scales
double <- make_multiplier(2)
triple <- make_multiplier(3)

double(5)  # [1] 10
triple(5)  # [1] 15
💡 نصيحة: تُعد الدوال التي تُستخدم كقيم إرجاع مفهومًا أساسيًّا في «البرمجة الوظيفية» بلغة R، وتستفيد العديد من الدوال في مكتبة tidyverse (map() reduce()) من هذا المفهوم.



8. do.call(): الاستدعاء الديناميكي

do.call() توسيع قائمة/متجه إلى معلمات دالة:

R
# Regular Call
sum(1, 2, 3, 4, 5)
# [1] 15

# do.call Call(The parameter is a list)
args <- list(1, 2, 3, 4, 5)
do.call(sum, args)
# [1] 15

# Practical Application:Dynamic Creation data.frame
col_names <- c("name", "age", "score")
col1 <- c("Alice", "Bob", "Charlie")
col2 <- c(25, 30, 35)
col3 <- c(95, 88, 92)

df <- do.call(data.frame, list(
  name = col1,
  age = col2,
  score = col3
))
print(df)
#       name age score
# 1    Alice  25    95
# 2      Bob  30    88
# 3 Charlie  35    92


9. النطاق: أين تكون المتغيرات صالحة؟

تستخدم لغة R نطاق الكلمات:

R
# Global Variables
x <- 10

# Global variables can be accessed within a function.
read_x <- function() {
  cat("x =", x, "\n")  # Read the whole picture x
}
read_x()
# x = 10

# However, local variables within a function do not affect the global scope.
set_local <- function() {
  x <- 999  # Local variables
  cat("Inside a function x =", x, "\n")
}
set_local()
# Inside a function x = 999
cat("Global x =", x, "\n")
# Global x = 10  <- Global x unchanged
⚠️ ملاحظة: بشكل افتراضي، لا يمكن لدوال R تعديل المتغيرات العالمية (على عكس لغة Python global). لتعديلها، استخدم <<- (ولكن لا يُنصح بذلك).



10. مثال كامل: وظائف أداة تقرير المبيعات

فيما يلي مثال على مسار عمل كامل يربط بين جميع الميزات الوظيفية التي تم تناولها في هذا الدرس.

▶ مثال: وظائف أداة تقرير المبيعات

R 📖 للعرض فقط
# ============================================
# Sales Report Tool Functions
# Features:Encapsulate Common Analytical Logic,Reuse
# ============================================

# 1. Basic Statistical Functions(Back to List)
basic_stats <- function(x) {
  list(
    n = length(x),
    sum = sum(x),
    mean = round(mean(x), 2),
    median = median(x),
    sd = round(sd(x), 2),
    min = min(x),
    max = max(x)
  )
}

# 2. Formatted Output Functions(invisible Back)
print_report <- function(name, x) {
  stats <- basic_stats(x)
  cat("=================================\n")
  cat("Sales Report:", name, "\n")
  cat("=================================\n")
  cat("Sample size:", stats$n, "\n")
  cat("Total Sales:", stats$sum, "\n")
  cat("Average Sales:", stats$mean, "\n")
  cat("Median:", stats$median, "\n")
  cat("Standard Deviation:", stats$sd, "\n")
  cat("Minimum:", stats$min, " / Maximum:", stats$max, "\n")
  invisible(stats)  # Silent Return
}

# 3. Classification Function(Vectorization)
classify_sales <- function(x) {
  ifelse(x < 100, "Low",
  ifelse(x < 200, "Mid", "High"))
}

# 4. Main Program
sales_data <- list(
  "Beijing" = c(120, 150, 80, 200, 175),
  "Shanghai" = c(95, 110, 130, 145, 160),
  "Shenzhen" = c(200, 220, 180, 250, 300)
)

# Generate reports for each branch
for (city in names(sales_data)) {
  sales <- sales_data[[city]]
  print_report(city, sales)
  
  # Batch Classification Using Anonymous Functions
  levels <- sapply(sales, function(x) classify_sales(x))
  cat("\nCategory Summary:\n")
  print(table(levels))
  cat("\n")
}

# 5. Use do.call to merge all branches
all_sales <- do.call(c, sales_data)
cat("=== Company-wide Summary ===\n")
print_report("Company-wide", all_sales)
44 سطر من الكود المنطقي (تجاوز الحد 40, للعرض فقط)

النتائج المتوقعة (مقتطف):

TEXT 📖 للعرض فقط
=================================
Sales Report: Beijing
=================================
Sample size: 5
Total Sales: 725
Average Sales: 145
Median: 150
Standard Deviation: 47.7
Minimum: 80 / Maximum: 200

Category Summary:
levels
Mid High 
3  2 

=================================
Sales Report: Shanghai
=================================
...


❓ أسئلة شائعة

س متى ينبغي استخدام الدوال المجهولة؟
ج تُستخدم في الغالب في دوال مثل lapply()، sapply()، map()، إلخ.

📖 ملخص


📝 تمارين

  1. المشكلة الأساسية: اكتب دالة celsius_to_fahrenheit(c) تقوم بتحويل درجات مئوية إلى درجات فهرنهايت (الصيغة: F = C × 9/5 + 32). اختبر الدالة بقيم الإدخال 0 و25 و100، وتأكد من أن قيم الإخراج هي 32 و77 و212.

  2. المسألة الأساسية: اكتب دالة circle_area(r) تحسب مساحة الدائرة (pi * r^2)، مع تقريب النتيجة إلى رقمين عشريين. اختبر الدالة بقيم r = 1، 2، 5.

  3. المشكلة الأساسية: اكتب دالة greet(name = "World", greeting = "Hello") ذات معلمات افتراضية تُخرج "Hello, World!" أو "Hi, Alice!"، واختبر ثلاث طرق مختلفة لاستدعائها.

  4. مشكلة متقدمة: اكتب دالة analyze_scores(scores) تأخذ متجهًا من الأعداد وتُرجع قائمة تحتوي على mean median max min pass_rate معدلات النجاح، باستخدام invisible() لمنع الطباعة التلقائية. بعد استدعاء الدالة، استخدم result$pass_rate للوصول إلى معدلات النجاح.

  5. التحدي: اكتب دالة تُرجع دالة make_power(n) تُرجع function(x) x^n. استخدم make_power(2) لإنشاء square، واستخدم make_power(3) لإنشاء cube، واختبر square(5) = 25 وcube(3) = 27. احفظ لقطة شاشة.

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%