Codelab นี้เป็นส่วนหนึ่งของหลักสูตร Kotlin Bootcamp สำหรับโปรแกรมเมอร์ คุณจะได้รับประโยชน์สูงสุดจากหลักสูตรนี้หากทำตาม Codelab ตามลำดับ คุณอาจข้ามบางส่วนได้ ทั้งนี้ขึ้นอยู่กับความรู้ของคุณ หลักสูตรนี้เหมาะสำหรับโปรแกรมเมอร์ที่รู้จักภาษาเชิงวัตถุและต้องการเรียนรู้ Kotlin
บทนำ
ใน Codelab นี้ คุณจะได้สร้างโปรแกรม Kotlin และเรียนรู้เกี่ยวกับฟังก์ชันใน Kotlin ซึ่งรวมถึงค่าเริ่มต้นสำหรับพารามิเตอร์ ตัวกรอง แลมบ์ดา และฟังก์ชันแบบย่อ
บทเรียนในหลักสูตรนี้ได้รับการออกแบบมาเพื่อสร้างความรู้ของคุณ แต่จะมีความเป็นอิสระจากกันในระดับหนึ่งเพื่อให้คุณข้ามส่วนที่คุณคุ้นเคยได้ แทนที่จะสร้างแอปตัวอย่างเพียงแอปเดียว ตัวอย่างหลายรายการใช้ธีมตู้ปลาเพื่อเชื่อมโยงตัวอย่างต่างๆ เข้าด้วยกัน และหากต้องการดูเรื่องราวทั้งหมดของตู้ปลา ให้ดูหลักสูตร Kotlin Bootcamp for Programmers ของ Udacity
สิ่งที่คุณควรทราบอยู่แล้ว
- พื้นฐานของภาษาโปรแกรมเชิงวัตถุที่ทันสมัยซึ่งมีการพิมพ์แบบคงที่
- วิธีเขียนโปรแกรมด้วยคลาส เมธอด และการจัดการข้อยกเว้นในภาษาอย่างน้อย 1 ภาษา
- วิธีทำงานกับ 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
- แผงโปรเจ็กต์ทางด้านซ้ายใน IntelliJ IDEA จะแสดงรายการไฟล์และโฟลเดอร์ของโปรเจ็กต์ ค้นหาและคลิกขวาที่โฟลเดอร์ src ใน Hello Kotlin (คุณควรมีโปรเจ็กต์ Hello Kotlin จาก Codelab ก่อนหน้านี้อยู่แล้ว)
- เลือก New > Kotlin File / Class
- ตั้งค่าประเภทเป็นไฟล์ และตั้งชื่อไฟล์เป็น Hello
- คลิกตกลง
ตอนนี้มีไฟล์ชื่อ Hello.kt อยู่ในโฟลเดอร์ src แล้ว

ขั้นตอนที่ 2: เพิ่มโค้ดและเรียกใช้โปรแกรม
- เช่นเดียวกับภาษาอื่นๆ ฟังก์ชัน
main()ของ Kotlin จะระบุจุดแรกเข้าสำหรับการดำเนินการ อาร์กิวเมนต์บรรทัดคำสั่งจะส่งผ่านเป็นอาร์เรย์ของสตริง
พิมพ์หรือวางโค้ดต่อไปนี้ลงในไฟล์ Hello.kt
fun main(args: Array<String>) {
println("Hello, world!")
}ฟังก์ชันนี้ไม่มีคำสั่ง return เช่นเดียวกับฟังก์ชัน printHello() ก่อนหน้านี้ ทุกฟังก์ชันใน Kotlin จะแสดงผลบางอย่าง แม้ว่าจะไม่ได้ระบุอย่างชัดเจนก็ตาม ดังนั้นฟังก์ชันเช่นฟังก์ชัน main() นี้จะแสดงผลประเภท kotlin.Unit ซึ่งเป็นวิธีของ Kotlin ในการระบุว่าไม่มีค่า
- หากต้องการเรียกใช้โปรแกรม ให้คลิกสามเหลี่ยมสีเขียวทางด้านซ้ายของฟังก์ชัน
main()เลือกเรียกใช้ "HelloKt" จากเมนู - IntelliJ IDEA จะคอมไพล์โปรแกรมและเรียกใช้ ผลลัพธ์จะปรากฏในแผงบันทึกที่ด้านล่าง ดังที่แสดงด้านล่าง

