Kotlin Bootcamp for Programmers 3: Functions

این Codelab بخشی از دوره Kotlin Bootcamp برای برنامه نویسان است . اگر به ترتیب روی کدها کار کنید، بیشترین ارزش را از این دوره خواهید گرفت. بسته به دانش خود، ممکن است بتوانید برخی از بخش ها را مرور کنید. این دوره برای برنامه نویسانی است که زبان شی گرا را می دانند و می خواهند Kotlin را یاد بگیرند.

مقدمه

در این کد لبه، شما یک برنامه Kotlin ایجاد می کنید و با توابع در Kotlin، از جمله مقادیر پیش فرض پارامترها، فیلترها، لامبداها و توابع فشرده آشنا می شوید.

به جای ساختن یک برنامه نمونه واحد، درس‌های این دوره برای ایجاد دانش شما طراحی شده‌اند، اما نیمه مستقل از یکدیگر باشند تا بتوانید بخش‌هایی را که با آن‌ها آشنا هستید، مرور کنید. برای گره زدن آنها به یکدیگر، بسیاری از نمونه ها از تم آکواریوم استفاده می کنند. و اگر می خواهید داستان کامل آکواریوم را ببینید، دوره Kotlin Bootcamp for Programmers Udacity را بررسی کنید.

آنچه از قبل باید بدانید

  • اصول یک زبان برنامه نویسی مدرن، شی گرا و تایپ ایستا
  • نحوه برنامه نویسی با کلاس ها، متدها و مدیریت استثنا در حداقل یک زبان
  • نحوه کار با Kotlin's REPL (Read-Eval-Print Loop) در IntelliJ IDEA
  • اصول اولیه Kotlin، از جمله انواع، عملگرها و حلقه ها

این کد لبه برای برنامه نویسانی طراحی شده است که یک زبان شی گرا را می دانند و می خواهند در مورد Kotlin اطلاعات بیشتری کسب کنند.

چیزی که یاد خواهید گرفت

  • نحوه ایجاد یک برنامه با تابع main() و آرگومان در IntelliJ IDEA
  • نحوه استفاده از مقادیر پیش فرض و توابع فشرده
  • نحوه اعمال فیلتر برای لیست ها
  • نحوه ایجاد لامبداهای پایه و توابع درجه بالاتر

کاری که خواهی کرد

  • برای امتحان کردن کد با REPL کار کنید.
  • برای ایجاد برنامه های اساسی Kotlin با IntelliJ IDEA کار کنید.

در این کار، یک برنامه Kotlin ایجاد می‌کنید و با تابع main() و همچنین نحوه ارسال آرگومان‌ها به برنامه از خط فرمان آشنا می‌شوید.

ممکن است تابع printHello() را که در REPL در لبه کد قبلی وارد کرده بودید به خاطر بیاورید:

fun printHello() {
    println ("Hello World")
}

printHello()
⇒ Hello World

توابع را با استفاده از کلمه کلیدی fun و به دنبال آن نام تابع تعریف می کنید. مانند سایر زبان های برنامه نویسی، پرانتز () برای آرگومان های تابع، در صورت وجود، است. پرانتزهای فرفری {} کد تابع را قاب می کنند. هیچ نوع بازگشتی برای این تابع وجود ندارد، زیرا چیزی بر نمی گرداند.

مرحله 1: یک فایل Kotlin ایجاد کنید

  1. IntelliJ IDEA را باز کنید.
  2. پنجره Project در سمت چپ IntelliJ IDEA لیستی از فایل ها و پوشه های پروژه شما را نشان می دهد. پوشه src را در زیر Hello Kotlin پیدا کرده و کلیک راست کنید. (شما از قبل باید پروژه Hello Kotlin را از نرم افزار کد قبلی داشته باشید.)
  3. New > Kotlin File / Class را انتخاب کنید.
  4. Kind as File را نگه دارید و نام فایل را Hello بگذارید .
  5. روی OK کلیک کنید.

اکنون یک فایل در پوشه src به نام Hello.kt وجود دارد.

مرحله 2: کد اضافه کنید و برنامه خود را اجرا کنید

  1. همانند سایر زبان ها، تابع main() Kotlin نقطه ورودی را برای اجرا مشخص می کند. هر آرگومان خط فرمان به عنوان آرایه ای از رشته ها ارسال می شود.

    کد زیر را تایپ کرده یا در فایل Hello.kt قرار دهید :
fun main(args: Array<String>) {
    println("Hello, world!")
}

مانند تابع printHello() قبلی شما، این تابع هیچ عبارت return ندارد. هر تابع در Kotlin چیزی را برمی گرداند، حتی زمانی که هیچ چیز به صراحت مشخص نشده باشد. بنابراین تابعی مانند این تابع main() یک نوع kotlin.Unit را برمی‌گرداند، که روش کاتلین برای گفتن هیچ مقدار است.

  1. برای اجرای برنامه خود، روی مثلث سبز رنگ سمت چپ تابع main() کلیک کنید. Run 'HelloKt' را از منو انتخاب کنید.
  2. IntelliJ IDEA برنامه را کامپایل کرده و اجرا می کند. نتایج در یک صفحه گزارش در پایین ظاهر می شود، همانطور که در زیر نشان داده شده است.

مرحله 3: ارسال آرگومان ها به main()

از آنجایی که شما برنامه خود را از IntelliJ IDEA اجرا می کنید و نه از خط فرمان، باید هر آرگومان را برای برنامه کمی متفاوت مشخص کنید.

  1. Run > Edit Configurations را انتخاب کنید. پنجره Run/Debug Configurations باز می شود.
  2. Kotlin! در قسمت Program arguments .
  3. روی OK کلیک کنید.

مرحله 4: کد را برای استفاده از یک قالب رشته تغییر دهید

یک قالب رشته یک متغیر یا عبارت را در یک رشته وارد می کند و $ مشخص می کند که بخشی از رشته یک متغیر یا عبارت باشد. در صورت وجود، پرانتزهای فرفری {} عبارت را قاب می‌کنند.

  1. در Hello.kt ، پیام تبریک را تغییر دهید تا از اولین آرگومان ارسال شده به برنامه، args[0] به جای "world" استفاده کنید.
fun main(args: Array<String>) {
    println("Hello, ${args[0]}")
}
  1. برنامه را اجرا کنید و خروجی شامل آرگومانی است که شما مشخص کرده اید.
⇒ Hello, Kotlin!

در این کار، می آموزید که چرا تقریباً همه چیز در کاتلین ارزش دارد و چرا این کار مفید است.

برخی از زبان‌های دیگر عبارت‌هایی دارند که خطوط کدی هستند که مقداری ندارند. در Kotlin، تقریباً همه چیز یک عبارت است و یک مقدار دارد - حتی اگر آن مقدار kotlin.Unit باشد.

  1. در Hello.kt کدی را در main() بنویسید تا یک println() به متغیری به نام isUnit و آن را چاپ کنید. ( println() مقداری را برمی گرداند، بنابراین kotlin.Unit را برمی گرداند.)
// Will assign kotlin.Unit
val isUnit = println("This is an expression")
println(isUnit)
  1. برنامه خود را اجرا کنید اولین println() رشته "This is an expression" را چاپ می کند. دومین println() مقدار اولین دستور println() () را چاپ می کند، یعنی kotlin.Unit .
⇒ This is an expression
kotlin.Unit
  1. یک val به نام temperature اعلام کنید و آن را به 10 مقداردهی کنید.
  2. مقدار دیگری به نام val را اعلام کنید و مقدار بازگشتی یک عبارت if / else را همانطور که در کد زیر نشان داده شده است به isHot isHot . از آنجایی که یک عبارت است، می توانید بلافاصله از مقدار عبارت if استفاده کنید.
