أنواع وأصناف الدوال في لغة الكوتلن

مقالة توضح طريقة معرفة نوع الدوال وكذلك أصنافهم المتعددة.

Mohammad Laifمنذ 4 سنوات

بسم الله الرحمن الرحيم

السلام عليكم ورحمة الله وبراكته

 

مقالة تسرد أنواع وأصناف الدوال في لغة الكوتلن.

 

من المستحسن الاطلاع على المقالة السابقة بعنوان مقدمة عن الدوال في لغة الكوتلن قبل قراءة هذه المقاله.

أصناف الدوال في لغة الكوتلن للإصدار ١٫٣ هي كالتالي: Lambda و Anonymous Function و Literal Function و Higher Order Function و Top Level Function و Member Functions و Local Function و Nested Function و Extension Function وفي انتظار المزيد من الدوال من قبل مبرمجي اللغة.

بالنسبة للدوال التأجيلية التي تأتي مع الكورتين Coroutines  فلها درس يخصها على الرابط: الدوال التأجيلية Suspended Functions.

 

ماذا ستقرأ في هذه المقالة؟

  • أنواع الدوال في لغة الكوتلن.
  • أصناف الدوال في لغة الكوتلن.
    • الـ Lambda.
    • الـ Anonymous Function.
    • الـ Literal Function.
    • الـ Higher Order Function.
    • الـ Top Level Function.
    • الـ Member Functions.
    • الـ Local Function (وتسمى كذلك الـ Nested Function).
  • المصادر.

 

أنواع الدوال في لغة الكوتلن

من المعروف أن العناصر لها انواع عديدة كالحروف Char والسلاسل النصية String والارقام Int وما الى ذلك. ولاننسى الأنواع التي نقوم بإنشائها نحن بشكل مخصص.

مثال يوضح بعض من الأنواع للعناصر:

val x: Int = 20
val name: String = “Mohammad”
val gotham: City = City(“Gotham”)
  • في السطر الاول لدينا كائن عددي نوعه هو Int.
  • في السطر الثاني لدينا كائن نصي نوعه هو String.
  • في السطر الثالث لدينا كائن مدينة ونوعه هو City قمنا بإنشائه نحن.

 

كذلك كل دالة في لغة الكوتلن لها نوع معين. فليس فقط العناصر لديها انواع. ولمعرفة كيفية فهم انواع الدوال فنحن نعبر عن الدالة بالقوسين (), وبداخلهما نضع انواع المدخلات لها. ونعبر عن النوع التي تخرجة الدالة بالسهم -> ومن ثم يتبعه نوع المخرج. وهكذا نستطيع ان نستشف نوع الدالة من هذه الاشياء.

 

الصيغة العامة لنوع الدالة التي لاتأخذ مدخلات ولاتخرج شئ:

() -> Unit
  • لاحظ ان القوسين الفارغين نعبر عنهم عن مدخلات الدالة. ولاحظ ان النوع Unit نعبر عنه لتمثيل اللا شئ (جافا: تستطيع فهمة كأنه النوع Void).

 

بعض الأمثلة لهذا النوع من الدوال:

fun message() {
    println("Message")
}

 

لاحظ ان نوع المخرج لم نكتبه بل يستطيع المترجم Compiler أن يستشفه بنفسه فلا داعي لكتابته هكذا:

fun message(): Unit {
    println("Message")
}

 

أما اذا كانت الدالة تأخد عدد من الانواع وتخرج نوع فنستطيع التعبير عنها كالتالي:

(Type, Type) -> Return Type

 

مثال لدالة من هذا النوع, تأخذ مدخلين من نوع Int وتخرج مجموعهم كنوع Int:

fun sum(a: Int, b: Int): Int {
    return a + b
}
  • لاحظ أن النوع Type هنا هو Int لكل من المدخلين والمخرج.

 

وفي البرمجة الدالية, الدوال تستطيع اخراج دالة او اخذ دالة كمعطى لها, وتكون الصيغة العامة لها على الشكل التالي:

()->()->Unit
  • وهذا معناة أن دالة لاتأخد مدخلات وتخرج دالة وتلك الدالة لاتأخذ مدخلات ولاترجع شئ.

 

مثال لدالة من هذا النوع:

fun foo(boo: () -> Unit): Unit {
}
  • لاحظ أن الدالة foo تأخد دالة اخرى بأسم boo ولاتخرج شئ.

 

مثال لدالة تأخد رقمين من نوع Int وتأخد ثالث كدالة لإجراء العملية الحسابية عليهم, ولاتخرج شئ بل تطبع الناتج:

fun calculator(a: Int, b: Int, operation: (Int, Int) -> Int): Unit {
    println("The result is " + operation(a, b))
}
  • لاحظ أن الدالة calculator تعتبر من صنف الدوال الـ Higher-Order Functions وهذا يعني انها قادرة على اخذ دالة او اخراج دالة اخرى. هنا قامت بأخذ دالة من من نوع (Int, Int) -> Int كمدخل ثالث.

 

ونستخدمها هكذا:

calculator(2, 4, ::sum)
  • لاحظ أننا قمنا بمناداة الدالة calculator واعطيناها رقمان ٢ و ٤ ثم اعطيناها دالة اخرى قد أنشئناها مسبقاً بأسم sum والتي تمثل عملية الجمع.
  • لاحظ اننا قمنا بالاشارة الى الدالة sum بطريقة :: ثم اسم الدالة. وهذا يسمى  بالـ Callable Reference ونستخدمه للإشارة الى الدوال من اصناف الـ Top Level و Local و Member و Extension في القسم التالي ستتعرفهم عليهم.

 

لاتجهد نفسك في معرفة حفظ هذه الاشياء فكثرة الممارسة وقرائة شفرة الدوال ستجد انك تفهم انواعهم.

 

أصناف الدوال الدالية في لغة الكوتلن

في هذا القسم سوف نتعرف على أصناف الدوال التي تقدمهم لغة الكوتلن كخطوات نحو البرمجة الدالية Functional Programming والتي تساعدنا في إنشاء نظام تصريحي من خلال البرمجة Declarative Programming يعتمد على التصريح بطلب ماذا نريد من النظام أن يفعل What.

 

الـ Lambda

هذه هي اهم دالة من خلالها نستطيع اختصار وفعل اشياء كثيرة بها. تنطق لامبدا lambda تعتبر نوع مختصر من انواع الدوال وتأتي بدون اسم. أي اننا لانحتاج الى كتابة اسم لها فقط الـ {}. وتتميز بإختصار الكتابه. فهي عبارة عن قوسين {} بداخلهما نكتب مانريد منها ان تفعله. وتعتبر مناسبة للإشياء الصغيرة.

{ 4 * 4 }

 

الـ Anonymous Function

الدالة المجهولة وهي دالة تأتي بدون اسم كالـ لامبدا. مايميز هذه انها تستطيع تحديد نوع الارجاع return. وكذلك الراحة في كتابة الكثير من الاسطر البرمجية بداخلها. اي اكثر وضوح Explicit من اللامبدا. مناسبة للإشياء اللتي تحتاج بعض الاسطر البرمجية وتحديد للمخرج.

// Anonymous Function
fun(x: Int, y: Int): Int {
    return x + y
}

 

اذن نستخدم لامبدا اذا لم نرد تحديد نوع الارجاع return. اما اذا فنستخدم الداله المجهوله anonymous. وهنا يكمل فرق بينهما.

 

الـ Literal Function

بالمختصر الدوال الحرفية (الـ Literal Function) هي Lambda Function او Anonymous Function تم اسنادها الى اسم متغير (Function Reference).

// Lambda Function as Literal Function
// Type of ‘()’ take no args.
// ‘Int’ is return type.
val lambdaFunctionLiteral: () -> Int = { 4 * 4 }
// Anonymous Function as Literal Function
// Type of ‘(Int, Int)’ take two args type ‘Int’
// ‘-> Int’ means return type is int.
val anonymousFunctionLiteral: (Int, Int) -> Int = fun(x: Int, y: Int): Int {
    return x + y
}
  • لاحظ طريقة اسناد دالة من نوع لامبدا الى المتغير lambdaFunctionLiteral لتصبح دالة حرفية literal.
  • لاحظ طريقة اسناد دالة من نوع مجهول الى المتغير anonymousFunctionLiteral لتصبح دالة حرفية literal.

 

