0Pricing
C Academy · درس

تمرير الدوال

Callbacks كمعاملات

تمرير الدوال درس مجاني في C Academy على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في C Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة C Academy 4 دروس في المجموع.

الدوال كمعاملات

بما أن مؤشر الدالة مجرد قيمة، يمكنك تمرير دالة إلى دالة أخرى. وتُسمى الدالة المُستقبَلة استدعاءً عكسيًا.

يتيح ذلك لإجراء واحد تخصيص جزء من سلوكه.

#include <stdio.h>

int apply(int x, int (*f)(int)) {
    return f(x);
}

int square(int x) { return x * x; }

int main(void) {
    printf("%d\n", apply(6, square));
    return 0;
}

معامل الاستدعاء العكسي

يُصرَّح عن معامل الاستدعاء العكسي بالطريقة نفسها التي يُصرَّح بها عن متغير مؤشر دالة. ويمكنك استدعاؤه داخليًا مثل أي دالة.

#include <stdio.h>

void run_twice(void (*action)(void)) {
    action();
    action();
}

void beep(void) { printf("beep\n"); }

int main(void) {
    run_twice(beep);
    return 0;
}

تبديل السلوك

مرّر استدعاءات عكسية مختلفة للحصول على نتائج مختلفة من الدالة نفسها ذات المستوى الأعلى.

#include <stdio.h>

int combine(int a, int b, int (*op)(int, int)) {
    return op(a, b);
}

int add(int a, int b) { return a + b; }
int mul(int a, int b) { return a * b; }

int main(void) {
    printf("%d\n", combine(3, 4, add));
    printf("%d\n", combine(3, 4, mul));
    return 0;
}

تطبيق دالة على عناصر مصفوفة

تطبّق دالة map استدعاءً عكسيًا على كل عنصر في مصفوفة في موضعه.

#include <stdio.h>

void map(int *a, int n, int (*f)(int)) {
    for (int i = 0; i < n; i++) a[i] = f(a[i]);
}

int inc(int x) { return x + 1; }

int main(void) {
    int a[] = {1, 2, 3};
    map(a, 3, inc);
    for (int i = 0; i < 3; i++) printf("%d ", a[i]);
    printf("\n");
    return 0;
}

التصفية باستخدام محك

المحك هو استدعاء عكسي يُرجع قيمة صواب أو خطأ. استخدمه لعدّ العناصر المطابقة أو تحديدها.

#include <stdio.h>

int count_if(int *a, int n, int (*pred)(int)) {
    int c = 0;
    for (int i = 0; i < n; i++) if (pred(a[i])) c++;
    return c;
}

int is_even(int x) { return x % 2 == 0; }

int main(void) {
    int a[] = {1,2,3,4,5,6};
    printf("%d\n", count_if(a, 6, is_even));
    return 0;
}

الاختزال إلى قيمة واحدة

تختزل دالة reduce مصفوفةً إلى قيمة واحدة باستخدام استدعاء عكسي ثنائي ومجمّع ابتدائي.

#include <stdio.h>

int reduce(int *a, int n, int init, int (*op)(int,int)) {
    int acc = init;
    for (int i = 0; i < n; i++) acc = op(acc, a[i]);
    return acc;
}

int add(int a, int b) { return a + b; }

int main(void) {
    int a[] = {1,2,3,4};
    printf("%d\n", reduce(a, 4, 0, add));
    return 0;
}

تمرير دالة مقارنة

غالبًا ما يستقبل الفرز والبحث استدعاءً عكسيًا للمقارنة، بحيث تعمل الخوارزمية نفسها مع أي ترتيب.

#include <stdio.h>

int max_by(int a, int b, int (*greater)(int,int)) {
    return greater(a, b) ? a : b;
}

int gt(int a, int b) { return a > b; }

int main(void) {
    printf("%d\n", max_by(3, 8, gt));
    return 0;
}

استخدام typedef لأنواع الاستدعاءات العكسية

يجعل تسمية نوع الاستدعاء العكسي باستخدام typedef توقيعات الدوال أسهل قراءة.

#include <stdio.h>

typedef int (*IntFn)(int);

int apply(int x, IntFn f) { return f(x); }

int neg(int x) { return -x; }

int main(void) {
    printf("%d\n", apply(5, neg));
    return 0;
}

بديل افتراضي للاستدعاء العكسي

إذا كان من الممكن أن تكون قيمة الاستدعاء العكسي NULL، فوفّر سلوكًا افتراضيًا حتى يتمكن المستدعي من عدم استخدامه.

#include <stdio.h>

int apply(int x, int (*f)(int)) {
    if (f == NULL) return x;
    return f(x);
}

int dbl(int x) { return x * 2; }

int main(void) {
    printf("%d\n", apply(9, NULL));
    printf("%d\n", apply(9, dbl));
    return 0;
}

تفصل الاستدعاءات العكسية بين أجزاء الشيفرة

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

#include <stdio.h>

void for_each(int *a, int n, void (*visit)(int)) {
    for (int i = 0; i < n; i++) visit(a[i]);
}

void show(int x) { printf("[%d]", x); }

int main(void) {
    int a[] = {7, 8, 9};
    for_each(a, 3, show);
    printf("\n");
    return 0;
}

اختيار الاستدعاء العكسي أثناء التشغيل

اجمع بين اختيار الشرط والتمرير: اختر استدعاءً عكسيًا، ثم مرّره إلى دالة ذات مستوى أعلى.

#include <stdio.h>

int apply(int x, int (*f)(int)) { return f(x); }
int sq(int x) { return x * x; }
int cube(int x) { return x * x * x; }

int main(void) {
    int power = 3;
    int (*f)(int) = (power == 2) ? sq : cube;
    printf("%d\n", apply(2, f));
    return 0;
}

اختبار سريع

اختبر مدى فهمك لتمرير الدوال.

مراجعة

تعلّمتَ تمرير الدوال بوصفها استدعاءات عكسية:

  • صرّح عن معامل الاستدعاء العكسي مثلما تصرّح عن مؤشر دالة.
  • أنشئ دوالًا ذات مستوى أعلى مثل map وfilter وreduce وfor_each.
  • استخدم typedef لتسمية أنواع الاستدعاءات العكسية.
  • تعامل مع الاستدعاءات العكسية ذات القيمة NULL باستخدام سلوك افتراضي.

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

هل درس «تمرير الدوال» مجاني؟

نعم — نص درس «تمرير الدوال» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة C Academy، انتقل إلى CoddyKit PRO. تتضمن دورة C Academy 4 دروس في المجموع.

ماذا ستتعلم في «تمرير الدوال»؟

Callbacks كمعاملات تتمرن على C Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ C Academy؟

لا تُشترط خبرة سابقة. C Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.

كم من الوقت يستغرق درس «تمرير الدوال»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس C Academy هذا؟

نعم. كل درس في C Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. التصريح بمؤشرات الدوال
  2. تمرير الدوال
  3. qsort مع المقارِنات
  4. جداول مؤشرات الدوال
← العودة إلى C Academy