توابع (functions) در کاتلین

توابع در کاتلین

تعریف تابع

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

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

موضوع تابع و برنامه نویسی تابعی (Functional Programming) در کاتلین به خوبی پشتیبانی میشه و کاتلین در این زمینه قوی عمل کرده.

به تابع ، فانکشن (function) و متد گفته می شود.

به علت گستردگی موضوع توابع مرتبه بالا (higher order functions)، توابع بی نام، عبارت لامبدا ، توابع اکستنشن و توابع خطی (inline functions) رو این مطلب پوشش نمیده و باید در صفحه ی اختصاصی خودشون مطالعه کنید.

برای مطالعه ی موارد ذکر شده میتونید به هر کدوم در مطالب مرتبط پایین همین صفحه مراجعه کنید.

ساختار تابع

فرم کلی:

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

 ساختار تابع در کاتلین
ساختار تابع در کاتلین

۱-بیان کننده ی سطح دسترسی به تابع در پروژه است. سطح دسترسی میتونه private، protected، internal و public باشه. به این کلیدواژه ها کلیدواژه های سطح دسترسی یا access modifier گفته میشه.

توجه:

سطح دسترسی توابع در کاتلین به صورت پیشفرض public است و نیازی به نوشتن نداره.

۲- با استفاده از کلیدواژه ی fun به کامپایلر اعلام میکنیم عبارت یک تابع است.

۳- myFunction اسم تابع است و هر اسمی میتونه باشه.

۴- برای تابع میتونیم پارامتر هایی تعریف کنیم تا هنگام صدا زدن تابع مقادیری به داخل تابع پاس بدیم. مثلا t0: T0 رو در نظر بگیرید t0 اسم دلخواه برای پارامتر و T0 کلاسی از نوع دلخواه برای پارامتر است سپس با یک ویرگول میتونیم پارامتر بعدی که در اینجا t1: T1 است رو برای فانکشن تعریف کنیم. به همین ترتیب هرچندتا پارامتر خواستید میتونید برای تابع تعریف کنید.

توجه:

هنگام استفاده از پارامتر داخل فانکشن در کاتلین، پارامتر val است.

۵- R کلاسیه که تابع بر میگردونه و هر نوعی میتونه باشه

۶- r مقداری از جنس R است که بعد از اجرای کد های داخل تابع با استفاده از کلیدواژه ی return برگردونده میشه.

توجه:

تابع حتما باید مقداری از جنس R برگردونه در غیر این صورت دچار خطای کامپایل میشیم، مگه اینکه R از نوع Unit باشه، در اینصورت نیاری نیست چیزی برگردونده بشه.

توجه:

علاوه بر اخرین عبارت میتونیم از return هرجای تابع که خواستیم استفاده کنیم، در اینصورت بعد از اجرای return تابع متوقف میشه و کد های بعد از return اجرا نمیشه.

مثال:

fun squareEven(a: Int): Int{ if(a % 2 != 0) return -1 val square = a * a return square }

در مثال بالا اگه عدد زوج نباشه مقدار -1 بر گردونده میشه و ادامه ی کد اجرا نمیشه.

صدا زدن تابع:

برای صدا زدن تابع کافیه اسم تابعو با پارامتر هایی که براش تعریف کردیم بنویسیم:

var t0: T0 = ... val t1: T1 = ... ... var tn: Tn = ... val returnedValue: R = myFunction(t0, t1, ..., tn)

مثال:

در زیر یک تابع با modifier پیشفرض public مقدار maximum بین دو عددو بر میگردونه.