اذن عندما نسند الدوال السابقه (اللامبدا و المجهوله) الى متغير ما, نستطيع تسميتها بالـ Literal Function.

 

الـ Higher Order Function

عبارة عن دالة تستطيع اخد دالة اخرى كمدخل لها Parameter. او اخراج دالة اخرى كمخرج.

// Lambdas as Literal Function
// Function type is (Int, Int) -> Int
val sumOperation: (Int, Int) -> Int = { x: Int, y: Int -> x + y }
val mulOperation: (Int, Int) -> Int = { x: Int, y: Int -> x * y }

// Higher Order Function
// Parameters:
//   1. ‘X’ and ‘y’ Type is Int.
//  2. ‘Function’ Type is (Int, Int) -> Int.
fun runOperation(x: Int, y: Int, operation: (Int, Int) -> Int): Int {
    return operation(x, y)
}

// Usage
runOperation(8, 2, mulOperation)  // 16
runOperation(8, 2, sumOperation)  // 10
  • دالتان حرفيتان Lambdas Literal بإسم sumOperation و mulOperation لعملية الجمع والقسمة.
  • دالة عالية المستوى Higher Order بإسم runOperation تأخد متغيرين Int ودالة اخرى, وتخرج Int.
  • لاحظ طريقة الاستخدام وذلك بتمرير لها رقمين ودالة ما.

 

مثال آخر لدالة الـ Higher Order Function تأخذ دوال كمدخلات:

// Higher Order Function
// 'x' & 'y' Type is Int.
// 'function' Type is (Int, Int) -> Int
fun runOperation(x: Int, y: Int, operation:(Int, Int) -> Int): Int {
    return operation(x, y)
}
  • المدخلات للمتغيران x و y من نوع Int.
  • المدخل للدالة التي بإسم operation من نوع (Int,Int) -> Int.

 

الاستخدام من خلال اعطائها المعطيات المطلوبة (ارقام وداله ما):

fun main() {
    // Usage with lambda
    runOperation(8, 2) { x: Int, y: Int -> x / y } // 4
    
    // Usage with Anonymous Function
    runOperation(5, 1,
                fun(x: Int, y: Int): Int {
                    return x - y
                }
    ) // 4
    
}
  • لاحظ تأخد دالة من نوع Lambda بشكل مختصر.
  • واستخدام آخر تأخد دالة من نوع Anonymous Function.

 

مثال لدالة الـ Higher Order Function تقوم بإرجاع دالة ما.

// Normal Functions
fun mulOperation(x: Int, y: Int): Int {
    return x * y
}

fun sumOperation(x: Int, y: Int): Int {
    return x + y
}

