يشكّل هذا الدرس التطبيقي التدريبي جزءًا من الدورة التدريبية لبرمجة مطوّري البرامج في Kolin. ستحصل على أقصى قيمة ممكنة من هذه الدورة التدريبية إذا كنت تستخدم الدروس التطبيقية حول الترميز بشكل متسلسل. بناءً على معلوماتك، قد تتمكّن من تصفّح بعض الأقسام. هذه الدورة التدريبية موجّهة للمبرمجين الذين يعرفون لغة موجّهة نحو الأغراض ويريدون تعلّم لغة Kotlin.
مقدمة
في هذا الدرس التطبيقي حول الترميز، يمكنك إنشاء برنامج Kotlin والتعرّف على الدوال في Kotlin، بما في ذلك القيم التلقائية للمعلّمات والفلاتر واللامدا والدوال المضغوطة.
بدلاً من إنشاء نموذج تطبيق واحد، تم تصميم الدروس في هذه الدورة التدريبية لبناء معرفتك، ولكن تكون شبه مستقلة عن بعضها البعض ليتسنى لك تصفح الأقسام التي تعرفها. لربطهما معًا، تستخدم العديد من الأمثلة مظهر معرض أحواض السمك. وإذا أردت الاطّلاع على القصة الكاملة لأحواض السمك، يمكنك الاطّلاع على الدورة التدريبية بعنوان Kotlin Botcamp for Programmers Udacity.
ما يجب معرفته
- أساسيات لغة برمجة حديثة وموجّهة بشكل ثابت ومكتوبة بشكل ثابت
- كيفية البرمجة من خلال الصفوف والطرق والتعامل مع الاستثناءات بلغة واحدة على الأقل
- كيفية استخدام Kotlin's REPL (Read-Eval-Print Loop) في IntelliJ IDEA
- أساسيات لغة Kotlin، بما في ذلك الأنواع وعوامل التشغيل والحلقات
يهدف هذا الدرس التطبيقي إلى برمجة المبرمجين الذين يعرفون لغة موجّهة إلى الأشخاص ويريدون معرفة المزيد من المعلومات حول لغة Kotlin.
ما ستتعرَّف عليه
- كيفية إنشاء برنامج باستخدام دالة
main()
ووسيطات في IntelliJ IDEA - كيفية استخدام القيم التلقائية والدوال المضغوطة
- كيفية تطبيق الفلاتر على القوائم
- كيفية إنشاء lambda الأساسية والوظائف ذات الترتيب الأعلى
الإجراءات التي ستنفذّها
- يمكنك استخدام REPL لتجربة بعض الرموز.
- يمكنك التعاون مع IntelliJ IDEA لإنشاء برامج Kotlin الأساسية.
في هذه المهمة، يمكنك إنشاء برنامج Kotlin والتعرّف على الدالة main()
، بالإضافة إلى كيفية تمرير الوسيطات إلى برنامج من سطر الأوامر.
قد تتذكر الدالة printHello()
التي أدخلتها في REPL في درس تطبيقي سابق حول الترميز:
fun printHello() {
println ("Hello World")
}
printHello()
⇒ Hello World
ويمكنك تحديد الدوال باستخدام الكلمة الرئيسية fun
، متبوعةً باسم الدالة. وكما هو الحال مع لغات البرمجة الأخرى، فإن الأقواس ()
خاصة بوسيطات الدوال، إن وجدت. استخدِم القوس المعقوف {}
لتوفير رمز للدالة. لا يتوفّر نوع إرجاع لهذه الدالة لأنها لا تعرض أي شيء.
الخطوة 1: إنشاء ملف Kotlin
- افتح IntelliJ IDEA.
- يعرض الجزء المشروع على اليمين في IntelliJ IDEA قائمة بملفات المشاريع والمجلدات. ابحث عن مجلد src وانقر عليه بزر الماوس الأيمن ضمن مرحبًا Kotlin. (يجب أن يكون لديك مشروع Hello Kotlin من الدرس التطبيقي السابق للترميز).
- اختَر New > Kotlin File / Class.
- اترك النوع باسم ملف، وأدخل اسمًا للملف مرحبًا.
- انقر على حسنًا.
ستجد الآن ملفًا في مجلد src يُسمى Hello.kt.
الخطوة 2: إضافة الرمز وتشغيل برنامجك
- وكما هو الحال مع اللغات الأخرى، تحدد دالة
main()
بلغة Kotlin نقطة الدخول للتنفيذ. يتم تمرير أي وسيطات سطر أوامر باعتبارها مصفوفة من السلاسل.
اكتب الرمز التالي أو الصِقه في الملف Hello.kt :
fun main(args: Array<String>) {
println("Hello, world!")
}
مثل الدالة printHello()
السابقة، لا تتضمن هذه الدالة عبارة return
. تعرض كل وظيفة في Kotlin شيءًا، حتى إذا لم يتم تحديد أي شيء بشكل صريح. وبالتالي، تعرض دالة مثل الدالة main()
نوع kotlin.Unit
وهو القيمة التي تستخدمها Kotlin's لقول القيمة.
- لتشغيل البرنامج، انقر على المثلث الأخضر على يمين دالة
main()
. اختَر Run ' HelloKt' من القائمة. - تعمل IntelliJ IDEA على تجميع البرنامج وتشغيله. تظهر النتائج في جزء السجل في أسفل الصفحة، كما هو موضح أدناه.
الخطوة 3: تمرير الوسيطات إلى main()
ولأنّك تشغِّل برنامجك من IntelliJ IDEA وليس من سطر الأوامر، عليك تحديد أي وسيطات للبرنامج بطريقة مختلفة قليلاً.
- اختَر تشغيل >؛ تعديل عمليات الضبط. تظهر نافذة تشغيل/تصحيحات الضبط.
- اكتب
Kotlin!
في الحقل وسيطات البرنامج. - انقر على حسنًا.
الخطوة 4: تغيير الرمز لاستخدام نموذج سلسلة
يُدرج نموذج السلسلة متغيرًا أو تعبيرًا في سلسلة، ويحدّد $
أن جزء من السلسلة سيكون متغيرًا أو تعبيرًا. استخدِم {}
لأقواس متعرجة للتعبير عن التعبير، إن وجد.
- في مرحبًا.kt، يمكنك تغيير رسالة الترحيب لاستخدام الوسيطة الأولى التي يتم تمريرها إلى البرنامج،
args[0]
، بدلاً من"world"
.
fun main(args: Array<String>) {
println("Hello, ${args[0]}")
}
- شغِّل البرنامج، وسيتضمّن النتيجة الوسيطة التي حدّدتها.
⇒ Hello, Kotlin!
في هذه المهمة، يمكنك التعرّف على سبب أهمية كل شيء في لغة Kotlin والسبب في ذلك.
تحتوي بعض اللغات الأخرى على كشوفات، وهي أسطر من الرمز ليس لها قيمة. في لغة Kotlin، يكون كل شيء تقريبًا تعبيرًا وله قيمة، حتى إذا كانت هذه القيمة kotlin.Unit
.
- في مرحبًا، اكتب الرمز في
main()
لتخصيصprintln()
للمتغيّر باسمisUnit
واطبعه. (لا يعرضprintln()
قيمة، لذا يؤدي إلى عرضkotlin.Unit
.)
// Will assign kotlin.Unit
val isUnit = println("This is an expression")
println(isUnit)
- شغِّل برنامجك. يطبع أول
println()
السلسلة"This is an expression"
. يطبعprintln()
الثاني قيمة أول عبارةprintln()
، أيkotlin.Unit
.
⇒ This is an expression kotlin.Unit
- أدخِل رقم
val
يُسمىtemperature
واضبطه على 10. - أعلن عن
val
آخر باسمisHot
وخصّص قيمة العرض لبيانif
/else
إلىisHot
، كما هو موضح في الرمز التالي. ولأنه تعبير، يمكنك استخدام قيمة تعبيرif
فورًا.
val temperature = 10
val isHot = if (temperature > 50) true else false
println(isHot)
⇒ false
- استخدم قيمة تعبير في نموذج السلسلة. يُرجى إضافة رمز للتحقّق من درجة الحرارة لتحديد ما إذا كانت السمكة آمنة أو دافئة جدًا، ثم تشغيل برنامجك.
val temperature = 10
val message = "The water temperature is ${ if (temperature > 50) "too warm" else "OK" }."
println(message)
⇒ The water temperature is OK.
في هذه المهمة، يمكنك الاطّلاع على مزيد من المعلومات حول الدوال في لغة Kotlin والمزيد من المعلومات حول تعبير when
المشروط الذي يكون مفيدًا جدًا.
الخطوة 1: إنشاء بعض الدوال
في هذه الخطوة، جمعت بعض ما تعلمته وأنشأت وظائف باستخدام أنواع مختلفة. يمكنك استبدال محتوى Hello.kt بهذا الرمز الجديد.
- اكتب دالة تُسمى
feedTheFish()
التي تستدعيrandomDay()
للحصول على يوم عشوائي من الأسبوع. استخدِم نموذج سلسلة لطباعةfood
حتى تتمكّن السمكة من تناوله في هذا اليوم. في الوقت الحالي، تتناول السمكة الطعام نفسه يوميًا.
fun feedTheFish() {
val day = randomDay()
val food = "pellets"
println ("Today is $day and the fish eat $food")
}
fun main(args: Array<String>) {
feedTheFish()
}
- يُرجى كتابة الدالة
randomDay()
لاختيار يوم عشوائي من مصفوفة وعرضها.
تستخدم الدالة nextInt()
حدًا صحيحًا، والذي يقيّد الرقم من Random()
إلى 0 إلى 6 لمطابقة المصفوفة week
.
fun randomDay() : String {
val week = arrayOf ("Monday", "Tuesday", "Wednesday", "Thursday",
"Friday", "Saturday", "Sunday")
return week[Random().nextInt(week.size)]
}
- يتم تعريف الدوال
Random()
وnextInt()
فيjava.util.*
. في أعلى الملف، أضِف الاستيراد المطلوب:
import java.util.* // required import
- شغِّل برنامجك وتحقّق من الناتج.
⇒ Today is Tuesday and the fish eat pellets
الخطوة 2: استخدام تعبير الوقت
لتوسيع هذا القسم، يجب تغيير الرمز لاختيار مأكولات مختلفة لأيام مختلفة باستخدام التعبير when
. يتشابه عبارة when
مع switch
في لغات البرمجة الأخرى، ولكن يتم تقسيم when
تلقائيًا في نهاية كل فرع. وتتأكّد أيضًا من أنّ الرمز يشمل كل الفروع في حال التحقّق من تعداد معيّن.
- في مرحبًا، أضِف دالة تسمى
fishFood()
يستغرق يومًا واحدًا مثلString
وتُعرِّض طعام السمك خلال اليوم بصفتهString
. استخدِمwhen()
، حتى تحصل السمكة على طعام معيّن كل يوم. شغِّل البرنامج بضع مرات للاطلاع على مخرجات مختلفة.
fun fishFood (day : String) : String {
var food = ""
when (day) {
"Monday" -> food = "flakes"
"Tuesday" -> food = "pellets"
"Wednesday" -> food = "redworms"
"Thursday" -> food = "granules"
"Friday" -> food = "mosquitoes"
"Saturday" -> food = "lettuce"
"Sunday" -> food = "plankton"
}
return food
}
fun feedTheFish() {
val day = randomDay()
val food = fishFood(day)
println ("Today is $day and the fish eat $food")
}
⇒ Today is Thursday and the fish eat granules
- أضف فرعًا تلقائيًا إلى التعبير
when
باستخدامelse
. لإجراء الاختبار، تأكّد من أن الإعداد التلقائي في برنامجك أحيانًا، أزِل الفرعَينTuesday
وSaturday
.
يضمن امتلاك فرع فرعي تلقائيًا حصولfood
على قيمة قبل إرجاعه، لذلك لا يجب إعداده بعد الآن. بما أن الرمز يُخصِّص الآن سلسلة إلىfood
مرة واحدة فقط، يمكنك تعريفfood
باستخدامval
بدلاً منvar
.
fun fishFood (day : String) : String {
val food : String
when (day) {
"Monday" -> food = "flakes"
"Wednesday" -> food = "redworms"
"Thursday" -> food = "granules"
"Friday" -> food = "mosquitoes"
"Sunday" -> food = "plankton"
else -> food = "nothing"
}
return food
}
- ونظرًا لأن كل تعبير له قيمة، يمكنك جعل هذا الرمز أكثر إيجازًا. يمكنك عرض قيمة التعبير
when
مباشرةً، واستبعاد المتغيّرfood
. قيمة التعبيرwhen
هي قيمة التعبير الأخير للفرع الذي استوفى الشرط.
fun fishFood (day : String) : String {
return when (day) {
"Monday" -> "flakes"
"Wednesday" -> "redworms"
"Thursday" -> "granules"
"Friday" -> "mosquitoes"
"Sunday" -> "plankton"
else -> "nothing"
}
}
يبدو الإصدار النهائي من البرنامج يشبه الرمز التالي.
import java.util.* // required import
fun randomDay() : String {
val week = arrayOf ("Monday", "Tuesday", "Wednesday", "Thursday",
"Friday", "Saturday", "Sunday")
return week[Random().nextInt(week.size)]
}
fun fishFood (day : String) : String {
return when (day) {
"Monday" -> "flakes"
"Wednesday" -> "redworms"
"Thursday" -> "granules"
"Friday" -> "mosquitoes"
"Sunday" -> "plankton"
else -> "nothing"
}
}
fun feedTheFish() {
val day = randomDay()
val food = fishFood(day)
println ("Today is $day and the fish eat $food")
}
fun main(args: Array<String>) {
feedTheFish()
}
في هذه المهمة، ستتعرّف على القيم التلقائية للدوال والطرق. يمكنك أيضًا معرفة معلومات حول الدوال المدمَجة، التي تجعل رمزك أكثر وضوحًا وقابلية للقراءة، ويمكن أن تقلل من عدد مسارات الرمز للاختبار. تُسمى الدوال المدمَجة أيضًا دوال التعبير الفردي.
الخطوة 1: إنشاء قيمة تلقائية للمعلمة
في لغة Kotlin، يمكنك تمرير الوسيطات حسب اسم المعلمة. يمكنك أيضًا تحديد القيم التلقائية للمعلّمات: إذا لم يوفّر المتصل وسيطة، سيتم استخدام القيمة التلقائية. وعندما تكتب طرقًا (دوال عضو)، يعني ذلك أنّه يمكنك تجنّب كتابة الكثير من نُسخ الحمل الزائد من الطريقة نفسها.
- في Hello.kt، اكتب دالة
swim()
مع معلمةString
تُسمىspeed
تطبع سرعة السمكة. المعلَمةspeed
لديها القيمة التلقائية"fast"
.
fun swim(speed: String = "fast") {
println("swimming $speed")
}
- من الدالة
main()
، عليك استدعاء الدالةswim()
بثلاث طرق. يجب أولاً استدعاء الدالة باستخدام القيمة التلقائية. وبعد ذلك، يمكنك استدعاء الدالة وتمرير المعلمةspeed
بدون اسم، ثم استدعاء الدالة عن طريق تسمية المعلمةspeed
.
swim() // uses default speed
swim("slow") // positional argument
swim(speed="turtle-like") // named parameter
⇒ swimming fast swimming slow swimming turtle-like
الخطوة 2: إضافة المعلّمات المطلوبة
إذا لم يتم تحديد قيمة تلقائية للمعلمة، يجب دائمًا تمرير الوسيطة المقابلة.
- في مرحبًا، اكتب دالة
shouldChangeWater()
التي تستخدم ثلاث معلمات:day
وtemperature
ومستوىdirty
. تعرض الدالةtrue
ما إذا كان يجب تغيير الماء، وذلك إذا كان الطقس يوم الأحد، أو إذا كانت درجة الحرارة مرتفعة جدًا، أو إذا كان الماء قذرًا جدًا. يجب اختيار يوم الأسبوع، ولكن درجة الحرارة التلقائية هي 22، ولكن المستوى التلقائي غير المسموح به هو 20.
يمكنك استخدام تعبيرwhen
بدون وسيطة، والذي يعمل بلغة Kotlin في شكل عمليات تحققif/else if
.
fun shouldChangeWater (day: String, temperature: Int = 22, dirty: Int = 20): Boolean {
return when {
temperature > 30 -> true
dirty > 30 -> true
day == "Sunday" -> true
else -> false
}
}
- اتصل بالرقم
shouldChangeWater()
منfeedTheFish()
وقدّم اليوم. لا تحتوي المعلّمةday
على معلمة تلقائية، لذلك يجب تحديد وسيطة. معلمتان أخريان لـshouldChangeWater()
لهما قيم تلقائية، لذلك لا تضطر إلى تمرير الوسيطات لها.
fun feedTheFish() {
val day = randomDay()
val food = fishFood(day)
println ("Today is $day and the fish eat $food")
println("Change water: ${shouldChangeWater(day)}")
}
=> Today is Thursday and the fish eat granules Change water: false
الخطوة 3: إنشاء وظائف مضغوطة
يضيف التعبير when
الذي كتبته في الخطوة السابقة الكثير من المنطق إلى مقدار صغير من الرموز. إذا كنت تريد فك الحزمة قليلاً، أو إذا كانت شروط التحقّق أكثر تعقيدًا، يمكنك استخدام بعض المتغيرات المحلية المشهورة. ولكن طريقة Kotlin لتنفيذ ذلك هي باستخدام الدوال المدمجة.
الدوال المدمَجة أو دوال التعبير الفردي هي نمط شائع في لغة Kotlin. عندما تعرض دالة نتائج تعبير واحد، يمكنك تحديد نص الدالة بعد رمز =
وحذف الأقواس المعقوفة {}
وحذف return
.
- في Hello.kt، أضِف الدوال المضغوطة لاختبار الشروط.
fun isTooHot(temperature: Int) = temperature > 30
fun isDirty(dirty: Int) = dirty > 30
fun isSunday(day: String) = day == "Sunday"
- غيِّر
shouldChangeWater()
لاستدعاء الدوال الجديدة.
fun shouldChangeWater (day: String, temperature: Int = 22, dirty: Int = 20): Boolean {
return when {
isTooHot(temperature) -> true
isDirty(dirty) -> true
isSunday(day) -> true
else -> false
}
}
- شغِّل برنامجك. يجب أن تكون مخرجات
println()
معshouldChangeWater()
هي نفسها كما كانت قبل التبديل إلى استخدام الدوال المضغوطة.
القيم التلقائية
ليس من الضروري أن تكون القيمة التلقائية للمعلّمة قيمة. ويمكن أن تكون دالة أخرى، كما هو موضّح في النموذج الجزئي التالي:
fun shouldChangeWater (day: String, temperature: Int = 22, dirty: Int = getDirtySensorReading()): Boolean {
...
في هذه المهمة، ستتعرّف على بعض المعلومات عن الفلاتر في لغة Kotlin. تُعد الفلاتر طريقة سهلة للحصول على جزء من القائمة استنادًا إلى بعض الشروط.
الخطوة 1: إنشاء فلتر
- في مرحبًا، يمكنك تحديد قائمة بزينات أحواض السمك على المستوى العلوي باستخدام
listOf()
. يمكنك استبدال محتوى مرحبًا.
val decorations = listOf ("rock", "pagoda", "plastic plant", "alligator", "flowerpot")
- أنشئ دالة
main()
جديدة باستخدام خط لطباعة زخارف فقط تبدأ بالحرف '؛p'؛. ويتوفّر رمز شرط الفلتر بين أقواس معقوفة{}
، ويشيرit
إلى كل عنصر أثناء تكرار الفلتر. إذا كان التعبير يعرضtrue
، سيتم تضمين العنصر.
fun main() {
println( decorations.filter {it[0] == 'p'})
}
- شغِّل برنامجك، وستظهر لك النتيجة التالية في نافذة Run (تشغيل):
⇒ [pagoda, plastic plant]
الخطوة 2: مقارنة الفلاتر الحركية والكسولة
إذا كنت معتادًا على استخدام الفلاتر بلغات أخرى، قد تتساءل عما إذا كانت الفلاتر في Kotlin متحمّسة أم كسولة. هل يتم إنشاء قائمة النتائج فورًا أم الوصول إلى القائمة؟ في لغة Kotlin، يحدث ذلك بالطريقة التي تحتاجها. حسب الإعدادات التلقائية، يكون filter
متحمّسًا لذا يتم إنشاء قائمة في كل مرة تستخدم فيها الفلتر.
لجعل الفلتر كسول، يمكنك استخدام Sequence
، وهي مجموعة يمكنها الاطّلاع على عنصر واحد فقط في كل مرة، بدءًا من البداية والنهاية. بسهولة، هذه هي واجهة برمجة التطبيقات التي يحتاج إليها الفلتر الكسول.
- في مرحبًا.، غيِّر رمزك لتخصيص القائمة التي تمت فلترتها لمتغير يُسمى
eager
، ثم طباعتها.
fun main() {
val decorations = listOf ("rock", "pagoda", "plastic plant", "alligator", "flowerpot")
// eager, creates a new list
val eager = decorations.filter { it [0] == 'p' }
println("eager: " + eager)
- وأسفل هذا الرمز، قيِّم الفلتر باستخدام
Sequence
معasSequence()
. خصِّص التسلسل إلى متغيّر باسمfiltered
، ثم اطبعه.
// lazy, will wait until asked to evaluate
val filtered = decorations.asSequence().filter { it[0] == 'p' }
println("filtered: " + filtered)
عندما تعرِض نتائج الفلتر على شكل Sequence
، لن يحتفظ المتغيّر filtered
بقائمة جديدة، وسيتمكّن Sequence
' من الاحتفاظ بمعلومات عناصر القائمة ومعرفة الفلتر لتطبيقها على تلك العناصر. عند الوصول إلى عناصر Sequence
، يتم تطبيق الفلتر، ويتم عرض النتيجة لك.
- فرض تقييم التسلسل من خلال تحويله إلى
List
باستخدامtoList()
. اطبع النتيجة.
// force evaluation of the lazy list
val newList = filtered.toList()
println("new list: " + newList)
- شغِّل برنامجك ولاحظ النتائج.
⇒ eager: [pagoda, plastic plant] filtered: kotlin.sequences.FilteringSequence@386cc1c4 new list: [pagoda, plastic plant]
لتصور ما يحدث في Sequence
والتقييم الكسول، استخدِم الدالة map()
. تقدّم الدالة map()
تحويلاً بسيطًا لكل عنصر في التسلسل.
- باستخدام قائمة
decorations
نفسها كما هو موضّح أعلاه، يمكنك إجراء تحويل باستخدامmap()
الذي لا يُنفِّذ أيّ إجراء، وعرض العنصر الذي تم تمريره ببساطة. أضِفprintln()
لإظهار كل مرة يتم فيها الوصول إلى عنصر، وحدِّد التسلسل لمتغيّر يُسمىlazyMap
.
val lazyMap = decorations.asSequence().map {
println("access: $it")
it
}
- يمكنك طباعة
lazyMap
، وطباعة العنصر الأول منlazyMap
باستخدامfirst()
، وطباعةlazyMap
إلىList
.
println("lazy: $lazyMap")
println("-----")
println("first: ${lazyMap.first()}")
println("-----")
println("all: ${lazyMap.toList()}")
- شغِّل برنامجك ولاحظ النتيجة. لا تشمل طباعة
lazyMap
إلا الإشارة إلىSequence
، ولا يتم طلب الجزء الداخلي منprintln()
. تؤدي طباعة العنصر الأول إلى الوصول إلى العنصر الأول فقط. يؤدي تحويلSequence
إلىList
إلى الوصول إلى جميع العناصر.
⇒ lazy: kotlin.sequences.TransformingSequence@5ba23b66 ----- access: rock first: rock ----- access: rock access: pagoda access: plastic plant access: alligator access: flowerpot all: [rock, pagoda, plastic plant, alligator, flowerpot]
- أنشئ
Sequence
جديدة باستخدام الفلتر الأصلي قبل تطبيقmap
. اطبع هذه النتيجة.
val lazyMap2 = decorations.asSequence().filter {it[0] == 'p'}.map {
println("access: $it")
it
}
println("-----")
println("filtered: ${ lazyMap2.toList() }")
- شغِّل برنامجك ولاحظ النتائج الإضافية. وكما هو الحال مع الحصول على العنصر الأول، يتم استدعاء
println()
الداخلي فقط للعناصر التي يتم الوصول إليها.
⇒ ----- access: pagoda access: plastic plant filtered: [pagoda, plastic plant]
في هذه المهمة، ستحصل على مقدمة عن lambda ووظائف أعلى ترتيب في لغة Kotlin.
مصابيح لامدا
بالإضافة إلى الدوال التقليدية التي تحمل اسمًا عاديًا، فإن لغة البرمجة Kotlin تدعم لغة lambda. lambda هي تعبير يُنشئ وظيفة. ولكن بدلاً من الإعلان عن دالة مُسماة، يمكنك الإعلان عن الدالة التي لا تحمل اسمًا. ويكمن ما يجعل ذلك مفيدًا في أنه يمكن الآن تمرير تعبير lambda كبيانات. بلغات أخرى، يُطلق على lambda اسم الدوال المجهولة المصدر أو القيم الحرفية للدوال أو الأسماء المتشابهة.
الدوال ذات الترتيب الأعلى
يمكنك إنشاء دالة ذات ترتيب أعلى من خلال تمرير لامدا إلى دالة أخرى. في المهمة السابقة، أنشأت دالة ذات ترتيب أعلى تُسمى filter
. لقد أرسلت تعبير lambda التالي إلى filter
كشرط للتحقق:{it[0] == 'p'}
وبالمثل، فإن map
هي وظيفة ذات ترتيب أعلى، وتكون لامدا التي مرّرتها تحوّلاً إلى التطبيق.
الخطوة 1: التعرّف على lambda
- وكما هو الحال مع الدوال المُسمّاة، لامدا يمكن أن تحتوي على معلَمات. بالنسبة إلى مصابيح اللامدا، تقع المعلمات (وأنواعها، إذا لزم الأمر) على يمين ما يُعرف باسم سهم الدالة
->
. ينتقل الرمز الذي يتم تنفيذه إلى يسار سهم الدالة. بعد تعيين lambda إلى متغيّر، يمكنك طلبه تمامًا مثل الدالة.
باستخدام REPL (الأدوات >؛ Kotlin >؛ Kotlin REPL)، جرِّب هذا الرمز:
var dirtyLevel = 20
val waterFilter = { dirty : Int -> dirty / 2}
println(waterFilter(dirtyLevel))
⇒ 10
في هذا المثال، أخذت lambda Int
باسم dirty
، وتعرض dirty / 2
. (لأن الفلترة تزيل الأوساخ).
- ترتبط بنية Kotlin' لأنواع الدوال ببنية lambda بشكل وثيق. استخدِم هذه البنية للإعلان عن متغيّر يحتوي على دالة بشكل واضح:
val waterFilter: (Int) -> Int = { dirty -> dirty / 2 }
في ما يلي الرمز:
- أنشِئ متغيّرًا باسم
waterFilter
. - يمكن أن تكون
waterFilter
أي دالة تستخدمInt
وتعرضInt
. - تَخْصِيصْ لَامْبَا لِـ
waterFilter
. - تعرض لامدا قيمة الوسيطة
dirty
مقسومةً على 2.
لاحظ أنه لا تحتاج إلى تحديد نوع وسيطة lambda بعد الآن. يتم احتساب النوع حسب استنتاج النوع.
الخطوة 2: إنشاء دالة ذات ترتيب أعلى
حتى الآن، تبدو أمثلة lambda تشبه الدوال في الغالب. وتمكّن قوة اللامدا الحقيقية من استخدامها لإنشاء وظائف بترتيب أعلى، حيث تكون الوسيطة إلى إحدى الوظائف وظيفة أخرى.
- اكتب دالة ذات ترتيب أعلى. في ما يلي مثال أساسي لدالة تتطلب وسيطتين. الوسيطة الأولى عدد صحيح. الوسيطة الثانية هي دالة تأخذ عددًا صحيحًا وتعرض عددًا صحيحًا. جرِّبها في REPL.
fun updateDirty(dirty: Int, operation: (Int) -> Int): Int {
return operation(dirty)
}
يستدعي نص الرمز الدالة التي تم تمريرها كوسيطة ثانية، ويمرر الوسيطة الأولى إليها.
- لاستدعاء هذه الدالة، أدخلها عددًا صحيحًا ودالة.
val waterFilter: (Int) -> Int = { dirty -> dirty / 2 }
println(updateDirty(30, waterFilter))
⇒ 15
ليس بالضرورة أن تكون الدالة التي تضبطها lambda، بل يمكن أن تكون دالة مُسمّاة عادية بدلاً من ذلك. لتحديد الوسيطة كدالة عادية، استخدِم عامل التشغيل ::
. بهذه الطريقة، يدرك Kotlin أنك تنقل مرجع الدالة كوسيطة، وليس محاولة استدعاء الدالة.
- حاوِل تمرير دالة مُسمّاة عادية إلى
updateDirty()
.
fun increaseDirty( start: Int ) = start + 1
println(updateDirty(15, ::increaseDirty))
⇒ 16
var dirtyLevel = 19;
dirtyLevel = updateDirty(dirtyLevel) { dirtyLevel -> dirtyLevel + 23}
println(dirtyLevel)
⇒ 42
- لإنشاء ملف مصدر Kotlin في IntelliJ IDEA، ابدأ بمشروع Kotlin.
- لتجميع برنامج وتشغيله في IntelliJ IDEA، انقر على المثلث الأخضر بجانب دالة
main()
. يظهر الناتج في نافذة سجل أدناه. - في IntelliJ IDEA، حدّد وسيطات سطر الأوامر لإدخالها في الدالة
main()
في Run > Edit Configurations (تعديل عمليات الضبط). - كل شيء تقريبًا في Kotlin له قيمة. يمكنك استخدام هذه الحقيقة لجعل رمزك أكثر إيجازًا باستخدام قيمة
if
أوwhen
كتعبير أو قيمة للعرض. - وتؤدي الوسيطات التلقائية إلى إلغاء الحاجة إلى إصدارات متعددة من دالة أو طريقة. على سبيل المثال:
fun swim(speed: String = "fast") { ... }
- يمكن أن تؤدي الدوال المصغرة أو وظائف التعبير الفردي إلى جعل الرمز أكثر سهولة في القراءة. على سبيل المثال:
fun isTooHot(temperature: Int) = temperature > 30
- لقد تعلّمت بعض الأساسيات عن الفلاتر التي تستخدم تعبيرات lambda. على سبيل المثال:
val beginsWithP = decorations.filter { it [0] == 'p' }
- تعبير lambda هو تعبير يُنشئ وظيفة بدون اسم. يتم تحديد التعبيرات اللامدا بين أقواس متعرجة
{}
. - في دالة ذات ترتيب أعلى، يتم تمرير دالة مثل تعبير lambda إلى دالة أخرى كبيانات. على سبيل المثال:
dirtyLevel = updateDirty(dirtyLevel) { dirtyLevel -> dirtyLevel + 23}
هناك الكثير في هذا الدرس، خاصةً إذا كنت مستخدمًا جديدًا لمصابيح لامدا. ينتقل درس لاحق إلى lambda والوظائف الأعلى ترتيبًا.
مستندات Kotlin
إذا كنت بحاجة إلى مزيد من المعلومات عن أي موضوع في هذه الدورة التدريبية، أو إذا واجهتك مشكلة، يمكنك البدء باستخدام https://kotlinlang.org.
- نماذج السلاسل
- التعبير
when
- دوال التعبير الفردي
- وظائف ذات ترتيب أعلى وlambda
- الفلاتر
- التسلسلات
- بنية طلب آخر معلّمة
برامج تعليمية بلغة Kotlin
يتضمّن الموقع الإلكتروني https://try.kotlinlang.org برامج تعليمية غنية اسمها Kotlin Koans ومترجمًا فوريًا مستندًا إلى الويب ومجموعة كاملة من المستندات المرجعية التي تتضمّن أمثلة.
دورة Udacity التدريبية
للاطّلاع على دورة Udacity التدريبية حول هذا الموضوع، يُرجى الاطّلاع على برنامج Kotlin Campus للمبرمجين.
IntelliJ IDEA
يمكن العثور على مستندات IntelliJ IDEA على الموقع الإلكتروني JetBrains.
يسرد هذا القسم المهام الدراسية المحتملة للطلاب الذين يعملون من خلال هذا الدرس التطبيقي حول الترميز في إطار دورة تدريبية يُديرها معلِّم. يجب أن ينفِّذ المعلّم ما يلي:
- يمكنك تخصيص واجب منزلي إذا لزم الأمر.
- التواصل مع الطلاب بشأن كيفية إرسال الواجبات المنزلية
- وضع درجات للواجبات المنزلية.
ويمكن للمعلّمين استخدام هذه الاقتراحات بقدر ما يريدون أو بقدر ما يريدون، ويجب عدم التردد في تخصيص أي واجبات منزلية أخرى مناسبة.
إذا كنت تستخدم هذا الدرس التطبيقي بنفسك، يمكنك استخدام هذه الواجبات المنزلية لاختبار معلوماتك.
الإجابة عن هذه الأسئلة
السؤال 1
تعرض الدالة contains(element: String)
القيمة true
إذا كانت السلسلة element
مضمّنة في السلسلة التي تم استدعاءها. ما نتيجة الرمز التالي؟
val decorations = listOf ("rock", "pagoda", "plastic plant", "alligator", "flowerpot")
println(decorations.filter {it.contains('p')})
▢ [pagoda, plastic, plant]
▢ [pagoda, plastic plant]
▢ [pagoda, plastic plant, flowerpot]
▢ [rock, alligator]
السؤال 2
في تعريف الدالة التالي، ما هي المعلّمات المطلوبة؟fun shouldChangeWater (day: String, temperature: Int = 22, dirty: Int = 20, numDecorations: Int = 0): Boolean {...}
▢ numDecorations
▢ dirty
▢ day
▢ temperature
السؤال 3
يمكنك تمرير دالة مُسماة عادية (وليس نتيجة استدعاءها) إلى دالة أخرى. كيف تمرِّر increaseDirty( start: Int ) = start + 1
إلى updateDirty(dirty: Int, operation: (Int) -> Int)
؟
▢ updateDirty(15, &increaseDirty())
▢ updateDirty(15, increaseDirty())
▢ updateDirty(15, ("increaseDirty()"))
▢ updateDirty(15, ::increaseDirty)
انتقل إلى الدرس التالي:
للحصول على نظرة عامة على الدورة التدريبية، بما في ذلك الروابط إلى مختبرات ترميز أخرى، يُرجى الاطّلاع على "Kotlin Botcamp للمبرمجين: مرحبًا بك في الدورة التدريبية&"