val temperature = 10
val isHot = if (temperature > 50) true else false
println(isHot)
⇒ false
  1. از مقدار یک عبارت در قالب رشته استفاده کنید. مقداری کد اضافه کنید تا دما را بررسی کنید تا بفهمید ماهی ایمن است یا خیلی گرم است، سپس برنامه خود را اجرا کنید.
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 را با این کد جدید جایگزین کنید.

  1. تابعی به نام 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()
}
  1. تابع 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)]
}
  1. توابع Random() و nextInt() در java.util.* تعریف شده اند. در بالای فایل، وارد کردن مورد نیاز را اضافه کنید:
import java.util.*    // required import
  1. برنامه خود را اجرا کنید و خروجی را بررسی کنید.
⇒ Today is Tuesday and the fish eat pellets

مرحله 2: از عبارت When استفاده کنید

با گسترش بیشتر، کد را برای انتخاب غذاهای مختلف برای روزهای مختلف با استفاده از عبارت when تغییر دهید. دستور when مشابه switch در سایر زبان های برنامه نویسی است، اما when به طور خودکار در انتهای هر شاخه شکسته می شود. همچنین مطمئن می شود که کد شما تمام شاخه ها را پوشش می دهد اگر در حال بررسی یک enum هستید.

  1. در 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
  1. یک شاخه پیش فرض را به عبارت when با استفاده از else اضافه کنید. برای تست، برای اینکه مطمئن شوید گاهی اوقات پیش فرض در برنامه شما گرفته شده است، شاخه های Tuesday و Saturday را حذف کنید.

    داشتن یک شاخه پیش فرض تضمین می کند که food قبل از بازگرداندن مقداری دریافت می کند، بنابراین دیگر نیازی به مقداردهی اولیه نیست. از آنجایی که کد اکنون فقط یک بار به food یک رشته اختصاص می دهد، می توانید food را به جای var با val اعلان کنید.
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
}
  1. از آنجا که هر عبارت دارای یک مقدار است، می توانید این کد را کمی مختصرتر کنید. مقدار عبارت 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، می توانید آرگومان ها را با نام پارامتر ارسال کنید. همچنین می‌توانید مقادیر پیش‌فرض را برای پارامترها مشخص کنید: اگر آرگومان توسط تماس‌گیرنده ارائه نشود، از مقدار پیش‌فرض استفاده می‌شود. بعداً، وقتی متدها (توابع عضو) را می‌نویسید، به این معنی است که می‌توانید از نوشتن نسخه‌های اضافه بار زیادی از همان روش اجتناب کنید.

  1. در Hello.kt ، یک تابع swim() با پارامتر String به نام speed بنویسید که سرعت ماهی را چاپ می کند. پارامتر speed دارای مقدار پیش فرض "fast" است.
fun swim(speed: String = "fast") {
   println("swimming $speed")
}
  1. از تابع 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: پارامترهای مورد نیاز را اضافه کنید

اگر هیچ پیش فرضی برای یک پارامتر مشخص نشده باشد، آرگومان مربوطه باید همیشه ارسال شود.

  1. در 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
    }
}
  1. با shouldChangeWater() 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 که در مرحله قبل نوشتید، منطق زیادی را در مقدار کمی کد بسته بندی می کند. اگر می‌خواهید آن را کمی باز کنید، یا اگر شرایط بررسی پیچیده‌تر بود، می‌توانید از چند متغیر محلی با نام خوب استفاده کنید. اما راه کاتلین برای انجام آن با توابع فشرده است.

توابع فشرده یا توابع تک بیانی ، یک الگوی رایج در کاتلین هستند. هنگامی که یک تابع نتایج یک عبارت واحد را برمی گرداند، می توانید بدنه تابع را بعد از علامت = مشخص کنید، پرانتزهای فرفری {} را حذف کنید و return را حذف کنید.

  1. در Hello.kt ، توابع فشرده را برای آزمایش شرایط اضافه کنید.
fun isTooHot(temperature: Int) = temperature > 30

fun isDirty(dirty: Int) = dirty > 30

fun isSunday(day: String) = day == "Sunday"
  1. برای فراخوانی توابع جدید 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
    }
}
  1. برنامه خود را اجرا کنید خروجی println() با shouldChangeWater() باید همان چیزی باشد که قبل از استفاده از توابع فشرده بود.

