التحكم بالتدفق (Control Flow) في Kotlin

من موسوعة حسوب

كما في أيّة لغة برمجة فإن لغة Kotlin تحتوي على تعابير للتحكم بالتدفق، وهي: تعبير if، وتعبير when، وحلقة for، وحلقة while. وتدعم كذلك الكلمتين المفتاحيّتَين continue و break المستخدَمتَين في الحلقات (راجع أوامر الرجوع والقفز returns and jump).

تعبير if

يُعدُّ الشرط if في لغة Kotlin تعبيرًا يعيد قيمة، وبالتالي لا حاجة للصيغة condition ? then : else لأن تعبير if يقوم بهذا الدور كما في الشيفرة الآتية:

// الاستخدام الاعتيادي
var max = a 
if (a < b) max = b

// استخدام else 
var max: Int
if (a > b) {
    max = a
} else {
    max = b
}
 
// استخدام الشرط كتعبير
val max = if (a > b) a else b

إذا يمكن تجميع عدّة تعليماتٍ في أحد الفروع (block) بحيث تكون قيمة هذا الجزء هي قيمة آخر تعبيرٍ فيه، مثل:

val max = if (a > b) {
    print("Choose a")
    a
} else {
    print("Choose b")
    b
}

وعند استخدام if باعتباره تعبيرًا بدلًا من تعليمة (أي أنّه قد يعيد قيمةً أو تُسنَد قيمته لمتغيرٍ ما) فإنه يجب أن يكون لهذا التعبير حالة else.

تعبير when

إنّ تعبير when بديلٌ للمعامل switch الموجود في لغات البرمجة مثل لغة C ومثيلاتها، وله الصيغة الآتية بأبسط شكلٍ ممكنٍ:

when (x) {
    1 -> print("x == 1")
    2 -> print("x == 2")
    else -> { // لاحظ وجود block
        print("x is neither 1 nor 2")
    }
}

إذ تُجرَى المقارنة ما بين القيمة الواقعة بين قوسيّ when والقيم الموجودة في كافّة الحالات وبالتسلسل الواردة فيه حتى يتحقَّق شرط التساوي بينهما في إحداها، ويكون استخدام الشرط when إما كتعليمة (statement) أو كتعبير (expression)؛ فإذا ما استُخدِم باعتباره تعبيرًا فإن قيمته ستصبح بقيمة آخر حالة تحقَّق فيها التساوي، أما إن كان تعليمةً فلا أهمية لقيم أيّ من الحالات الموجودة، وكما هو الحال في if يُمكن أن تُجمَّع عدة تعليمات ضمن نفس الحالة (block) وتكون قيمة هذا الجزء حينئذٍ بقيمة آخر تعبيرٍ موجودٍ فيه.

ويًحال نسق البرنامج إلى حالة else إن لم تتحقَّق أيٌّ من الحالات السابقة، وعند استخدام شرط when كتعبيرٍ فإن وجود حالة else أمرٌ إجباريٌّ أو قد يكون اختياريًا إذا كان بوسع المُترجِم (compiler) التأكدُ من أنّ الحالات الموجودة مستوفيةٌ لكلّ الحالات الممكنة.

ويُلاحظ أنّه إذا تطابقت التعليمات بأكثر من حالة فيُمكن دمجُ الحالتين سويةً والفصل فيما بينهما بالفاصلة , مثل:

when (x) {
    0, 1 -> print("x == 0 or x == 1")
    else -> print("otherwise")
}

ويصلُح استخدام التعبير (expression) في حالات when بدلًا من القيم الثابتة، مثل:

when (x) {
    parseInt(s) -> print("s encodes x")
    else -> print("s does not encode x")
}

أو يمكن أن تكون مجالًا أو مجموعة (collection) بالاستفادة من المعامل in أو ‎!in، مثل:

when (x) {
    in 1..10 -> print("x is in the range")
    in validNumbers -> print("x is valid")
    !in 10..20 -> print("x is outside the range")
    else -> print("none of the above")
}

وكذلك تُمكن الاستفادة من المُعامِل is أو نفيه ‎!is مع أحد الأنواع (type)، ولا حاجة للمزيد من عمليات التحقّق للوصول إلى التوابع (methods) أو الخاصّيّات (properties) وهذا بفضل عمليات التحويل (smartcasts) ، مثل:

fun hasPrefix(x: Any) = when(x) {
    is String -> x.startsWith("prefix")
    else -> false
}

ويُستعاض باستخدام when عن السلسلة الطويلة (chain) من الشرط if-else if ، وإن لم يكن هناك أي وسيط (argument) واقعٍ بين قوسيّ when فإن شروط الحالات ستكون عبارة عن تعابير لها قيمة منطقيّة (boolean) وستُنفَّذ تعليمات الحالة التي تكون قيمتها الثنائية true، مثل:

when {
    x.isOdd() -> print("x is odd")
    x.isEven() -> print("x is even")
    else -> print("x is funny")
}

تعبير for

تُستخدَم حلقة for مع أيّ مجموعةٍ من القيم القابلة للتكرار (iterations)، وهي تماثل الحلقة foreach في لغات البرمجة مثل C#‎ ولحلقة for الصيغة الآتية:

for (item in collection) print(item)

ويمكن أن تُنفَّذ عدة تعليمات (block) بالتكرار الواحد، مثل:

for (item: Int in ints) {
    // ...
}

وتحتاج حلقة for لوجود ما يجعلها تكراريّة (iterator) مثل:

  • دالة تابعة للصنف (member function) أو دالة إضافيّة (extension function) مثل iterator()‎ والتي يكون نوعها المُعاد:
    • يحتوي على دالةٍ تابعةٍ للصنف أو دالةٍ إضافيّةٍ next()
    • يحتوي على دالةٍ تابعةٍ للصنف أو دالةٍ إضافيّةٍ hasNext()‎ والتي تعيد قيمة منطقيّة (boolean)

وتُحدَّد هذه الدوال الثلاثة كمعاملات operator.

أمّا عند استخدام حلقة for مع المصفوفات فتُحوَّل إلى حلقةٍ دليليّةٍ (index-based) لا تحتاج إلى إنشاء كائنٍ تكراريّ (iteration object) إذ يتمّ التكرار في الحلقة عبر الدليل (index) مثل:

for (i in array.indices) {
    print(array[i])
}

أو قد تُستدعى الدالة withIndex من المكتبة لتصبح الشيفرة بالشكل الآتي:

for ((index, value) in array.withIndex()) {
    println("the element at $index is $value")
}

تعبير while

تعمل كلٌّ من حلقتيّ while و do..while كما في أيّة لغة برمجة أخرى، مثل:

while (x > 0) {
    x--
}

do {
    val y = retrieveData()
} while (y != null) // يمكن الوصول إلى المتغير هنا

مصادر