R: دوال R: الدوال المخصصة والبرمجة الوظيفية
آخر تحديث: 2026-08-26
في الدروس الستة السابقة، كنا نستخدم الدوال المدمجة في لغة R (
sum()mean()c()). لكن ما يجعل لغة R قوية حقًّا هو أنه يمكنك كتابة دوالك الخاصة. في هذا الدرس، سنتعرف على «الدوال المخصصة» في لغة R — أي تحويل الأكواد المتكررة إلى أدوات قابلة لإعادة الاستخدام.
لغة R هي لغة برمجة وظيفية، حيث تُعتبر الدوال «عناصر من الدرجة الأولى». وهذا يعني أنه يمكن تعيين الدوال إلى متغيرات، وتمريرها كمعاملات، وإرجاعها كقيم. في هذا الدرس، سنتعرف على جميع ميزات الدوال في لغة R.
1. ما ستتعلمه
- function(){} الصيغة الأساسية
- 4 أنواع من المعلمات (المعلمات الموضعية، والمعلمات المسماة، والمعلمات الافتراضية، والمعلمات المتغيرة ...)
- 3 أنواع من قيم الإرجاع (القيمة المرجعة / السطر الأخير / غير مرئية)
- الدوال المجهولة (لامدا)
- الدوال ككائنات (التعيين، التمرير كمعاملات، الإرجاع)
- استدعاءات do.call() المجمعة
- تدريب عملي: كتابة دالة المنفعة الكاملة
2. قصة حول إعادة استخدام الكود
(1) المشكلة: تكرار الكود
تشين هو محلل بيانات يتعين عليه «تحويل بيانات المبيعات إلى تقارير» 10 مرات في اليوم. وفي كل مرة، يكتب نفس الشيء مرارًا وتكرارًا:
# 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) حل الدوال
# 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) تعريف الدالة
# The Simplest Function
greet <- function() {
cat("Hello, World!\n")
}
# Call
greet()
# Output:Hello, World!
(2) الدوال ذات المعلمات
# 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 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) المعلمات الموضعية مقابل المعلمات المسماة
# 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) المعلمات الافتراضية
# 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:
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
- المطابقة التامة:
exp = 3(الأكثر تحديدًا) - مطابقة البادئة:
e = 3→exp(عدد أقل من ضغطات المفاتيح) - مطابقة الموضع: المعلمة الأولى غير المسماة →
base(الأقل أمانًا)
exp = 3). قد لا يوفر كتابة بضعة أحرف إضافية الكثير من الوقت، لكنه يساعد في تجنب الغموض.
(4) المعلمات المتغيرة ...
... تعني «أي عدد من الوسيطات» وتُستخدم عادةً في الدوال المُغلفة:
# 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 يُرجع تلقائيًا:
add <- function(a, b) {
a + b # Last line,Auto-Return
}
add(3, 4)
# [1] 7
(2) صريح return()
# 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() منع دالة من الطباعة التلقائية لقيمة الإرجاع الخاصة بها (مع السماح بتعيينها):
# 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 الدوال المجهولة (الدوال التي لا تحمل أسماء):
# 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) تعيين قيمة لمتغير
f <- function(x) x * 2
g <- f # A function has two names
f(5) # [1] 10
g(5) # [1] 10 ← Equivalent
(2) يتم تمريرها كمعلمة
# 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) كقيمة مرتجعة
# 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
map() reduce()) من هذا المفهوم.
8. do.call(): الاستدعاء الديناميكي
do.call() توسيع قائمة/متجه إلى معلمات دالة:
# 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 نطاق الكلمات:
# 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
global). لتعديلها، استخدم <<- (ولكن لا يُنصح بذلك).
10. مثال كامل: وظائف أداة تقرير المبيعات
فيما يلي مثال على مسار عمل كامل يربط بين جميع الميزات الوظيفية التي تم تناولها في هذا الدرس.
▶ مثال: وظائف أداة تقرير المبيعات
# ============================================
# 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)
النتائج المتوقعة (مقتطف):
=================================
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()، إلخ.📖 ملخص
- تعريف دالة R
function(params) { code }، يتم إرجاع السطر الأخير تلقائيًا - 4 أنواع من المعلمات: الموضعية، والمسماة (
name = value)، والافتراضية (name = default)، والمتغيرة (...) - 3 قيم إرجاع: السطر الأخير (تلقائي)،
return()(صريح)،invisible()(صامت، بدون إخراج) - تُعد الدوال المجهولة
function(x) x^2شائعة للغاية فيlapplyوsapply - لغة R هي لغة وظيفية، وتُعتبر الدوال «عناصر من الدرجة الأولى»: حيث يمكن تعيينها، وتمريرها كمعاملات، وإرجاعها (الإغلاقات).
do.call(func, list)توسيع القائمة لتشمل معلمات الدالة- تستخدم لغة R النطاق اللغوي؛ وبشكل افتراضي، لا يمكن تعديل المتغيرات العالمية داخل الدوال (باستخدام
<<-، على الرغم من أن ذلك غير موصى به)
📝 تمارين
-
المشكلة الأساسية: اكتب دالة
celsius_to_fahrenheit(c)تقوم بتحويل درجات مئوية إلى درجات فهرنهايت (الصيغة: F = C × 9/5 + 32). اختبر الدالة بقيم الإدخال 0 و25 و100، وتأكد من أن قيم الإخراج هي 32 و77 و212. -
المسألة الأساسية: اكتب دالة
circle_area(r)تحسب مساحة الدائرة (pi * r^2)، مع تقريب النتيجة إلى رقمين عشريين. اختبر الدالة بقيم r = 1، 2، 5. -
المشكلة الأساسية: اكتب دالة
greet(name = "World", greeting = "Hello")ذات معلمات افتراضية تُخرج "Hello, World!" أو "Hi, Alice!"، واختبر ثلاث طرق مختلفة لاستدعائها. -
مشكلة متقدمة: اكتب دالة
analyze_scores(scores)تأخذ متجهًا من الأعداد وتُرجع قائمة تحتوي علىmeanmedianmaxminpass_rateمعدلات النجاح، باستخدامinvisible()لمنع الطباعة التلقائية. بعد استدعاء الدالة، استخدمresult$pass_rateللوصول إلى معدلات النجاح. -
التحدي: اكتب دالة تُرجع دالة
make_power(n)تُرجعfunction(x) x^n. استخدمmake_power(2)لإنشاءsquare، واستخدمmake_power(3)لإنشاءcube، واختبرsquare(5) = 25وcube(3) = 27. احفظ لقطة شاشة.