مقادیر پیش فرض

مقدار پیش فرض برای یک پارامتر لازم نیست یک مقدار باشد. می تواند تابع دیگری باشد، همانطور که در نمونه جزئی زیر نشان داده شده است:

fun shouldChangeWater (day: String, temperature: Int = 22, dirty: Int = getDirtySensorReading()): Boolean {
    ...

در این کار کمی با فیلترها در Kotlin آشنا می شوید. فیلترها روشی مفید برای دریافت بخشی از لیست بر اساس برخی شرایط هستند.

مرحله 1: یک فیلتر ایجاد کنید

  1. در Hello.kt ، فهرستی از تزئینات آکواریوم را در سطح بالایی با listOf() کنید. می توانید محتویات Hello.kt را جایگزین کنید.
val decorations = listOf ("rock", "pagoda", "plastic plant", "alligator", "flowerpot")
  1. یک تابع main() جدید با یک خط ایجاد کنید تا فقط تزئیناتی را که با حرف 'p' شروع می شوند چاپ کنید. کد شرایط فیلتر در پرانتزهای مجعد {} است و it هر مورد اشاره می‌کند که فیلتر از میان آن عبور می‌کند. اگر عبارت true را برگرداند، مورد شامل می شود.
fun main() {
    println( decorations.filter {it[0] == 'p'})
}
  1. برنامه خود را اجرا کنید و خروجی زیر را در پنجره Run مشاهده می کنید:
⇒ [pagoda, plastic plant]

مرحله 2: فیلترهای مشتاق و تنبل را مقایسه کنید

اگر با فیلترها در زبان های دیگر آشنا هستید، ممکن است تعجب کنید که آیا فیلترها در Kotlin مشتاق هستند یا تنبل . آیا لیست نتایج بلافاصله ایجاد می شود یا زمانی که به لیست دسترسی پیدا کرد؟ در کاتلین، به هر شکلی که نیاز داشته باشید، اتفاق می افتد. به طور پیش فرض، filter مشتاق است و هر بار که از فیلتر استفاده می کنید، یک لیست ایجاد می شود.

برای تنبل کردن فیلتر، می‌توانید از یک Sequence استفاده کنید، که مجموعه‌ای است که می‌تواند هر بار فقط به یک مورد نگاه کند، از ابتدا تا انتها می‌رود. به راحتی، این دقیقا همان API است که یک فیلتر تنبل به آن نیاز دارد.

  1. در 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)
  1. در زیر آن کد، فیلتر را با استفاده از 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 دسترسی پیدا کردید، فیلتر اعمال می شود و نتیجه به شما بازگردانده می شود.

  1. ارزیابی اجباری دنباله را با تبدیل آن به List با استفاده از toList() . نتیجه را چاپ کنید.
    // force evaluation of the lazy list
    val newList = filtered.toList()
    println("new list: " + newList)
  1. برنامه خود را اجرا کنید و خروجی را مشاهده کنید.
⇒ eager: [pagoda, plastic plant]
filtered: kotlin.sequences.FilteringSequence@386cc1c4
new list: [pagoda, plastic plant]

برای تجسم آنچه در ارزیابی Sequence و Lazy در حال انجام است، از تابع map() استفاده کنید. تابع map() یک تبدیل ساده روی هر عنصر در دنباله انجام می دهد.

  1. با همان لیست decorations بالا، یک تبدیل با map() ایجاد کنید که هیچ کاری انجام نمی دهد و به سادگی عنصری را که ارسال شده برمی گرداند. برای نمایش هر بار دسترسی به یک عنصر، یک println() اضافه کنید و دنباله را به متغیری به نام lazyMap .
    val lazyMap = decorations.asSequence().map {
        println("access: $it")
        it
    }
  1. lazyMap را چاپ کنید، اولین عنصر lazyMap را با استفاده از first() چاپ کنید، و lazyMap را که به List تبدیل شده است چاپ کنید.
    println("lazy: $lazyMap")
    println("-----")
    println("first: ${lazyMap.first()}")
    println("-----")
    println("all: ${lazyMap.toList()}")
  1. برنامه خود را اجرا کنید و خروجی را مشاهده کنید. چاپ 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]
  1. قبل از اعمال map با استفاده از فیلتر اصلی یک Sequence جدید ایجاد کنید. آن نتیجه را چاپ کنید.
    val lazyMap2 = decorations.asSequence().filter {it[0] == 'p'}.map {
        println("access: $it")
        it
    }
    println("-----")
    println("filtered: ${ lazyMap2.toList() }")
  1. برنامه خود را اجرا کنید و خروجی اضافی را مشاهده کنید. همانند دریافت اولین عنصر، println() داخلی فقط برای عناصری که به آنها دسترسی دارند فراخوانی می شود.