fun main(){ val number0 = 34 val number1 = 48 //صدا زدن تابع val result = max(number0, number1) println("Maximum number between $number0 and $number1 is $result") } //تعریف تابع fun max(a: Int, b: Int): Int { return if(a < b) a else b }

تعریف overloading

وقتی دو یا چند تابع هم نام با پارامتر های متفاوت تعریف کنیم بهش میگیم overloading

مثال

fun max(a: Int, b: Int): Int { return if(a < b) a else b } fun max(a: Double, b: Double): Double { return if(a < b) a else b } fun max(a: Long, b: Long): Long { return if(a < b) a else b }

در بالا تابع max رو با تغییر دادن نوع پارامتر ها overload کردیم.

ویژگی پارامتر ها در توابع

۱- اختصاص دادن مقدار پیشفرض به پارامتر:

هر پارامتری که برای تابع تعریف میکنید میتونید بهش مقدار پیشفرض اختصاص بدید. در اینصورت هنگام صدا زدن تابع اگه مقداری به پارامتر اختصاص نداده باشید ، کامپایلر به صورت خودکار مقدار پیشفرض رو برای پارامتر در نظر میگیره.

مثال:

تعریف تابع:

fun hello(name: String = "World"): String{ return "Hello $name" }

صدا زدن تابع:

fun main(){ val helloWorld = hello() val helloKotlin = hello("Kotlin") println(helloWorld) println(helloKotlin) }

نوشتن اسم پارامتر به صورت آشکار هنگام صدا زدن تابع:

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

مثال:

تعریف تابع:

fun multiply(number1: Double, number2: Double): Double{ return number1 * number2 }

صدا زدن تابع:

val mult = multiply(number1 = 78.3, number2 = 36.0) println("Multiplication of 78.3 and 36 is $mult")

۳- استفاده از کلیدواژه ی vararg هنگام تعریف پارامتر:

توابع arrayOf ، listOf ، asList و ... با این روش پیاده‌سازی شدن.

با استفاده از کلیدواژه ی vararg برای پارامتر، هنگام صدا زدن تابع میتونید چندین مقدار به پارامتر اختصاص بدید.

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

توجه:

از این کلید واژه فقط برای یک پارامتر میتونید استفاده کنید.

توجه:

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

مثال:

حساب کردن میانگین اعداد.

تعریف تابع:

fun findAverage(vararg numbers: Double): Double{ var sum = 0 for(num in numbers) sum += num return sum / numbers.size }

صدا زدن تابع:

val average = findAverage(4, 7, 2, 9) println("Average is $average")

مثال:

بررسی دفعات تکرار یک کد ASCII در مجموعه ی استرینگ ها.

تعریف تابع:

fun countASCIIOccurrences(asciiCode: Int, vararg strings: String): Int{ var count = 0 for(s in strings) for(ch in s) if(ch.code == asciiCode) count++ return count }

صدا زدن تابع:

val asciiCode = 65 val asciiCodeOccurrences = countASCIIOccurrences(asciiCode, "Emily, "Adam", "Billy", "Michael") println("ASCII code $asciiCode has occurrences")

تابع تک عبارتی

اگه داخل تابع فقط یک عبارت داشته باشیم و اون عبارتو میخوایم برگردونیم، به این تابع تک عبارتی یا تابع single expression در کاتلین میگیم.

هنگام تعریف تابع میتونیم از نوشتن بدنه ی تابع {} صرف نظر کنیم و در جلوی تابع علامت = میزاریم.

مثال:

fun multiply(number1: Double, number2: Double): Double = number1 * number2

میتونیم از نوشتن نوع کلاسی که تابع برمیگردونه صرف نظر کنیم کامپایلر به صورت خودکار تشخصی میده چه نوعی تابع قراره برگردونه.

مثال:

fun multiply(number1: Double, number2: Double) = number1 * number2

مثال:

fun max(a: Double, b: Double) = if(a > b) a else b

تابع Void

تابع main که برای run کردن پروژه تعریف میکنیم void است.

اگه نوع کلاسی که تابع برمیگردونه Unit باشه تابع void محسوب میشه ، به این توابع در کاتاین تابع Unit Type نیز گفته میشه.

Unit در کاتلین بدون مقدار در نظر گرفته شده ، یک کلاس بدون هیچ عضوی.

مثال:

fun hello(): Unit{ println("Hello World") return Unit }

برای تعریف تابع void در کاتلین میتونیم از نوشتن Unit صرف نظر کنیم. کامپایلر به صورت خودکار تشخیص میده تابع Unit است.

در توابع void میتونیم از return برای متوقف کردن تابع و جلوگیری از اجرای بقیه ی کد ها استفاده کنیم.

مثال:

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

مثال:

fun hello(name: String = "World!"){ println("Hello $name") }

مثال:

fun printSquareEven(n: Int){ if(n % 2 != 0){ println("Not Even Number") } val square = n * n println("Square of number $n is $square") }

مثال:

fun printGrade(score: Double){ if(score >= 90){ println('A') }else if(score >= 80){ println('B') else if(score >= 70){ println('C') }else if(score >= 60) println('E') }else { println('F') } }

تابع Infix

توابع until، downTo و step که در حلقه ی for به کار میبریم به صورت infix تعریف شدن.

به تابعی که با کلیدواژه ی infix تعریف میشه، تابع infix میگن.

برای تعریف تابع infix در کاتلین باید قوانین زیر رعایت بشه:

۱-تابع باید عضو کلاس (Member Function) یا تابع اکستنشن باشه.

۲-تابع نباید بیشتر از یک پارامتر داشته باشه.

۳- در پارامتر نباید از vararg استفاده کنیم.

۴- به پارامتر نباید مقدار پیشفرض اختصاص بدیم.

فرم کلی:

infix fun myInfixFunction(t: T): R { ... }

صدا زدن تابع:

میتونیم به یکی از دو روش زیر تابع رو صدا بزنیم.

a myInfixFunction b //Or a.myInfixFunction(b)

متغیر a رسیور (receiver) و متغیر b مقدار اختصاصی به پارامتر است.

توجه:

متغیر a باید یک آبجکت از کلاسی باشه که براش تابع رو تعریف کردیم.

مثال:

در زیر یک تابع infix تعریف میکنیم تا دوتا String رو به هم زنجیر کنه:

infix fun String.chain(other: String): String { return this.plus(other) }

توجه:

کلیدواژه ی this اشاره به آبجکت همون کلاسی داره که تابع رو براش تعریف کردیم.

صدا زدن تابع

val s = "Hello!" val chained = s chain " I have been chained to hello" println(chained)

مثال:

تابع زیر مانند until عمل میکنه با این تفاوت، بزرگترین عدد هم تو عمل تکرار به حساب میاد.

infix fun Int.to(to: Int): IntRange{ if(to <= Int.MIN_VALUE) return IntRange.EMPTY return this..to }

مثال:

تابع زیر معادل downTo برای حلقه ی for عمل میکنه با این تفاوت کوچکترین عدد تو عمل تکرار حساب نمیشه.

infix fun Int.downUntil(to: Int): IntProgression{ return IntProgression.fromClosedRange(this, (to + 1) , -1) }

صدا زدن تابع:

for(i in 100 downUntil 0) println(i)

توجه:

اجرای تابع infix نسبت به عمل ریاضی، rangeTo و کست کردن اولویت پایین تری داره.

مثلا وقتی عمل ریاضی به عنوان پارامتر برای تابع تعریف میکنیم ابتدا عمل ریاضی اتفاق میفته بعد به عنوان پارامتر به تابع پاس داده میشه.

p until 8 + 5 همون p until (8 + 5) است. ابتدا 8 با 5 جمع میشه بعد به تابع until پاس داده میشه.

توجه:

اجرای تابع نسبت به عملگر های منطقی و in اولویت بالاتری داره.

a && b xor c همون a && (b xor c) است. ابتدا تابع xor اجرا میشه بعد && بررسی میشه.

تابع Generic

به توابعی که پارامتر های جنریک دارن تابع جنریک میگیم.

فرم کلی:

fun <E> myFunction(e: E): Type<E>{ ... }

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

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

مثال:

fun <E> myList(vararg e: E): MutableList<E>{ val list = MutableList<E>() for(item in e) list.add(item) return list }

صدا زدن تابع:

val myFriendList = myList<String>("Joe", "Rose", "Kate", "Jane")

تابع Tail Recursive

با استفاده از کلیدواژه ی tailrec میتونید به صورت native تابع tail recursive در کاتلین تعریف کنید. tail recursion نوع خاصی از توابع بازگشتی است که صدا زدن تابع داخل خودش باید اخرین عبارت داخل تابع باشه.

توجه:

در تابع tail recursive پس از صدا کردن تابع در داخل خود تابع، هیچ کدی نباید نوشته بشه در غیر این صورت دچار خطای کامپایل میشیم.

مثال:

محاسبه ی فیبوناچی به روش tail recursion.

tailrec fun printFib(n: Int, a: Int = 0, b: Int = 1){ if(n == 0){ print("$a ") }else { print("$a, ") printFib(n - 1, b , a + b) } }

فرض کنید n = 3. برای n=3 ، a = 0 و b = 1 سپس تابع در داخل خودش صدا زده میشه و n = 2 میشه a = b و b = a + b میشه و به همین منوال این عمل تکرار میشه تا n به 0 برسه در اینجا صدا زدن تابع متوقف میشه.

مثال:

محاسبه ی فاکتوریل به روش tail recursion.

tailrec fun fact(n: Int, result: Int = 1): Int = if(n <= 1) result else fact(n -1 , n * result)

فرض کنید میخوایم 5! حساب کنیم در اینصورت n=5 و result=5*4*3*2*1.

محدوده ی تعریف توابع

یک تابع رو میتونیم:

در فایل کاتلین تعریف کنیم، که بهش میگیم تاپ لول فانکشن (Top Level Function).

میتونیم داخل یک کلاس تعریف کنیم که بهش میگیم ممبر فانکشن (Member Function).

میتونیم داخل یک تابع دیگه تعریف کنیم که بهش میگیم لوکال فانکشن (Local Function).

تاپ لول فانکشن:

fun myFunction(){ ... }

ممبر فانکشن:

class MyClass{ fun myFunction(){ ... } }

لوکال فانکشن:

لوکال فانکشن (تابع داخلی) رو فقط داخل تابعی که تعریف کردیم میشه صدا زد.

تابع داخلی در کاتلین میتونه به متغیر های تابع خارجی دسترسی داشته باشه.

فرم کلی:

fun outerFunction(...): R0{ ... innerFunction(...): R1{ ... } ... innerFunction(...) ... }

مثال:

fun myFunction(){ val a = 23 val b = 54 fun printNumbers(){ print("a is $a and b is $b) } printNumbers() }

مثال:

tailrec fun printFib(n: Int, p0: Int = 0, p1: Int = 1){ var a: Int = p0 var b: Int = p1 fun printAndUpdate(){ if(n == 0){ print("$a ") return } print("$a, ") a = p1 b = p0 + p1 } printAndUpdate() if(n > 0){ printFib(n -1, a, b) } }

خلاصه

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

-در حالت نرمال توابع از 6 قسمت تشکیل شدن:

۱-کلیدواژه ی سطح دسترسی ۲-کلیدواژه ی fun که مخفف فانکشن است ۳-اسم تابع ۴-پارامتر های تابع ۵-نوعی که تابع بر میگردونه ۶-کلیدواژه ی return برای برگردوندن.

-هرجای تابع که خواستیم میتونیم از return استفاده کنیم. پس از اجرای return تابع متوقف شده و کد های بعد از return اجرا نمیشن.

به تابعی که یک عبارت داشته باشه single expression میگیم.

-توابع void مقداری از Unit بر میگردونن که میتونیم از نوشتنش صرف نظر کنیم

-توابع infix چهارتا قانون دارن که هنگام تعریف باید رعایت بشه:

۱-تابع باید ممبر فانکشن یا اکستنشن باشه. ۲-باید فقط یک پارامتر داشته باشه. ۳-برای پارامتر نباید vararg تعریف کنیم. ۴-به پارامتر نباید مقدار پیشفرض اختصاص بدیم.

-کاتلین به صورت native از تابع tail recursive پشتیبانی میکنه. برای تعریف این تابع کافیه از کلیدواژه ی tailrec استفاده کنیم.

-در توابع tail recursive اخرین عبارت باید صدا زدن تابع داخل خودش باشه و بعدش هیچ کدی نباید نوشته بشه.

-میتونیم توابع رو در سه جا تعریف کنیم: ۱-داخل فایل کاتلین. ۲-داخل کلاس. ۳ داخل یک تابع دیگه.

بحث توابع در کاتلین بسیار گستردس پشتیبانی کاتلین از توابع قویه. در این مطلب به بخشی از توابع پرداختیم، همینطور که بالا گفتم، یه سری از تابعها رو اینجا پوشش ندادم وبراشون مطلب اختصاصی گذاشتم میتونید مطالعه کنید.

موفق باشید.

arrow_drop_up
کپی شد!