Lambda: ما هو، بناء الجملة والاستخدام في Kotlin

المؤلف: IT Sectr نُشر: 2026-06-23 وقت القراءة: 7 دق

Lambda هي دالة مجهولة ممثلة بتعبير مضغوط { وسائط -> جسم }. في Kotlin، تعتبر lambdas الطريقة الأساسية لتمرير السلوك إلى دوال الترتيب الأعلى، مما يوفر بناء جملة موجز دون الحاجة لتعريف دالة منفصلة. وفقًا لتوثيق Kotlin (2026)، أكثر من 70% من دوال الترتيب الأعلى في المشاريع النموذجية تُستدعى مع lambdas. Lambda تتيح كتابة الكود بأسلوب وظيفي مع أقل قدر من الضوضاء التركيبية.

النقاط الرئيسية

  • Lambda — دالة مجهولة ببناء { args -> body }، بدون اسم
  • الاسم الضمني it يستبدل الوسيط الوحيد للـ lambda، مما يبسط الكتابة
  • Trailing lambda — إخراج آخر lambda خارج أقواس الاستدعاء لسهولة القراءة
  • الإغلاق (closure) — يمكن للـ lambda الوصول إلى المتغيرات من النطاق الخارجي
  • النوع الوظيفي (T) -> R يحدد توقيع الـ lambda ويتم التحقق منه بواسطة المترجم

ما هو lambda؟

Lambda (تعبير lambda) هي دالة مجهولة تُعرف كتعبير بدلاً من تعريف. المصطلح يأتي من حساب اللامدا لألونزو تشرتش (1936). في البرمجة، lambda هي كتلة من الكود يمكن تمريرها كقيمة، تخزينها في متغير واستدعاؤها لاحقًا. في Kotlin، lambda دائمًا محصورة بين أقواس مجعدة { }، مع فصل الوسائط عن الجسم بواسطة السهم ->.

تختلف lambda عن الدالة العادية بغياب الاسم والكلمة المفتاحية fun. هذا يسمح بتعريف السلوك مباشرة في نقطة الاستخدام، دون إرباك الكود بتعريفات إضافية. يقوم مترجم Kotlin باستنتاج أنواع الوسائط من السياق، لذلك في كثير من الحالات يمكن حذف الأنواع. lambda هي سكر نحوي فوق النوع الوظيفي (T) -> R: كل lambda لها نوع وظيفي مقابل.

الاستخدام الرئيسي للـ lambdas في Kotlin هو تمريرها إلى دوال الترتيب الأعلى. تستخدم المكتبة القياسية lambdas في كل مكان: filter { it > 0 }، map { it.uppercase() }، forEach { println(it) }. تُستخدم lambdas أيضًا لمستمعي الأحداث، الاستدعاءات الخلفية ومُهيئات الكائنات عبر apply و run، مما يجعل الكود أكثر تصريحية مقارنة بالفئات المجهولة.

بناء جملة تعبيرات lambda في Kotlin

الشكل الكامل لتعبير lambda في Kotlin: { وسائط -> جسم_lambda }. تُدرج الوسائط مفصولة بفواصل، يليها سهم ثم الجسم — تعبير واحد أو أكثر. إذا كان الجسم يحتوي على عدة أسطر، يصبح آخر تعبير هو القيمة المُعادة. إذا كانت الدالة تقبل lambda كآخر وسيطة، يمكن إخراجها خارج أقواس الاستدعاء.

kotlin
val sum: (Int, Int) -> Int = { a, b -> a + b }

// الأنواع المستنتجة من السياق
val sumShort = { a: Int, b: Int -> a + b }

// استدعاء Lambda
val result = sum(5, 3)  // 8
println(result)

المتغير sum يُعلن بنوع وظيفي صريح (Int, Int) -> Int ويُهيأ بـ lambda { a, b -> a + b }. بالنسبة لـ sumShort، الأنواع محددة داخل lambda ونوع المتغير مستنتج. كلا الخيارين متكافئان. استدعاء lambda لا يختلف عن استدعاء دالة عادية — باستخدام بناء sum(5, 3). lambda بدون وسائط تُكتب كـ { body }، بوسيطة واحدة — { it -> it * 2 } أو ببساطة { it * 2 }.

الاسم الضمني it وحذف الوسائط

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

kotlin
val numbers = listOf(1, 2, 3, 4, 5)

// اسم المعامل الصريح
val squaredExplicit = numbers.map { n -> n * n }

// it ضمني
val squared = numbers.map { it * it }

// التصفية باستخدام it
val even = numbers.filter { it % 2 == 0 }

println(squared) // [1, 4, 9, 16, 25]

في المثال، يستخدم map { it * it } it بدلاً من تعريف n ->. يقوم Kotlin تلقائيًا بتعيين it لمعامل lambda الوحيد. إذا كانت lambda تقبل وسيطتين أو أكثر، لا يتم إنشاء it — يجب تعريف جميع المعاملات بشكل صريح. لتحسين قراءة lambdas المتداخلة، من الأفضل إعطاء المعاملات أسماء ذات معنى واستخدام it فقط في المسندات القصيرة المكونة من سطر واحد.

Trailing lambda و lambdas متعددة الأسطر

