Higher-order function — تابعی است که تابع دیگری را به عنوان پارامتر میپذیرد یا یک تابع را در نتیجه برمیگرداند. در Kotlin توابع اشیاء درجه یک هستند: میتوان آنها را در متغیرها ذخیره کرد، به عنوان آرگومان ارسال کرد و برگرداند. بر اساس مستندات Kotlin (2026)، higher-order functions تکرار کد را به طور متوسط ۳۰٪ در مقایسه با رویکردهای امری کاهش میدهند. Higher-order function — مفهوم بنیادین سبک تابعی در توسعه مدرن است.
نکات اصلی
Higher-order function — تابعی است که حداقل یکی از دو ویژگی را دارد: پذیرش تابع دیگری به عنوان آرگومان یا بازگرداندن تابع به عنوان نتیجه. در ریاضیات چنین توابعی تابعک یا عملگر نامیده میشوند. در برنامهنویسی آنها در Lisp در سال ۱۹۵۸ ظاهر شدند و به بخش اجباری تمام زبانهای مدرن — JavaScript، Python، Swift، Kotlin، Scala و Haskell تبدیل شدند. Higher-order function امکان انتزاع از عملیات مشخص و ارسال رفتار به عنوان مقدار را فراهم میکند.
تفاوت کلیدی بین higher-order function و تابع معمولی — وجود پارامتر با نوع تابعی یا مقدار بازگشتی تابعی. در Kotlin نوع تابعی به صورت (ParamType) -> ReturnType نوشته میشود. برای مثال، نوع (Int) -> String تابعی را نشان میدهد که Int میپذیرد و String برمیگرداند. نوع () -> Unit تابع بدون پارامتر و بدون بازگرداندن مقدار مفید را نشان میدهد. دقیقاً همین سیستم نوع است که higher-order functions را نوع-ایمن میکند.
متضاد higher-order function — first-class function (تابع درجه یک). First-class function به این معنی است که تابع مانند هر مقدار دیگری قابل استفاده است: به متغیر اختصاص داده شود، در مجموعه ذخیره شود، به عنوان آرگومان ارسال شود. Higher-order function تابعی است که از first-class functions برای پذیرش یا بازگرداندن استفاده میکند. Kotlin هر دو مفهوم را در سطح زبان بدون کتابخانههای اضافی پشتیبانی میکند.
در Kotlin هر تابعی که حداقل یک پارامتر با نوع تابعی داشته باشد یا نوع بازگشتی آن تابعی باشد، به طور خودکار higher-order function محسوب میشود. کامپایلر به توضیح ویژهای نیاز ندارد — کافی است (T) -> R را در امضا مشخص کنید. هنگام فراخوانی چنین تابعی، آرگومان یا یک عبارت لامبدا، یا ارجاع به تابع موجود از طریق ::، یا مقدار تابعی ذخیره شده در متغیر ارسال میشود.
fun operate(a: Int, b: Int, op: (Int, Int) -> Int): Int {
return op(a, b)
}
fun main() {
val sum = operate(10, 20) { x, y -> x + y }
println(sum) // ۳۰
}
در لیست، تابع operate دو عدد صحیح و پارامتر op از نوع (Int, Int) -> Int را میپذیرد. بدنه تابع در یک خط عملیات ارسال شده را فراخوانی میکند. در main فراخوانی operate لامبدای { x, y -> x + y } را ارسال میکند — Kotlin آن را به لطف نحو trailing lambda بعد از پرانتزها قرار میدهد. اگر لامبدا آخرین آرگومان بود، میتوان آن را کاملاً خارج از پرانتزها قرار داد که خوانایی زنجیرههای فراخوانی را افزایش میدهد.
در پشت صحنه، Kotlin higher-order function را از طریق رابط Function (Function2 برای دو پارامتر) کامپایل میکند. هر لامبدا به یک کلاس ناشناس تبدیل میشود که رابط FunctionN مربوطه را پیادهسازی میکند. این به این معنی است که ایجاد لامبدا برای هر عملیات تخصیص حافظه دارد. برای کاهش سربار، Kotlin از توابع inline پشتیبانی میکند که بدنه higher-order function را در محل فراخوانی قرار میدهند و ایجاد کلاس ناشناس را حذف میکنند.
ارسال تابع به عنوان آرگومان — رایجترین الگوی استفاده از higher-order function. به جای ایجاد سلسلهمراتب کلاسها با روش چندریختی، برنامهنویس رفتار مورد نیاز را مستقیماً به نقطه استفاده ارسال میکند. این اصل «باز بودن برای توسعه» (Open/Closed Principle) را بدون وراثت پیادهسازی میکند: عملیات جدید به عنوان یک لامبدای جدید اضافه میشود، نه به عنوان یک زیرکلاس جدید.
برای توابع nullable، Kotlin از نوع ((T) -> R)? با علامت سؤال بعد از پرانتزها استفاده میکند. چنین تابعی را فقط بعد از بررسی null یا از طریق عملگر ?.invoke() میتوان فراخوانی کرد. امضای higher-order function به وضوح نشان میدهد که ارسال تابع اجباری نیست — کد فراخوان میتواند آرگومان را حذف کند. این برای callbackها و رویدادگردانها با رفتار اختیاری مفید است.
fun <T> List<T>.customFilter(
predicate: (T) -> Boolean
): List<T> {
val result = mutableListOf<T>()
for (item in this) {
if (predicate(item)) result.add(item)
}
return result
}
fun main() {
val numbers = listOf(1, 2, 3, 4, 5)
val even = numbers.customFilter { it % 2 == 0 }
println(even) // [۲، ۴]
}
تابع-افزونه customFilter پارامتر predicate از نوع تابعی (T) -> Boolean را میپذیرد. در داخل حلقه برای هر عنصر predicate را فراخوانی میکند و موارد منطبق را جمعآوری میکند. فراخوانی در main لامبدای { it % 2 == 0 } را ارسال میکند، که در آن it نام ضمنی تنها پارامتر لامبدا است. به لطف higher-order function منطق فیلتر کاملاً از مکانیزم پیمایش مجموعه جدا شده است.
Higher-order function میتواند تابع برگرداند — چنین الگویی کارخانه تابع یا مولد رفتار نامیده میشود. تابع برگشتی میتواند متغیرهایی را از محدوده دید خارجی (closure) بگیرد و آنها را بین فراخوانیها حفظ کند. این امکان ایجاد رویدادگردانهای قابل تنظیم و عملیات تخصصی بر اساس الگوهای عمومی را فراهم میکند.
هنگام بازگرداندن تابع، Kotlin نوع تابعی بازگشتی ((T) -> R)? را از امضا استنباط میکند. کامپایلر بررسی میکند که تمام عبارات return در بدنه مقادیر تابعی سازگار برمیگردانند. متغیرهای گرفته شده در شیء لامبدا ذخیره میشوند و تا زمانی که ارجاع به آن وجود دارد در دسترس هستند. این مکانیزم قدرتمندی است اما نیاز به توجه در مدیریت حافظه دارد.
fun makeMultiplier(factor: Int): (Int) -> Int {
return { x -> x * factor }
}
fun main() {
val double = makeMultiplier(2)
val triple = makeMultiplier(3)
println(double(5)) // ۱۰
println(triple(5)) // ۱۵
}
تابع makeMultiplier factor را میپذیرد و لامبدای { x -> x * factor } را برمیگرداند، که در آن factor از محدوده خارجی (closure) گرفته شده است. در هر فراخوانی makeMultiplier یک تابع جدید با مقدار factor خود ایجاد میشود. متغیرهای double و triple توابع برگشتی را ذخیره میکنند و میتوانند چندین بار فراخوانی شوند. این الگو در پیکربندی مشتریان HTTP، دکوراتورها و میانافزارها به طور گسترده استفاده میشود.
استفاده ترکیبی از higher-order function و لامبداها امکان ساخت زنجیرههای عملیاتی رسا بدون متغیرهای میانی را فراهم میکند. کتابخانه استاندارد Kotlin شامل دهها higher-order functions است: let, run, apply, also, filter, map, flatMap, fold, reduce, forEach, groupBy و دیگران. هر کدام یک لامبدا میپذیرد و با کمک آن تبدیل داده را انجام میدهد.
data class User(val name: String, val age: Int)
fun main() {
val users = listOf(
User("Alice", 25),
User("Bob", 17),
User("Charlie", 30)
)
val result = users
.filter { it.age >= 18 }
.map { it.name.uppercase() }
.sorted()
println(result) // آلیس
}
در مثال، زنجیرهای از سه higher-order functions لیست کاربران را پردازش میکند. filter یک گزاره میپذیرد و فقط کاربران بالغ را نگه میدارد. map هر کاربر را به نام با حروف بزرگ تبدیل میکند. sorted نتیجه را به صورت صعودی مرتب میکند. هر عملیات یک لامبدا میپذیرد و Kotlin ایمنی نوع را در تمام مراحل تضمین میکند. بدون higher-order function باید یک حلقه با if، لیستهای موقت و مرتبسازی دستی نوشت.
سوالات متداول
Higher-order function تابع دیگری را به عنوان پارامتر میپذیرد یا آن را برمیگرداند. تابع معمولی فقط با دادهها — اعداد، رشتهها، اشیاء — کار میکند. Higher-order function با رفتار کار میکند و منطق را به عنوان آرگومان ارسال میکند.
بله، اصلاحکننده inline سربار ایجاد کلاس ناشناس برای لامبدا را حذف میکند. با استفاده از crossinline یا noinline میتوان مدیریت کرد که کدام لامبداها جایگزین میشوند و کدام به عنوان شیء باقی میمانند.
تابع بدون پارامتر و بدون مقدار بازگشتی نوع () -> Unit دارد. تابع با یک پارامتر از نوع T و بازگشت R به صورت (T) -> R نوشته میشود. برای دو پارامتر — (T, U) -> R و به همین ترتیب تا ۲۲ آرگومان.
لامبدا — نوشتار فشرده { args -> body }، تابع ناشناس — fun(args): ReturnType { body }. لامبدا نمیتواند return بدون برچسب داشته باشد، تابع ناشناس میتواند. هر دو میتوانند به higher-order function ارسال شوند.
از higher-order function در حلقههای داغ بدون inline اجتناب کنید — هر لامبدا یک شیء ایجاد میکند. برای عملکرد بحرانی از inline fun استفاده کنید. همچنین از تودرتوی عمیق لامبداها سوءاستفاده نکنید — این خوانایی را کاهش میدهد.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید