Funcții de ordin superior în Kotlin

Funcții și lambda Kotlin 1.0

O funcție de ordin superior primește o funcție ca parametru sau returnează o funcție.

Sintaxă
fun aplica(x: Int, operatie: (Int) -> Int): Int = operatie(x)

fun creeazaReducere(procent: Int): (Double) -> Double = { it * (100 - procent) / 100 }

val f = ::numeFunctie            // referință la o funcție

Ce face Funcții de ordin superior

În Kotlin funcțiile sunt valori: le poți pune în variabile, le poți trimite ca argumente și le poți returna. O funcție care primește sau returnează o altă funcție se numește funcție de ordin superior. filter, map, forEach sau sortedBy din biblioteca standard sunt astfel de funcții.

Tipul unui parametru funcție se scrie (TipParametri) -> TipRezultat: (Int) -> Int, (String, Int) -> Boolean, () -> Unit. Poate fi și nullable: ((Int) -> Int)?, apelat cu f?.invoke(x).

Ca argument poți da o lambda sau o referință de funcție cu ::: lista.filter(::estePar). Referințele merg și pentru metode (String::uppercase) și constructori (::Produs).

Exemple

Exemplul 1: Funcție care primește o funcție

aplicaLaToate nu știe ce operație face; o primește ca parametru. Același cod calculează jumătate de preț, adaugă transportul sau rotunjește la zeci.

fun aplicaLaToate(preturi: List<Double>, operatie: (Double) -> Double): List<Double> {
    val rezultat = mutableListOf<Double>()
    for (p in preturi) rezultat.add(operatie(p))
    return rezultat
}

fun main() {
    val preturi = listOf(104.0, 247.0, 39.0)
    println(aplicaLaToate(preturi) { it / 2 })
    println(aplicaLaToate(preturi) { it + 15 })
    println(aplicaLaToate(preturi) { Math.round(it / 10) * 10.0 })
}

Rezultat

[52.0, 123.5, 19.5]
[119.0, 262.0, 54.0]
[100.0, 250.0, 40.0]

Exemplul 2: Funcție care returnează o funcție

reducere creează și returnează o lambda care „ține minte” procentul primit.

fun reducere(procent: Int): (Double) -> Double = { pret -> pret * (100 - procent) / 100 }

fun main() {
    val blackFriday = reducere(30)
    val studenti = reducere(10)

    println("Laptop: ${blackFriday(3000.0)} lei")
    println("Abonament: ${studenti(50.0)} lei")

    val oferte = listOf(reducere(5), reducere(15), reducere(50))
    println(oferte.map { it(200.0) })
}

Rezultat

Laptop: 2100.0 lei
Abonament: 45.0 lei
[190.0, 170.0, 100.0]

Exemplul 3: Referințe de funcții cu ::

::estePar trimite funcția existentă în loc de o lambda. String::uppercase este o referință la o metodă.

fun estePar(n: Int) = n % 2 == 0

fun main() {
    val numere = listOf(3, 8, 12, 7, 4)
    println(numere.filter(::estePar))

    val orase = listOf("iași", "cluj", "brașov")
    println(orase.map(String::uppercase))

    val verificare: (Int) -> Boolean = ::estePar
    println("10 este par? ${verificare(10)}")
}

Rezultat

[8, 12, 4]
[IAȘI, CLUJ, BRAȘOV]
10 este par? true

Greșeli frecvente

  • Scrierea tipului funcție fără paranteze la parametri: Int -> Int nu este valid, se scrie (Int) -> Int.
  • Apelarea unei funcții nullable direct: f(x) nu compilează dacă f are tipul ((Int) -> Int)?. Folosește f?.invoke(x).
  • Funcțiile de ordin superior creează un obiect pentru fiecare lambda. În bucle foarte dese, marchează funcția cu inline, ca biblioteca standard.

Comenzi înrudite

  • lambda: O lambda este o funcție fără nume, scrisă între acolade, pe care o poți pune într-o variabilă sau trimite altei funcții.
  • inline, noinline și crossinline: inline copiază corpul funcției și lambda-urile ei în locul apelului; noinline și crossinline controlează lambda-urile primite.
  • fun: fun declară o funcție: un bloc de cod cu nume, care primește parametri și poate returna un rezultat.
  • Funcții de extensie: O funcție de extensie adaugă o metodă nouă unui tip existent (String, Int, List), fără să modifici clasa.
  • Iterable.map: Creează o listă nouă în care fiecare element este rezultatul funcției aplicate elementului original.