Trailing lambda هي ميزة نحوية في Kotlin تسمح بإخراج آخر وسيطة lambda خارج أقواس استدعاء الدالة. هذا يجعل الكود يشبه تركيبات اللغة المضمنة. إذا كانت lambda هي الوسيطة الوحيدة، يمكن حذف الأقواس بالكامل. يُستخدم هذا الأسلوب بنشاط في المكتبة القياسية والـ DSLs.

kotlin
// استدعاء قياسي
thread(block = {
    println("مرحبًا من السلسلة")
})

// إصدار trailing lambda
thread {
    println("مرحبًا من السلسلة")
}

// تكرار مع trailing lambda
repeat(3) { index ->
    println("تكرار $index")
}

// تشغيل مع lambda متعددة الأسطر
val config = run {
    val host = "localhost"
    val port = 8080
    "$host:$port"
}

الدالة thread تقبل Runnable كآخر وسيطة. بفضل trailing lambda، يمكن تمرير lambda خارج الأقواس، مما يشبه بصريًا كتلة كود. الدالة repeat تأخذ عدد التكرارات و lambda مع فهرس. run هي مثال لدالة ترتيب أعلى حيث lambda متعددة الأسطر تحسب قيمة يُرجعها آخر تعبير. يمكن حذف الأقواس حول run() إذا كانت lambda هي الوسيطة الوحيدة.

التقاط المتغيرات (closure) في lambdas

التقاط المتغيرات (closure) هو قدرة lambda على الوصول إلى المتغيرات المُعلنة في النطاق الخارجي. في Kotlin، يمكن لـ lambda قراءة وتعديل متغيرات var، وكذلك قراءة متغيرات val من السياق المحيط. تُخزن المتغيرات الملتقطة مع lambda طوال دورة حياتها، مما يتيح إنشاء دوال بحالة.

kotlin
fun makeCounter(): () -> Int {
    var count = 0
    return {
        count++  // عدد مرات الالتقاط
    }
}

fun main() {
    val counter = makeCounter()
    println(counter()) // 0
    println(counter()) // 1
    println(counter()) // 2
}

الدالة makeCounter تُعلن متغيرًا محليًا count وتُعيد lambda { count++ }. على الرغم من أن makeCounter أكملت تنفيذها، تحتفظ lambda المُعادة بمرجع إلى count عبر الإغلاق. كل استدعاء لـ counter() يزيد ويعيد القيمة الحالية. آلية الإغلاق تكمن وراء الكاري، مصانع الدوال والتخزين المؤقت. من المهم تذكر أن التقاط المتغيرات القابلة للتغيير قد يؤدي إلى سلوك غير متوقع في التنفيذ المتزامن.

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

هل يمكن أن تحتوي lambda على return؟

Lambda لا تدعم return غير المشروط — return بدون علامة يُعيد التحكم إلى الدالة الخارجية. للخروج من lambda، استخدم return@label، حيث label هو اسم دالة الترتيب الأعلى التي مُررت إليها lambda.

ما الفرق بين lambda والدالة المجهولة؟

الدالة المجهولة تُكتب كـ fun(a: Int): Int { return a * 2 } وتدعم return بدون علامة. lambda { a -> a * 2 } أقصر في بناء الجملة ولكنها تتطلب return@label للخروج المبكر.

كيف تمرر lambda كوسيطة بدون سهم؟

إذا كانت lambda لا تقبل وسائط، يُحذف السهم: { body() }. إذا كانت تقبل وسيطة واحدة ولا تستخدمها، اكتب { _ -> body() } بتسطير لتجاهل المعامل.

هل تنشئ lambda دائمًا كائنًا جديدًا؟

نعم، كل lambda تُمرر إلى دالة غير inline تنشئ مثيلاً لفئة مجهولة. بالنسبة للدوال inline، تُضمّن lambda في موقع الاستدعاء دون إنشاء كائن. lambdas غير الملتقطة تُخزن مؤقتًا في كائنات منفردة.

لماذا لا يمكن استخدام return مباشرة في lambda؟

Return في Kotlin هو عامل قفز ينتمي إلى أقرب دالة مُعلنة بـ fun. lambda ليست دالة (fun)، بل تعبير. لذلك، return داخل lambda بدون علامة يُعتبر عودة من الدالة الخارجية المحيطة.

الخلاصة

  • Lambda — دالة مجهولة ببناء { args -> body }، تُمرر كقيمة إلى دوال الترتيب الأعلى
  • الاسم الضمني it يستبدل الوسيطة الوحيدة، مما يجعل الكود أكثر compactness في المسندات القصيرة
  • Trailing lambda تُخرج آخر lambda خارج أقواس الاستدعاء، لتُقرأ ككتلة كود
  • الإغلاق (closure) يسمح لـ lambda بالوصول إلى المتغيرات من السياق الخارجي بعد اكتمال الدالة المحتوية
  • النوع الوظيفي (T) -> R يحدد توقيع lambda مع التحقق في وقت الترجمة
  • الدوال inline تلغي إنشاء الفئات المجهولة للـ lambdas، مما يحسن الأداء في الحلقات
  • Lambdas متعددة الأسطر تُعيد قيمة آخر تعبير، وتدعم العمليات المعقدة دون تعريف دالة منفصلة

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا