ה-codelab הזה הוא חלק מקורס Kotlin Bootcamp for Programmers. כדי להפיק את המרב מהקורס הזה, מומלץ לעבוד על ה-codelabs לפי הסדר. אם יש לכם ידע בנושא, יכול להיות שתוכלו לדלג על חלק מהקטעים. הקורס הזה מיועד למתכנתים שמכירים שפה מונחית-אובייקטים ורוצים ללמוד Kotlin.
מבוא
ב-codelab הזה תיצרו תוכנית Kotlin ותלמדו על פונקציות ב-Kotlin, כולל ערכי ברירת מחדל לפרמטרים, מסננים, פונקציות למדה ופונקציות קומפקטיות.
במקום ליצור אפליקציה לדוגמה אחת, השיעורים בקורס הזה נועדו להרחיב את הידע שלכם, אבל הם גם עצמאיים למחצה, כך שתוכלו לדלג על חלקים שאתם מכירים. כדי לקשור בין הדוגמאות, רבות מהן מתבססות על נושא האקווריום. אם אתם רוצים לראות את הסיפור המלא של האקווריום, כדאי לעיין בקורס Kotlin Bootcamp for Programmers ב-Udacity.
מה שכדאי לדעת
- היסודות של שפת תכנות מודרנית, מונחית-עצמים ועם הקלדה סטטית
- איך לתכנת עם מחלקות, שיטות וטיפול בחריגים בשפה אחת לפחות
- איך עובדים עם REPL (Read-Eval-Print Loop) של Kotlin ב-IntelliJ IDEA
- היסודות של Kotlin, כולל סוגים, אופרטורים ולולאות
ה-codelab הזה מיועד למתכנתים שמכירים שפה מונחית-עצמים ורוצים ללמוד עוד על Kotlin.
מה תלמדו
- איך יוצרים תוכנית עם פונקציה וארגומנטים של
main()ב-IntelliJ IDEA - איך משתמשים בערכי ברירת מחדל ובפונקציות קומפקטיות
- איך להחיל מסננים על רשימות
- איך יוצרים פונקציות למבדה בסיסיות ופונקציות מסדר גבוה
הפעולות שתבצעו:
- אפשר להשתמש ב-REPL כדי לנסות קוד.
- שימוש ב-IntelliJ IDEA כדי ליצור תוכניות בסיסיות ב-Kotlin.
במשימה הזו תיצרו תוכנית Kotlin ותלמדו על הפונקציה main() ועל האופן שבו מעבירים ארגומנטים לתוכנית משורת הפקודה.
יכול להיות שאתם זוכרים את הפונקציה printHello() שהזנתם ב-REPL ב-codelab קודם:
fun printHello() {
println ("Hello World")
}
printHello()⇒ Hello World
מגדירים פונקציות באמצעות מילת המפתח fun, ואחריה שם הפונקציה. כמו בשפות תכנות אחרות, הסוגריים () משמשים לארגומנטים של פונקציות, אם יש כאלה. הסוגריים המסולסלים {} מסמנים את הקוד של הפונקציה. אין סוג החזרה לפונקציה הזו, כי היא לא מחזירה כלום.
שלב 1: יוצרים קובץ Kotlin
- פותחים את IntelliJ IDEA.
- בחלונית Project בצד ימין ב-IntelliJ IDEA מוצגת רשימה של קבצים ותיקיות בפרויקט. מחפשים את התיקייה src ולוחצים עליה לחיצה ימנית. (אמור להיות לכם כבר פרויקט Hello Kotlin מה-codelab הקודם).
- בוחרים באפשרות New > Kotlin File / Class (חדש > קובץ או מחלקה של Kotlin).
- משאירים את Kind (סוג) כ-File (קובץ) ונותנים לקובץ את השם Hello (שלום).
- לוחצים על אישור.
עכשיו יש קובץ בתיקייה src בשם Hello.kt.

שלב 2: מוסיפים קוד ומריצים את התוכנית
- בדומה לשפות אחרות, הפונקציה
main()של Kotlin מציינת את נקודת הכניסה להרצה. כל הארגומנטים של שורת הפקודה מועברים כמערך של מחרוזות.
מקלידים או מדביקים את הקוד הבא בקובץ Hello.kt :
fun main(args: Array<String>) {
println("Hello, world!")
}בדומה לפונקציה הקודמת printHello(), לפונקציה הזו אין הצהרת return. כל פונקציה ב-Kotlin מחזירה משהו, גם אם לא מצוין משהו באופן מפורש. לכן פונקציה כמו main() מחזירה סוג kotlin.Unit, שזו הדרך של Kotlin לציין שאין ערך.
- כדי להריץ את התוכנית, לוחצים על המשולש הירוק שמימין לפונקציה
main(). בתפריט, בוחרים באפשרות Run 'HelloKt' (הפעלת HelloKt). - IntelliJ IDEA יקמפל את התוכנית ויריץ אותה. התוצאות יופיעו בחלונית יומן בתחתית, כמו שמוצג בהמשך.

