1. Lambda ne vaxt “oruyu yalniz oruyu xatirine” cevrilir
Lambda-lari yeni menimseyende tehlukeli bir dovr olur: her seyi lambda ile “sarimaq” isteyi. Bu, shurupovyor alib onunla suse butulkasinin qapagini burmaq kimidir. Formal olaraq isleyir, amma size qeribe baxmaga baslayirlar — hetta pisik ve kompilyator bele. Cox vaxt problem o anda yaranir ki, lambda yalniz bir hereket edir: sadəcə basqa funksiyani cagirira.
Tipik bir veziyyete baxaq. Bizde isShort predikat funksiyasi var ve biz onu yuksek dereceli funksiyaya oturmek isteyirik; hemin funksiya (String) -> Boolean gozleyir.
fun isShort(s: String): Boolean = s.length <= 3
fun findFirstOrNull(items: List<String>, predicate: (String) -> Boolean): String? {
for (s in items) if (predicate(s)) return s
return null
}
fun main() {
val words = listOf("kotlin", "go", "java")
val result = findFirstOrNull(words) { s -> isShort(s) } // lamba-oruyu
println(result) // go
}
Burada { s -> isShort(s) } lambdasi hec bir yeni sey etmir: sadəcə arqumenti isShort-a “yonlendirir”. Bele hallarda Kotlin daha qisa ve adeten daha oxunaqli yazmaga imkan verir.
2. Callable reference :: nedir ve onu nece oxumaq olar
Callable reference — movcud funksiyani (veya diger “cagirila bilen” seyi, meselen konstruktoru) parametr kimi oture bileceyiniz deyere cevirmek usuludur. Mahiyyet etibarile, bu, funksiya tipi ile uygun olan “funksiyaya istinad”dir. Kotlin callable reference-leri lambdalarla yanaşı “funksiya tipinin numunesini elde etme” usullarindan biri kimi qebul edir.
Sintaksis bele gorunur:
- ::isShort — isShort funksiyasina istinad
- String::toInt — String uzerinde cagirmaq mumkun olan funksiya/metoda istinad (indilik bunu “setri edede cevirmeye hazir cevirme” kimi qebul edin)
- ::Regex — konstruktora istinad (qisa toxunuruq, derine getmirik)
Bu gun bizi esasen birinci forma maraqlandirir: ::funksiyaAdi.
::-i nece oxumaq olar
Rahat “beyinde tercume”:
- { x -> f(x) } — “f-i cagiran lambda” kimi oxunur
- ::f — “f funksiyasinin ozu, deyer kimi” kimi oxunur
Yəni biz cagirmiriq f-i, biz onu otururuk.
Sxematik (sade “beyin xeritəsi”):
flowchart LR
A["Lambda-oruyu
{ x -> f(x) }"] --> B["Callable reference
::f"]
B --> C["Yuksek dereceli funksiya
(T)->R gozleyir"]
3. { x -> f(x) }-in ::f ile evezlenmesi
Bu bolmede “he, qisa imis” seviyyesinden anlayisa kecirik: bu ne vaxt dogrudan eyni seydir, ne vaxt ise yox. Burada tuzaga dusmemek vacibdir: :: “sehirli qisalma” deyil, kompilyatorun tipler uzre ciddi yoxladigi mexanizmdir.
Numuneye qaydaq ve onu callable reference ile yeniden yazaq:
fun isShort(s: String): Boolean = s.length <= 3
fun findFirstOrNull(items: List<String>, predicate: (String) -> Boolean): String? {
for (s in items) if (predicate(s)) return s
return null
}
fun main() {
val words = listOf("kotlin", "go", "java")
val result = findFirstOrNull(words, ::isShort)
println(result) // go
}
Esas meqam: ::isShort uyğundur, cunki siqnatur ust-uste dusur:
- gozlenir: (String) -> Boolean
- isShort ise: (String) -> Boolean
Ele buna gore kompilyator lazim olan yerdə lambdanin yerine funksiyani rahatliqla “yerlesdirir”.
4. Siqnaturun ust-uste dusmesi: niye :: bezen kompilyasiya olunmur
Callable reference-lerle bagli bir yaxsi xeber var, bir de… yenə yaxsi xeber var — sadəcə evvelce azacıq qiciqlandirir.
Yaxsi xeber: eger ::f kompilyasiya olunursa, demek tipler uzre her sey uyğundur.
Ikinci yaxsi xeber: eger kompilyasiya olunmursa — kompilyator sizi xilas edir, cunki siz “o davranisi deyil” oturmeye calisirdiniz. Ve bunu indi bilmek iki saat debug-dan ve heyata dair felsefi suallardan yaxsidir.
Parametrlerin sayi ust-uste dusmedi
Tutaq ki, iki arqument qebul eden funksiyani bir arqument gozlenilen yere oturmek isteyirik.
fun startsWithPrefix(s: String, prefix: String): Boolean = s.startsWith(prefix)
fun usePredicate(items: List<String>, predicate: (String) -> Boolean): Int {
var count = 0
for (x in items) if (predicate(x)) count++
return count
}
fun main() {
val words = listOf("kotlin", "koala", "java")
usePredicate(words, ::startsWithPrefix) // Kompilyasiya olunmayacaq: 1 parametr lazimdir, funksiyada ise 2 var
}
Burada ::startsWithPrefix-in tipi (String, String) -> Boolean-dur, amma gozlenir (String) -> Boolean. Kompilyator duzunu deyir: “uyğun gelmir”.
Ne etmeli? Bu halda callable reference lambdani evez etmir, cunki lambda burada dogrudan da mentiq elave edir: “prefix-i sabitlemek”.
fun main() {
val words = listOf("kotlin", "koala", "java")
val prefix = "ko"
val count = usePredicate(words) { s -> startsWithPrefix(s, prefix) }
println(count) // 2
}
Ve bu tam normaldir: lambda faydali is gorur (xarici parametr elave edir).
Qayidish deyeri tipi ust-uste dusmedi
Eger (String) -> Boolean gozlenirsə, siz ise (String) -> Int funksiyasini oturmeye calisirsinizse, Kotlin buna da icaze vermeyecek.
fun lengthOf(s: String): Int = s.length
fun usePredicate(items: List<String>, predicate: (String) -> Boolean): Int {
var count = 0
for (x in items) if (predicate(x)) count++
return count
}
fun main() {
val words = listOf("a", "bbb", "cc")
usePredicate(words, ::lengthOf) // Kompilyasiya olunmayacaq: Int Boolean-a cevrilmir
}
Bu “dilin iradi” deyil, bu baza tehlukesizlikdir: predikat true/false qaytarmalidir, “2, cunki uzunluq 2-dir” yox (hercend bu, proqramcinin yuxusundaki mentiqe oxsayir).
5. Callable reference deyer kimi: val-a qoymaq ve cagirmaq olar
::f-in dogrudan da deyer oldugunu hiss etmek ucun bir defe “duzden” edin: funksiyaya istinadi deyisene saxlayin, sonra cagirin.
Bu, “oturmek” ile “cagirmaq”i qarishdirmamaga da ela komek edir.
fun normalize(s: String): String = s.trim().lowercase()
fun main() {
val f: (String) -> String = ::normalize
println(f(" HeLLo ")) // hello
}
Biz normalize-i birbasa cagirmiriq, biz f(...)-i cagiriraq, ve f-in icinde normalize-a istinad var.
Yeri gelmisken, Kotlin-in lambda-lar ve funksiya tipleri haqqinda senedlerinde callable references funksiya tipi deyerini yaratmagin ayrica usulu kimi gosterilir. Ele budur: “funksiya deyer kimi”.
6. :: harda oxunusu yaxsilasdirir, harda ise mane olur
Callable reference-lerle iki ifrata varmaq asandir.
Birinci ifrat — her yerde lamba-oruyu yazmaq, hetta orada bir setir f(x) olsa bele.
Ikinci ifrat — her seyi ::-e cevirmek, hetta elave mentiq olduqda bele. O zaman kod “cox agilli” gorunur: yığcamdir, amma anlasilmaz.
Qerar vermek ucun sadə bir cedveli yadda saxlamaq faydalidir:
| Vəziyyət | Daha yaxsi | Niye |
|---|---|---|
| Lambda deqiq olaraq bir funksiyani deyishikliksiz cagirira | ::f | { x -> f(x) } səs-kuyunu gotururuk |
| Xarici parametri sabitlemek veya yoxlama elave etmek lazimdir | lambda { x -> ... } | Bu artiq “sadə cagirish” deyil, mentiqdir |
| Funksiya adi addimi yaxsi izah edir | ::f | Kod ozunu senedlesdirir |
| Funksiya adi pis/abstraktdir (doIt, process) | daha cox lambda veya adini deyishmek | Eks halda ::doIt hec ne izah etmir |
Ve bəli: kodda ::doIt gorursunuzse, bu demek olar ki, her zaman “funksiyani adlandirmaq lazimdir” siqnali olur, “doIt-in felsefesini anlamaq lazimdir” yox.
7. Mini-numune: :: ile “pseudo-map” edirik
Biz hele kolleksiyalarin standart emeliyyatlarini map/filter/find esas alət kimi istifade etmirik (plana gore bu sonra olacaq). Ona gore telim analoqu edek: adi for ile her elemente cevirme tetbiq eden funksiya.
Evvelce yuksek dereceli funksiyani yazaq:
fun transformAll(items: List<String>, transform: (String) -> String): List<String> {
val result = mutableListOf<String>()
for (x in items) result.add(transform(x))
return result
}
Indi tesəvvur edek ki, istifadeci daxilolmasini normallasdirmaq isteyirik: bosluqlari kesmek ve kicik herfe cevirmek.
Variant 1: lamba-oruyu (isleyir, amma səs-kuy yaradir)
fun normalize(s: String): String = s.trim().lowercase()
fun main() {
val raw = listOf(" ADD ", " LiSt", " remove ")
val normalized = transformAll(raw) { x -> normalize(x) }
println(normalized) // [add, list, remove]
}
Variant 2: callable reference (daha qisa ve daha “duz”)
fun normalize(s: String): String = s.trim().lowercase()
fun main() {
val raw = listOf(" ADD ", " LiSt", " remove ")
val normalized = transformAll(raw, ::normalize)
println(normalized) // [add, list, remove]
}
Burada ::normalize — ideal uyğundur: addimi deqiq tesvir edir ve “gizli sehr” saxlamir.
8. ::-i xerc CLI tetbiqine qurasdiririq
Indi koleksiyalar ve add/list/remove komandalarina aid bu yaxinlarda qurdugunuz konsol tetbiqinde kicik, amma real bir yaxsilasma edek. Bizde hele klasslar ve obyektler yoxdur, ona gore xercleri cutlukler siyahisi kimi saxlayacagiq: Pair<String, Int>, burada first — ad, second — mebleg.
Bugunku yaxsilasdirmanin ideyasi sadedir: “emal addimlari” kodunun oxunaqli olmasini isteyirik. Callable references addimlari “adlandirilmis” etmek ucun ela usuldur.
Tetbiqin baza funksiyalari
Evvelce minimum bazani skec edek (cox qisa). Burada yeni hec nə yoxdur, bu sadəcə kontekstdir:
fun addExpense(expenses: MutableList<Pair<String, Int>>, title: String, amount: Int) {
expenses.add(title to amount)
}
fun printExpenses(expenses: List<Pair<String, Int>>) {
if (expenses.isEmpty()) {
println("Hecer hələ yoxdur") // Hecer hələ yoxdur
return
}
for ((title, amount) in expenses) println("$title: $amount")
}
“Komandanin normallasdirilmasi” addimi ayrica funksiya kimi
Evvel bir coxu normallasdirmani birbasa main-in icinde yazir, meselen bele: val cmd = readln().trim().lowercase(). Bu normaldir, amma mentiq boyudukce addimi funksiyaya cixarmaq isteyi yaranir.
fun normalizeCommandLine(line: String): String = line.trim().lowercase()
Ve mehz burada callable reference rol oynayir: bu addimi parametr kimi oture bilirik, mentiqi main boyu yaymadan.
“Setri oxu ve hazirla” yuksek dereceli funksiyasi
Sade bir utilit yazaq: setri oxuyur, ona cevirme tetbiq edir ve neticeni qaytarir. Bu, daxilolma movzularinda artiq istifade etdiyiniz “oxudum → hazirladim → pars etdim” patterne oxsayir.
fun readPreparedLine(prepare: (String) -> String): String {
val raw = readln()
return prepare(raw)
}
Indi main-de bele ede bilirik:
fun main() {
println("Komandani daxil edin:")
val line = readPreparedLine(::normalizeCommandLine)
println("Komanda: $line")
// Eger " LiSt " daxil etsəniz, belə olacaq: Komanda: list
}
Burada ::normalizeCommandLine ideal uyğundur: funksiya artiq var, adi aydindir, (String) -> String siqnaturu teleb olunanla ust-uste dusur.
9. Predikatlarda ::: kod “danishiq” kimi olur
Callable references predikatlarla xususi gozəl isleyir, cunki predikati cox vaxt bir aydin ada malik funksiyayla ifadə etmək olur.
Tutaq ki, biz xerc adlarini saxlayiriq ve adi qisa olan ilk xerci tapmaq isteyirik (meselen, “chay”), ve ya eksine — “uzun ve subheli” (meselen, “something-e abonelik kimi anlasilmaz”).
Predikati adlandirilmis funksiya kimi edek:
fun isShortTitle(title: String): Boolean = title.length <= 3
Ve kecen leksiyadaki oz axtarish funksiyamizi istifade edek (fayllar arasinda tullanmayasiniz deyə, onu bir de tam verirəm):
fun findFirstOrNull(items: List<String>, predicate: (String) -> Boolean): String? {
for (s in items) {
if (predicate(s)) return s
}
return null
}
Indi oxunaqli cagirish:
fun main() {
val titles = listOf("kofe", "chay", "podpiska")
val firstShort = findFirstOrNull(titles, ::isShortTitle)
println(firstShort) // chay
}
Bunu sesli oxusaniz, demek olar ki, normal cumle alinacaq: “isShortTitle qaydasina gore ilkini tap, ya da null”. Ve bugun ::-i oyrendiyimiz esas meqsed de budur: az səs-kuy, daha cox mena.
Funksiya adi senedlesdirmenin bir hissesi kimi
Callable reference neyminqin tesirini guclendirir: funksiya adi “davranish yarligi”na cevrilir. Buna gore :: ile hereket ve ya qayda kimi səslənən funksiyalar xususi yaxsi gorunur: normalizeCommandLine, isShortTitle, parseAmountOrNull.
Eger funksiya adi ugursuzdursa, :: problemi daha da vurğulayacaq. Meselen:
fun doIt(s: String): String = s.trim()
// transformAll(items, ::doIt) // texniki olaraq ok, amma oxunus… basha dusdunuz
Eger :: ile kod qeribe gorunurse, bu cox vaxt “lambdani geri qaytarmaq” deyil, sadəcə funksiyani yeniden adlandirmaq lazim oldugunun isaresidir.
10. Callable references :: istifadesinde tipik sehvler
Sehv №1: “funksiyani oturmek” ile “funksiyani cagirmaq”i qarishdirmək.
En cox rast gelinen qarishiqliq bele olur: telebe findFirstOrNull(words, isShort(words[0])) yazir ve niye her seyin dagildigina heyret edir. Cunki isShort(words[0]) artiq Boolean-dir, amma oturmek lazimdir (String) -> Boolean. Bele yerlerde bunu dilinizde demek komek edir: ::isShort — otururam, isShort("go") — cagirira m.
Sehv №2: lambdanin heqiqeten elave is gorduyu yerde ::-i evez etmeye calismaq.
Xarici parametri “qarishdirmaginiz” lazim gelende, callable reference adeten uygun olmur, ve bu normaldir. { s -> startsWithPrefix(s, prefix) } lambdasi oruyu deyil — o, prefix-i sabitleyir. Bu meqamda :: “pis” deyil, sadəcə basqa situasiya ucundur.
Sehv №3: “menaca uyğundur” deyə dusunub, kompilyatorun ::f-i qebul etmemesine heyretlenmek.
Callable reference ciddi tipler dunyasinda yasayir: eger (T) -> R gozlenirse, oturulen funksiya da deqiq ele siqnatura malik olmalidir. Parametrler coxdursa veya qayidish tipi ferqlidirse, Kotlin “tahmin etmeyecek”. Bu dilin xurdaçiliyi deyil, gec ortaya cixa biləcək bug-lardan qorunmadir.
Sehv №4: pis adli funksiyalarla :: istifade edib oxunmaz kod almaq.
:: adeten yalniz o zaman kodu yaxsilasdirir ki, funksiya adi ozunu izah edir. Eger ad process, handle, doStuff kimidirsə, onda ::process hec ne bas salmir. Neticede kod qisalir, amma daha aydin olmur. Burada duz mualice — neyminqdir, daha bir lambda yox.
Sehv №5: “::-i her yerde sirmalamaq”i proqramlasdirma stili kimi qebul etmek.
Callable reference — lokal (noqtəvi) sadeleşdirmə alətidir. Eger siz her bir lambdani ::-e cevirmeye baslayir ve bununla mentiqi tez anlama imkanini itirirsinizse, demek siz muhendislikden “kim daha qisa yazar” idmanina kecmisiniz. Real layihələrdə bu idmanda qaliblari mukafatlandirmirlar, amma tez-tez texniki borca cavabdeh edirlər.
GO TO FULL VERSION