⇒
-----
access: pagoda
access: plastic plant
filtered: [pagoda, plastic plant]

در این کار، شما با لامبداها و توابع درجه بالاتر در Kotlin آشنا می شوید.

لامبدا

علاوه بر توابع نامگذاری شده سنتی، کاتلین از لامبدا پشتیبانی می کند. لامبدا عبارتی است که یک تابع را می سازد. اما به جای اعلان یک تابع با نام، تابعی را اعلام می کنید که نام ندارد. بخشی از چیزی که این را مفید می کند این است که عبارت لامبدا اکنون می تواند به عنوان داده ارسال شود. در زبان‌های دیگر، لامبدا را توابع ناشناس ، توابع تحت اللفظی یا نام‌های مشابه می‌گویند.

توابع مرتبه بالاتر

شما می توانید با ارسال یک لامبدا به تابع دیگر، یک تابع مرتبه بالاتر ایجاد کنید. در کار قبلی، یک تابع مرتبه بالاتر به نام filter ایجاد کردید. شما عبارت لامبدا زیر را به عنوان شرط بررسی برای filter کردن:
{it[0] == 'p'}

به طور مشابه، map یک تابع مرتبه بالاتر است، و لامبدای که به آن ارسال کردید، تبدیلی بود که باید اعمال شود.

مرحله 1: با لامبدا آشنا شوید

  1. مانند توابع نامگذاری شده، لامبداها می توانند پارامترهایی داشته باشند. برای لامبداها، پارامترها (و انواع آنها، در صورت نیاز) در سمت چپ چیزی که فلش تابع نامیده می شود -> می رود. کدی که باید اجرا شود به سمت راست فلش تابع می رود. هنگامی که لامبدا به یک متغیر اختصاص داده می شود، می توانید آن را درست مانند یک تابع فراخوانی کنید.

    با استفاده از REPL ( Tools > Kotlin > Kotlin REPL )، این کد را امتحان کنید:
var dirtyLevel = 20
val waterFilter = { dirty : Int -> dirty / 2}
println(waterFilter(dirtyLevel))
⇒ 10

در این مثال، لامبدا یک Int به نام dirty می گیرد و dirty / 2 را برمی گرداند. (زیرا فیلتر کردن کثیفی را از بین می برد.)

  1. نحو کاتلین برای انواع توابع ارتباط نزدیکی با نحو آن برای لامبدا دارد. از این نحو استفاده کنید تا متغیری را که دارای تابعی است به طور تمیز اعلام کنید:
val waterFilter: (Int) -> Int = { dirty -> dirty / 2 }

این چیزی است که کد می گوید:

  • یک متغیر به نام waterFilter .
  • waterFilter می تواند هر تابعی باشد که یک Int بگیرد و یک Int برگرداند.
  • یک لامبدا را به waterFilter اختصاص دهید.
  • لامبدا مقدار آرگومان dirty را تقسیم بر 2 برمی گرداند.

توجه داشته باشید که دیگر لازم نیست نوع آرگومان لامبدا را مشخص کنید. نوع با استنتاج نوع محاسبه می شود.

مرحله 2: یک تابع مرتبه بالاتر ایجاد کنید