ขั้นตอนที่ 3: ส่งอาร์กิวเมนต์ไปยัง main()
เนื่องจากคุณเรียกใช้โปรแกรมจาก IntelliJ IDEA ไม่ใช่จากบรรทัดคำสั่ง คุณจึงต้องระบุอาร์กิวเมนต์ใดๆ ให้กับโปรแกรมในลักษณะที่แตกต่างออกไปเล็กน้อย
- เลือกเรียกใช้ > แก้ไขการกำหนดค่า หน้าต่างการกำหนดค่าการเรียกใช้/การแก้ไขข้อบกพร่องจะเปิดขึ้น
- พิมพ์
Kotlin!ในช่องอาร์กิวเมนต์ของโปรแกรม - คลิกตกลง

ขั้นตอนที่ 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()ที่ 2 จะพิมพ์ค่าของคำสั่ง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()เพื่อให้ปลาได้รับอาหารที่เฉพาะเจาะจงในแต่ละวัน เรียกใช้โปรแกรม 2-3 ครั้งเพื่อดูเอาต์พุตต่างๆ
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()ได้ 3 วิธี ก่อนอื่น ให้เรียกใช้ฟังก์ชันโดยใช้ค่าเริ่มต้น จากนั้นเรียกใช้ฟังก์ชันและส่งพารามิเตอร์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()ที่ใช้พารามิเตอร์ 3 รายการ ได้แก่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ไม่มีค่าเริ่มต้น คุณจึงต้องระบุอาร์กิวเมนต์ พารามิเตอร์อีก 2 รายการของ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'})
}- เรียกใช้โปรแกรม แล้วคุณจะเห็นเอาต์พุตต่อไปนี้ในหน้าต่างเรียกใช้
⇒ [pagoda, plastic plant]
ขั้นตอนที่ 2: เปรียบเทียบตัวกรองแบบ Eager และ Lazy
หากคุ้นเคยกับตัวกรองในภาษาอื่นๆ คุณอาจสงสัยว่าตัวกรองใน Kotlin เป็นแบบกระตือรือร้นหรือขี้เกียจ ระบบจะสร้างรายการผลลัพธ์ทันทีหรือเมื่อมีการเข้าถึงรายการ ใน Kotlin จะเกิดเหตุการณ์ขึ้นไม่ว่าคุณจะต้องการให้เกิดในลักษณะใดก็ตาม โดยค่าเริ่มต้น filter จะกระตือรือร้น และทุกครั้งที่คุณใช้ตัวกรอง ระบบจะสร้างรายการ
หากต้องการทำให้ตัวกรองทำงานแบบเลซี คุณสามารถใช้ Sequence ซึ่งเป็นคอลเล็กชันที่ดูได้ทีละ 1 รายการเท่านั้น โดยเริ่มจากต้นทางและไปจนถึงปลายทาง ซึ่ง API นี้เป็น 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]
ในงานนี้ คุณจะได้รู้จักแลมบ์ดาและฟังก์ชันลำดับสูงใน Kotlin
Lambda
นอกจากฟังก์ชันที่มีชื่อแบบดั้งเดิมแล้ว Kotlin ยังรองรับ Lambda ด้วย แลมบ์ดาคือนิพจน์ที่สร้างฟังก์ชัน แต่แทนที่จะประกาศฟังก์ชันที่มีชื่อ คุณจะประกาศฟังก์ชันที่ไม่มีชื่อ ส่วนหนึ่งที่ทำให้ฟีเจอร์นี้มีประโยชน์คือตอนนี้คุณสามารถส่งนิพจน์ Lambda เป็นข้อมูลได้แล้ว ในภาษาอื่นๆ แลมบ์ดาเรียกว่าฟังก์ชันที่ไม่ระบุชื่อ ฟังก์ชันลิเทอรัล หรือชื่อที่คล้ายกัน
ฟังก์ชันลำดับที่สูงกว่า
คุณสร้างฟังก์ชันลำดับสูงได้โดยส่งแลมบ์ดาไปยังฟังก์ชันอื่น ในงานก่อนหน้านี้ คุณได้สร้างฟังก์ชันลำดับสูงที่ชื่อ filter คุณส่งนิพจน์ Lambda ต่อไปนี้ไปยัง filter เป็นเงื่อนไขที่จะตรวจสอบ{it[0] == 'p'}
ในทำนองเดียวกัน map ก็เป็นฟังก์ชันลำดับสูง และ Lambda ที่คุณส่งไปยังฟังก์ชันนี้คือการเปลี่ยนรูปแบบที่จะใช้
ขั้นตอนที่ 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 สำหรับประเภทฟังก์ชันมีความเกี่ยวข้องอย่างใกล้ชิดกับไวยากรณ์สำหรับ Lambda ใช้ไวยากรณ์นี้เพื่อประกาศตัวแปรที่เก็บฟังก์ชันอย่างชัดเจน
val waterFilter: (Int) -> Int = { dirty -> dirty / 2 }โค้ดระบุไว้ดังนี้
- สร้างตัวแปรชื่อ
waterFilter waterFilterสามารถเป็นฟังก์ชันใดก็ได้ที่รับIntและแสดงผลInt- กำหนดฟังก์ชัน Lambda ให้กับ
waterFilter - Lambda จะแสดงผลค่าของอาร์กิวเมนต์
dirtyหารด้วย 2
โปรดทราบว่าคุณไม่จำเป็นต้องระบุประเภทของอาร์กิวเมนต์ Lambda อีกต่อไป ระบบจะคำนวณประเภทโดยใช้การอนุมานประเภท
ขั้นตอนที่ 2: สร้างฟังก์ชันลำดับที่สูงกว่า
ที่ผ่านมา ตัวอย่างของ Lambda ส่วนใหญ่มีลักษณะคล้ายฟังก์ชัน ความสามารถที่แท้จริงของแลมบ์ดาคือการใช้เพื่อสร้างฟังก์ชันลำดับสูง ซึ่งอาร์กิวเมนต์ของฟังก์ชันหนึ่งคืออีกฟังก์ชันหนึ่ง
- เขียนฟังก์ชันลำดับสูง ตัวอย่างพื้นฐานต่อไปนี้เป็นฟังก์ชันที่รับอาร์กิวเมนต์ 2 รายการ อาร์กิวเมนต์แรกเป็นจำนวนเต็ม อาร์กิวเมนต์ที่ 2 คือฟังก์ชันที่รับจำนวนเต็มและแสดงผลจำนวนเต็ม ลองใช้ใน REPL
fun updateDirty(dirty: Int, operation: (Int) -> Int): Int {
return operation(dirty)
}ส่วนเนื้อหาของโค้ดจะเรียกใช้ฟังก์ชันที่ส่งผ่านเป็นอาร์กิวเมนต์ที่ 2 และส่งผ่านอาร์กิวเมนต์แรกไปยังฟังก์ชันนั้น
- หากต้องการเรียกใช้ฟังก์ชันนี้ ให้ส่งจำนวนเต็มและฟังก์ชันไปยังฟังก์ชันนี้
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 - คุณได้เรียนรู้ข้อมูลพื้นฐานเกี่ยวกับตัวกรองที่ใช้นิพจน์ Lambda แล้ว เช่น
val beginsWithP = decorations.filter { it [0] == 'p' } - นิพจน์แลมบ์ดาคือนิพจน์ที่สร้างฟังก์ชันที่ไม่มีชื่อ นิพจน์ Lambda จะกำหนดไว้ระหว่างวงเล็บปีกกา
{} - ในฟังก์ชันลำดับสูง คุณจะส่งฟังก์ชัน เช่น นิพจน์ Lambda ไปยังฟังก์ชันอื่นเป็นข้อมูล เช่น
dirtyLevel = updateDirty(dirtyLevel) { dirtyLevel -> dirtyLevel + 23}
บทเรียนนี้มีเนื้อหามากมาย โดยเฉพาะอย่างยิ่งหากคุณเพิ่งเริ่มใช้ Lambda บทเรียนในภายหลังจะกลับมาพูดถึงแลมบ์ดาและฟังก์ชันลำดับสูงอีกครั้ง
เอกสารประกอบ Kotlin
หากต้องการข้อมูลเพิ่มเติมเกี่ยวกับหัวข้อใดก็ตามในหลักสูตรนี้ หรือหากคุณติดขัด https://kotlinlang.org คือจุดเริ่มต้นที่ดีที่สุด
- เทมเพลตสตริง
whenนิพจน์- ฟังก์ชันแบบนิพจน์เดียว
- ฟังก์ชันลำดับสูงและแลมบ์ดา
- ตัวกรอง
- ลำดับ
- ไวยากรณ์การเรียกพารามิเตอร์สุดท้าย
บทแนะนำ Kotlin
เว็บไซต์ https://try.kotlinlang.org มีบทแนะนำที่สมบูรณ์ซึ่งเรียกว่า Kotlin Koans, ตัวแปลภาษาบนเว็บ และชุดเอกสารอ้างอิงที่สมบูรณ์พร้อมตัวอย่าง
หลักสูตร Udacity
หากต้องการดูหลักสูตร Udacity ในหัวข้อนี้ โปรดดูค่ายฝึก Kotlin สำหรับโปรแกรมเมอร์
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)
ไปยังบทเรียนถัดไป:
ดูภาพรวมของหลักสูตร รวมถึงลิงก์ไปยังโค้ดแล็บอื่นๆ ได้ที่ "Kotlin Bootcamp for Programmers: Welcome to the course"