שלב 3: העברת ארגומנטים אל main()
מכיוון שאתם מריצים את התוכנית מ-IntelliJ IDEA ולא משורת הפקודה, אתם צריכים לציין את הארגומנטים של התוכנית בצורה קצת שונה.
- בוחרים באפשרות הפעלה > עריכת הגדרות. ייפתח החלון Run/Debug Configurations (הגדרות הרצה/ניפוי באגים).
- מקלידים
Kotlin!בשדה Program arguments (ארגומנטים של התוכנית). - לוחצים על אישור.

שלב 4: משנים את הקוד לשימוש בתבנית מחרוזת
תבנית מחרוזת מוסיפה משתנה או ביטוי למחרוזת, ו$ מציינת שחלק מהמחרוזת יהיה משתנה או ביטוי. סוגריים מסולסלים {} מקיפים את הביטוי, אם יש.
- ב-Hello.kt, משנים את הודעת הפתיחה כך שתשתמש בארגומנט הראשון שמועבר לתוכנית,
args[0], במקום"world".
fun main(args: Array<String>) {
println("Hello, ${args[0]}")
}- מריצים את התוכנית, והפלט כולל את הארגומנט שציינתם.
⇒ Hello, Kotlin!
במשימה הזו נסביר למה כמעט לכל דבר ב-Kotlin יש ערך, ולמה זה שימושי.
בשפות אחרות יש הצהרות, שהן שורות קוד ללא ערך. ב-Kotlin, כמעט כל דבר הוא ביטוי ויש לו ערך – גם אם הערך הוא kotlin.Unit.
- ב-Hello.kt, כותבים קוד ב-
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. ההצהרה when דומה ל-switch בשפות תכנות אחרות, אבל when מפסיקה אוטומטית בסוף כל ענף. בנוסף, אם בודקים enum, הקוד יכסה את כל הענפים.
- ב-Hello.kt, מוסיפים פונקציה בשם
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: מוסיפים את הפרמטרים הנדרשים
אם לא מציינים ערך ברירת מחדל לפרמטר, תמיד צריך להעביר את הארגומנט המתאים.
- ב-Hello.kt, כותבים פונקציה
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: יצירת מסנן
- ב-Hello.kt, מגדירים רשימה של קישוטים לאקווריום ברמה העליונה באמצעות
listOf(). אפשר להחליף את התוכן של Hello.kt.
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 הם eager או lazy. האם רשימת התוצאות נוצרת באופן מיידי או כשניגשים לרשימה? ב-Kotlin, זה קורה בכל דרך שרוצים. כברירת מחדל, filter הוא חמדן, ובכל פעם שמשתמשים במסנן, נוצרת רשימה.
כדי להגדיר את המסנן כעצלן, אפשר להשתמש ב-Sequence, שהוא אוסף שיכול לבדוק רק פריט אחד בכל פעם, החל מההתחלה ועד הסוף. למזלנו, זה בדיוק ה-API שנדרש לסינון עצלני.
- ב-Hello.kt, משנים את הקוד כדי להקצות את הרשימה המסוננת למשתנה בשם
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]
במשימה הזו תקבלו מבוא ל-lambdas ולפונקציות מסדר גבוה ב-Kotlin.
Lambdas
בנוסף לפונקציות בעלות שם רגילות, Kotlin תומכת ב-lambdas. למדה היא ביטוי שיוצר פונקציה. אבל במקום להצהיר על פונקציה בעלת שם, אתם מצהירים על פונקציה ללא שם. אחד היתרונות של השיטה הזו הוא שאפשר להעביר את ביטוי ה-lambda כנתונים. בשפות אחרות, פונקציות למדה נקראות פונקציות אנונימיות, פונקציות מילוליות או שמות דומים.
פונקציות מסדר גבוה
אפשר ליצור פונקציה מסדר גבוה יותר על ידי העברת פונקציית lambda לפונקציה אחרת. במשימה הקודמת יצרתם פונקציה מסדר גבוה בשם filter. העברת את ביטוי ה-lambda הבא אל filter כתנאי לבדיקה:{it[0] == 'p'}
באופן דומה, map היא פונקציה מסדר גבוה, והלמדה שהעברתם אליה הייתה הטרנספורמציה להחלה.
שלב 1: מידע על פונקציות למדא
- בדומה לפונקציות בעלות שם, לפונקציות למדה יכולים להיות פרמטרים. בפונקציות למדה, הפרמטרים (והסוגים שלהם, אם צריך) מופיעים משמאל למה שנקרא חץ פונקציה
->. הקוד להרצה מופיע משמאל לחץ הפונקציה. אחרי שמקצים את ה-lambda למשתנה, אפשר לקרוא לו בדיוק כמו לפונקציה.
אפשר לנסות את הקוד הזה באמצעות REPL (Tools > Kotlin > Kotlin REPL):
var dirtyLevel = 20
val waterFilter = { dirty : Int -> dirty / 2}
println(waterFilter(dirtyLevel))⇒ 10
בדוגמה הזו, פונקציית ה-lambda מקבלת את הארגומנט Int שנקרא dirty ומחזירה את הערך dirty / 2. (כי הסינון מסיר את הלכלוך).
- התחביר של Kotlin לסוגי פונקציות קשור קשר הדוק לתחביר של lambdas. משתמשים בתחביר הזה כדי להצהיר בצורה נקייה על משתנה שמכיל פונקציה:
val waterFilter: (Int) -> Int = { dirty -> dirty / 2 }הנה מה שכתוב בקוד:
- יוצרים משתנה בשם
waterFilter. -
waterFilterיכולה להיות כל פונקציה שמקבלתIntומחזירהInt. - מקצים את פונקציית ה-lambda ל-
waterFilter. - פונקציית ה-lambda מחזירה את הערך של הארגומנט
dirtyחלקי 2.
שימו לב שכבר לא צריך לציין את הסוג של ארגומנט ה-lambda. הסוג מחושב על ידי היסק סוגים.
שלב 2: יצירת פונקציה מסדר גבוה
עד עכשיו, הדוגמאות ל-lambdas נראות בעיקר כמו פונקציות. היתרון האמיתי של פונקציות למדא הוא השימוש בהן ליצירת פונקציות מסדר גבוה, שבהן הארגומנט של פונקציה אחת הוא פונקציה אחרת.
- לכתוב פונקציה מסדר גבוה. הנה דוגמה בסיסית, פונקציה שמקבלת שני ארגומנטים. הארגומנט הראשון הוא מספר שלם. הארגומנט השני הוא פונקציה שמקבלת מספר שלם ומחזירה מספר שלם. כדאי לנסות את זה ב-REPL.
fun updateDirty(dirty: Int, operation: (Int) -> Int): Int {
return operation(dirty)
}גוף הקוד קורא לפונקציה שהועברה כארגומנט השני, ומעביר אליה את הארגומנט הראשון.
- כדי לקרוא לפונקציה הזו, מעבירים לה מספר שלם ופונקציה.
val waterFilter: (Int) -> Int = { dirty -> dirty / 2 }
println(updateDirty(30, waterFilter))⇒ 15
הפונקציה שמעבירים לא חייבת להיות למדה, היא יכולה להיות פונקציה רגילה בעלת שם. כדי לציין את הארגומנט כפונקציה רגילה, משתמשים באופרטור ::. כך מערכת 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 - למדתם כמה דברים בסיסיים על מסננים, שמשתמשים בביטויי למבדה. לדוגמה:
val beginsWithP = decorations.filter { it [0] == 'p' } - ביטוי למדה הוא ביטוי שיוצר פונקציה ללא שם. ביטויי Lambda מוגדרים בין סוגריים מסולסלים
{}. - בפונקציה מסדר גבוה, מעבירים פונקציה כמו ביטוי למבדה לפונקציה אחרת כנתונים. לדוגמה:
dirtyLevel = updateDirty(dirtyLevel) { dirtyLevel -> dirtyLevel + 23}
יש הרבה מידע בשיעור הזה, במיוחד אם אתם חדשים בנושא של פונקציות למדה. באחד השיעורים הבאים נחזור לנושא של פונקציות למבדה ופונקציות מסדר גבוה.
תיעוד של Kotlin
אם אתם רוצים לקבל מידע נוסף על נושא כלשהו בקורס הזה, או אם נתקעתם, https://kotlinlang.org הוא המקום הכי טוב להתחיל בו.
- תבניות של מחרוזות
- ביטוי
when - פונקציות של ביטוי יחיד
- פונקציות מסדר גבוה ופונקציות למבדה
- מסננים
- רצפים
- תחביר של קריאה לפרמטר האחרון
מדריכים ל-Kotlin
באתר https://try.kotlinlang.org יש הדרכות מפורטות שנקראות Kotlin Koans, מפרש מבוסס-אינטרנט וסט מלא של מסמכי עזר עם דוגמאות.
קורס של Udacity
כדי לצפות בקורס של Udacity בנושא הזה, אפשר לעבור אל Kotlin Bootcamp for Programmers.
IntelliJ IDEA
מסמכי התיעוד של IntelliJ IDEA זמינים באתר JetBrains.
בקטע הזה מפורטות אפשרויות למשימות ביתיות לתלמידים שעובדים על ה-Codelab הזה כחלק מקורס בהנחיית מדריך. המורה צריך:
- אם צריך, מקצים שיעורי בית.
- להסביר לתלמידים איך להגיש מטלות.
- בודקים את שיעורי הבית.
אנשי ההוראה יכולים להשתמש בהצעות האלה כמה שרוצים, ומומלץ להם להקצות כל שיעורי בית אחרים שהם חושבים שמתאימים.
אם אתם עובדים על ה-codelab הזה לבד, אתם יכולים להשתמש במשימות האלה כדי לבדוק את הידע שלכם.
עונים על השאלות הבאות
שאלה 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)
עוברים לשיעור הבא:
סקירה כללית של הקורס, כולל קישורים ל-Codelabs אחרים, זמינה במאמר "Kotlin Bootcamp for Programmers: Welcome to the course".