// Higher Order Function
fun getOperationMulOrSum(operation: String): ((x: Int, y: Int) -> Int) {
    return if (operation == "mul" {
        ::mulOperation
    } else {
        ::sumOperation
    }
}
  • دالتين عادية بإسم mulOperation و sumOperation لعمليتا الضرب والجمع.
  • دالة عالية المستوى بإسم getOperationMulOrSum تأخد نص عبارة عن أسم للعملية, وتقوم بإخراج دالة من خلال ندائها من مكان ما بإستخدام ::.
  • لاحظ استخدام الرمز :: وذلك للإشارة الى الدالة, يسمى بالـ Callable References او Feature Literals هنا تم استخدامة كـ callable للإشارة الى دالة موجودة مسبقاً.

 

الاستخدام:

fun main() {
    val mulResult: Int = getOperationMulOrSum("mul")(50, 3) // 150
    val sumResult: Int = getOperationMulOrSum("sum")(50, 3) // 53
}

 

مثال آخر لدالة الـ Higher Order Function تقوم بإرجاع دالة ما. تاره من نوع Anonymous Function وتارى اخرى من نوع Lambda بشكل مختصر. ولكن يجب ان تكون من نفس النوع المحدد الا وهو (Int, Int -> Int):

// Higher Order Function
fun calculator(operation: String): ((x: Int, y: Int) -> Int) {
    when (operator) {
        "sum" -> return fun(x: Int, y: Int): Int { return x + y }
        "sub" -> return fun(x: Int, y: Int): Int { return x - y }
        "mul" -> return fun { x: Int, y: Int -> x * y }
        "div" -> return fun { x: Int, y: Int -> x / y }
    }
    return fun(_: Int, _: Int): Int { return 0}
}
  • لاحظ الرمز _ يعبر عن تسمية للمتغير الذي لن يستخدم في الدالة.

 

الاستخدام:

fun main() {
    calculator("sum")(4, 6)  // 10
    calculator("div")(12, 3) // 4
}

 

الـ Top Level Function

هي الدوال التي لاتنتمي الى اي فئه class او واجهة interface او عنصر object. بل يتم كتابتها في اي ملف كوتلن. تستطيع تشبيهها بالـ Static Methods في الجافا, تناديها من اي مكان في تطبيقك. ولكن لاتنسى ان تعمل لها import.

In a kotlin file.kt
fun log(message: String) {
    println(message)
}
// Usage in other classes or files
fun main() {
  log(“Hi from level function”)
}

 

الـ Member Functions

كل دالة نكتبها بداخل فئة class او عنصر Object نستطيع تسميتها بالـ Member Functions. نستطيع نداء هذا النوع من العناصر Object المنشئه من هذه الفئة (لاشئ جديد هنا).

// Class City
class City {
    val population = 10014
    // Member Function
    fun printPopulation() {
        println(population)
    }
}
// Usage
val city = City()
city.printPopulation()

 

الـ Local Function وتسمى كذلك بالـ Nested Function

عبارة عن دالة بداخل دالة. كما بالمثال:

// Local Function | Nested Function
// Normal Function
fun mul(x: Int, y: Int): Int {
    // Validation
    require(x != 0) { "x must not be zero" }
    require(y != 0) { "y must not be zero" }
    // Local Function
    fun calculate(x: Int, y: Int): Int {
        return x + y
    }
    // Usage of Local Function
    return calculate(x, y)
}
  • دالة عملية الضرب, تتأكد ان المدخل ليس بصفر (ربما تقذف Exception تذكر ان هذا العمل ضد طريقة الـ Functional Programming).
  • ثم توجد دالتنا الـ Local بداخلها للقيام بعملية الضرب بحذ ذاتها.

 

الـ Extension Function

دالة تم الحاقها بفئة class بحيث تكتسبها تلك الفئة كدالة لها. وظيفتها اضافة تصرف الى فئه دون التعديل على شفرتها.

مثال لدالة إضافية تقوم بالتحقق من ازدواجية الرقم, تم الحاقها بفئة الـ Int. وآخر لدالة تطبع قيم اي متسلسلة تم الحاقها بالـ Collection.

// Extension Function
fun Int.isEven() {
    println(this % 2 == 0)
}
// Usage
4.isEven()

 

وتأتي مع  الدوال (كالـ Extension Function) كلمات Keyword في لغة الكوتلن نستطيع استخدامها اثناء تعريفها مثل:

  • الـ infix: لجعلها تنادى بدون استخدام النقطة.
  • و inline: لجعل الدالة خفيفة على المترجم compiler.
  • و reified: للدلاله على الفئة class بدون تحديدها.

 

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

  • دالية Functional.
  • وكائنية OOP.
  • وتستطيع الخلط او استخدام احدها فقط 🙂.

 

في هذه المقاله تعرفنا على انواع الدوال في لغة الكوتلن بشكل سريع ومختصر.

 

المصادر

كلمات دليلية: kotlin
0
إعجاب
3319
مشاهدات
0
مشاركة
1
متابع

التعليقات (0)

لايوجد لديك حساب في عالم البرمجة؟

تحب تنضم لعالم البرمجة؟ وتنشئ عالمك الخاص، تنشر المقالات، الدورات، تشارك المبرمجين وتساعد الآخرين، اشترك الآن بخطوات يسيرة !