تاکنون، نمونه‌های لامبدا بیشتر شبیه توابع هستند. قدرت واقعی لامبداها استفاده از آنها برای ایجاد توابع مرتبه بالاتر است، جایی که آرگومان یک تابع تابع دیگری است.

  1. یک تابع مرتبه بالاتر بنویسید. در اینجا یک مثال اساسی وجود دارد، تابعی که دو آرگومان می گیرد. آرگومان اول یک عدد صحیح است. آرگومان دوم تابعی است که یک عدد صحیح می گیرد و یک عدد صحیح برمی گرداند. آن را در REPL امتحان کنید.
fun updateDirty(dirty: Int, operation: (Int) -> Int): Int {
   return operation(dirty)
}

بدنه کد تابعی را که به عنوان آرگومان دوم ارسال شده است فراخوانی می کند و آرگومان اول را به آن ارسال می کند.

  1. برای فراخوانی این تابع، آن را یک عدد صحیح و یک تابع ارسال کنید.
val waterFilter: (Int) -> Int = { dirty -> dirty / 2 }
println(updateDirty(30, waterFilter))
⇒ 15

تابعی که پاس می کنید لازم نیست لامبدا باشد. در عوض می تواند یک تابع با نام معمولی باشد. برای تعیین آرگومان به عنوان یک تابع منظم، از عملگر :: استفاده کنید. به این ترتیب کاتلین می داند که شما مرجع تابع را به عنوان آرگومان ارسال می کنید، نه اینکه سعی کنید تابع را فراخوانی کنید.

  1. سعی کنید یک تابع با نام معمولی را به 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 مشخص کنید.
  • تقریباً همه چیز در کاتلین ارزشی دارد. با استفاده از مقدار 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}

در این درس چیزهای زیادی وجود دارد، به خصوص اگر تازه با لامبدا آشنا شده اید. درس بعدی به بررسی مجدد لامبداها و توابع مرتبه بالاتر می پردازد.

مستندات کاتلین

اگر در مورد هر موضوعی در این دوره اطلاعات بیشتری می خواهید، یا اگر گیر کرده اید، https://kotlinlang.org بهترین نقطه شروع شما است.

آموزش های کاتلین

وب‌سایت https://try.kotlinlang.org شامل آموزش‌های غنی به نام Kotlin Koans، یک مترجم مبتنی بر وب و مجموعه کاملی از مستندات مرجع با مثال است.

دوره جسارت

برای مشاهده دوره Udacity در مورد این موضوع، به Kotlin Bootcamp for Programmers مراجعه کنید.

ایده IntelliJ

اسناد IntelliJ IDEA را می توان در وب سایت JetBrains یافت.

این بخش، تکالیف احتمالی را برای دانش‌آموزانی که در این آزمایشگاه کد به عنوان بخشی از دوره‌ای که توسط یک مربی هدایت می‌شود، فهرست می‌کند. این وظیفه مربی است که موارد زیر را انجام دهد:

  • در صورت نیاز تکالیف را تعیین کنید.
  • نحوه ارسال تکالیف را با دانش آموزان در میان بگذارید.
  • تکالیف را درجه بندی کنید.

مربیان می‌توانند از این پیشنهادات به اندازه‌ای که می‌خواهند استفاده کنند، و باید با خیال راحت هر تکلیف دیگری را که فکر می‌کنند مناسب است به آنها اختصاص دهند.

اگر به تنهایی بر روی این کدها کار می کنید، از این تکالیف برای آزمایش دانش خود استفاده کنید.

یه این سوالات پاسخ دهید

سوال 1

تابع contains(element: String) در صورتی که element رشته در رشته ای که روی آن فراخوانی می شود وجود داشته باشد، مقدار true را برمی گرداند. خروجی کد زیر چه خواهد بود؟

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)

به درس بعدی بروید: 4. طبقات و اشیاء

برای یک نمای کلی از دوره، از جمله پیوندهایی به دیگر کدها، به «کوتلین بوت کمپ برای برنامه نویسان: به دوره خوش آمدید» مراجعه کنید.