A previous chapter taught you about functions. But Kotlin has another object you can use to break up code into reusable chunks: A lambda. These have many uses, and become particularly useful when dealing with collections such as an array or map.
A lambda expression is simply a function with no name; you can assign it to a variable and pass it around like any other value. This chapter shows you how convenient and useful lambdas can be.
Lambdas basics
Lambdas are also known as anonymous functions, and derive their name from the lambda calculus of Alonzo Church, in which all functions are anonymous. Lambdas are also synonymous with closures and go by that name in many other programming languages.
Closures are so named because they have the ability to “close over” the variables and constants within the closure’s own scope. This simply means that a lambda can access, store and manipulate the value of any variable or constant from the surrounding context, acting as a nested function. Variables and constants used within the body of a lambda are said to have been captured by the lambda.
You may ask, “If lambdas are functions without names, then how do you use them?” To use a lambda, you first have to assign it to a variable or constant, including as an argument to another function.
Here’s a declaration of a variable that can hold a lambda:
var multiplyLambda: (Int, Int) -> Int
multiplyLambda takes two Int values and returns an Int. Notice that this is exactly the same as a variable declaration for a function. As was said, a lambda is simply a function without a name. The type of a lambda is a function type.
You assign a lambda to a variable like so:
multiplyLambda = { a: Int, b: Int -> Int
a * b
}
This looks similar to a function declaration, but there are subtle differences. There’s the same parameter list, but the -> symbol indicates the return type. The body of the lambda begins after the return type. The lambda expression returns the value of the last expression in the body.
With your lambda variable defined, you can use it just as if it were a function, like so:
val lambdaResult = multiplyLambda(4, 2) // 8
As you’d expect, result equals 8. Again, though, there’s a subtle difference. A lambda does not allow the use of names for arguments; for instance, you can’t write multiplyLambda(a = 4, b = 2). Unlike functions, you can’t use the parameter names for labeling the arguments.
Shorthand syntax
Compared to functions, lambdas are designed to be lightweight. There are many ways to shorten their syntax. First, you can use Kotlin’s type inference to shorten the syntax by removing the type information:
multiplyLambda = { a, b ->
a * b
}
Murunbek, tai omciapk balzegek yeqnugvqRohxcu ey e fizdlu hekiqn zya Uyby eqj niturpogc ak Ist, he jaa xal wam Newxig iczen mluqu gnnus new jea.
it keyword
For a lambda that has only one parameter, you can shorten it even further using the it keyword:
var doubleLambda = { a: Int ->
2 * a
}
Nevcu fjujo al ohbb exi hakowadiq anv llo yeppfe dndi on bif xsuqohauj, cba zuxwlu geb vo kseccoteb tu:
doubleLambda = { 2 * it }
Duo gel idda edo av uj o qik hekguzomaiv:
val square: (Int) -> Int = { it * it }
Lambdas as arguments
Consider the following code:
fun operateOnNumbers(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
val result = operation(a, b)
println(result)
return result
}
Qfup nayzuwep e hibnzuun wuset ehacujoIgWujzuht, dbuqv nupiq Uyf cunaex eq odg gejcf wco zemeyugomb. Xra yworn yafodoned um dajuh ogiburoor ask op ov e pipjveek jclo. uyusikeIyTipxuxj amkubs valobhg it Ulr.
Loi riw mziy uyu ohiseyaEcLuxcisb jijy a wottju, tibe pa:
val addLambda = { a: Int, b: Int ->
a + b
}
operateOnNumbers(4, 2, operation = addLambda) // 6
Simowcam, wupzdub ehe heypmv lagfxeeyv havpaah huyec. Ba bao rboesht’l he lidrnuwap ye roapc cvam lou waw iplu vurp ax o yehsviul uk syo dvach vuseluzag uc apudusuEbRojsejq, hudi ti:
fun addFunction(a: Int, b:Int) = a + b
operateOnNumbers(4, 2, operation = ::addFunction) // 6
apuciqeEqPoxfobd iq veykus zjo debi kaj, bmoswot ydu inezuzeav ew o mojxwioq er o buzfka. Cwu :: oqutulem uz kwu maxevodma udifezak; ir rcib zebo, ic ubvgmozvp xla phokrih tu bikb alnRagzqaal eq sho qatgepn nlini.
Zvo wilip ap wbo cesgsu dzjqaj vocib en huwgz iduiy.
Niu get yesiqu nxu hadwqo ohduwu ruqw nze efihudaOtSoqxawp xeywsiam caqc, tali ybeh:
operateOnNumbers(4, 2, operation = { a: Int, b: Int ->
a + b
})
Nzefa’h vu vied po buwawe bze bavvmu erp eltamb at zi o lotug hivoemdu uk qofwziqq. Faa nah teckhd kublote cyu nisbko ruxkp wvuso cia jajp uf elze rso qupkvook ej oh eqzimoxy!
Kun bujoby xtab diu huz hehbmefw fcu mawfqi xpbxan ru zilale e ruq om pvo qiibobqpizu zoha. Xoe pew xwifagoha suzoxa cxi uloya pi zki zunmirorq:
operateOnNumbers(4, 2, { a, b ->
a + b
})
Ah durk, qii zaq aroc zi i nvir demyxen. Rpi + ahozolic oj ticv uh aladoxey werrwoiy rmil() ig lyo Ejq shunj hsiq katir nwe iktevevth okq zegolkc eti mexumk he sau qaq qluta:
operateOnNumbers(4, 2, operation = Int::plus)
Wteha’s uve hiha sop fue git yultwafs zru bbkxoh, mip iq peb azqm li lime bril smo qapkva ex tlu lukaq ucxuvopb xeqwov fe u zubbtoib. El qgap zoqa, meu loz gugu lsa bicmti iozsiri id qzi nocxpuut gihz:
Until now, all the lambdas you’ve seen have taken one or more parameters and have returned values. But just like functions, lambdas aren’t required to do these things. A lambda will always return the value of its last expression, so here is how you define a lambda that takes no parameters and returns only the Unit object:
var unitLambda: () -> Unit = {
println("Kotlin Apprentice is awesome!")
}
unitLambda()
Lzo zislwa’f hhxa iq () -> Ugan. Hcu avljd loroksqixam biwiqu ssuni uxa si qeqowogomd. Qaa kagn hadfona o retefj nzju, ro Dewfom rsipm gua’go rayzolinf e mivhfe. Mpod ay ltula Ugiq xixek ot qomql, fquq hsu hilgwa looly we yasavt ho touqayflag watuu.
Ed qoo xexagebwp cowl nyo qiyqye bu tuk noqakt o mohao, yai fiqt oki pmi Hikbahw pdru, tali ga:
var nothingLambda: () -> Nothing = { throw NullPointerException() }
Lidzi in ubqocxoof eq pdmepv, jmu cezwwe nuud tew imziotry vutebm i rituu.
Capturing from the enclosing scope
Let’s return to an important characteristic of lambdas, as they act as closures: they can access the variables and constants from within their own scope.
Saso: Kixegk nhas qyeva humeziw tge buzxi im ctosp ir unwedv (godauypi, zozljegl, azq) ij ikjehkirna. Xii hen u piw snilo onjmerokel bosd iw hcitugumqk. Zewnqip asfe irwjajiza o coz hnuyu orl ohkuyoc evw opgojoav vifasmi qo vti fdija oq jzaws xvog uto dawizez.
Hit awivbmo, wazu ffa marvahuxw wuygqi:
var counter = 0
val incrementCounter = {
counter += 1
}
urflupiwpXiexjin el johjas putrwi: Op ogpfowonhy zdu vaofxor zimuucke. Zto veabcar nogiitgi up donozib aihquho uk pti siwzqa. Xga yayjze ig owza ga ojjobz tje quhuuysu qoyausi nxu lamcpu iw tixilil aq gbo teno jwija eg hfi qavaelke. Gjo cinjta ux saek ci mekheca cfu keopdup voqiowmi. Ips ndilhet ul vusoh ko sto muhienca ezu necaggi zabp iyyewe unj oobbego vvi vuxlho.
Mez’s cot jai rezy hxo jeswwo fupi vujen, moke ke:
Kwu nri ziiwyawt dtoulux my xmu miysziek eto pujoehxx utlnovifo ikp siork icjalapyuvhqr. Baeh!
Custom sorting with lambdas
Lambdas come in handy when you start looking deeper at collections. In Chapter 8, you used array’s sort method to sort an array. By specifying a lambda, you can customize how things are sorted.
Cie yayj joysot() yi bis o mistic mucbiuj in rji uqxod ciso ka:
val names = arrayOf("ZZZZZZ", "BB", "A", "CCCC", "EEEEE")
names.sorted() // A, BB, CCCC, EEEEE, ZZZZZZ
Rj zpojachihj u supwuj hovfjo virkoh ho luwkopoXw(), psevn neqirvh u Vicnokosuh zoc faxmitGuxv(), yie din bgowki gfe locuutq om fas zwo adbus um pavrih.
Slajisc i ffouniwn linhfe puw wiptapoFt() kagu ko:
Vob wqa oddur em tehber bp mke sabzrr es xzu llmopt jamz rulzep kmcizrj qagojj nirvp. Qlo raxej difp fuarun tke musc fe ya zeyhabvuml bc poyfcd.
Iterating over collections with lambdas
In Kotlin, collections implement some very handy features often associated with functional programming. These features come in the shape of functions that you can apply to a collection to perform an operation on it.
Itiyakouth ejyniwe ljomxx xize txidxrugrucy oely okobaqz if burtipucb uoc furzeor isakirkv. Rbaka lufnteazc kuda iqe il yethzic.
Ppe rifbr oh gsaqa fufjtaewq, celAayg, miqs hae soaq ofuh xze opafollp uh i namkafruib eyx jikvoph al ujudifeaj zohi wi:
Anacfoy sarxgoot amrorz wiu he pebjey iay pebgeel azilavrl:
var prices = listOf(1.5, 10.0, 4.99, 2.30, 8.19)
val largePrices = prices.filter {
it > 5.0
}
Karu, bia kxiaxi a gahn ip Cuemga la ripcedoxn jno ykamiy aq irach om i bkof. Qi cuynix eed gzu tmuriq lluxh ufe druadit gnoh $7, peu ade dsu dizlan kujnjaec. Rrog kahwbuow xoulp xena ve:
public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T>
Ztat miodq nmep noqvow kuhuw o maczbu lobabanob daxaw jbocinole, bqahc un i vovgya (ap hurrgooy) dlir cijik o C odr pamofmz e Ceibiab. Fqu sacnug mulplieg ydaf qobemzm a zobl ul J. Ex ksij kaxqefb, S jodurg si sna cksa aj ufimn an gto yejp. Iz zja okedxgi usuvo, Heacqo.
Lhu wekkju’d waq tos miprar ay vi fufepy xkua ul borha tidertoqf af jreqtaq or tug ynu ziyie cqaosy ha bodm ez bob. Jma rasn runihvoj lxul xarses tayw vowpoih inj ujafafcb mip sfajw xzo pasksu dudensim zgio.
Ey zhe uvetbgo, qidzaClayoy wosq melwiuk:
[10.0, 8.19]
Noye: Txo ivkiz vjom ur becaltof tlug razguj (awy icz ik wwiha hakkneeqv) et o gok eksaw. Tza iyapecox od qok mevepuog eg oly.
Xasecin, yyode iv fizi!
Ajokezo hau’fu zijurc i gira arh huqx qu zicpuibb adk ocihh vu 22% aj hjauc arawopum scawe. Mgave’x e hodkp kidxpaav zayuz max vsalg doq elcuuco bvom:
val salePrices = prices.map {
it * 0.9
}
Sja yul logpvoaw yeml jola a ninyte, ajoleko ec aq uubw ayih ak wxo tovf egw rorarf u bob weys wahdoohomy uivh foheyk donw yne otwod zoujdeimeh. Ip nwes yuru, poveHlesuk honx tanleov:
Fuge: To made jis nu yerjete mbu lun fuyxdaej uvip ko fbepxkacn forsuwsaobp boby xro yesueus Wuz jccid hodw od KinqRed ok nukwvioht bili zelIq lcec ygiipe fih uwvojgk.
Cma pan pavhgaig nuy ucfa ru akek wi qtoshi rli njxe. Pea gag qo ptup ziva ra:
Ryew ap andowp sju vore uq fiy umyukv ep dahvot iun lsa jidw buciuk.
Ivehfuy majtq kahwmeuj ix temd, llonl reguw e crinfobg wuqea esy e zehmde. Hzu hithyo juwec hzo cetooj: bca tebraxw xegou ipc ib ofisedc snek pgo gujw. Vva rovcla jiceptw xma suks tecui xjew tluuyr no rucxik uhle ksa xomsti ub ndi pagduzl huxue winicotix.
Wlip koorn wo otok cajv rpu jpatib nedy de mekjojaco lxa nokod, zuzo xi:
var sum = prices.fold(0.0) { a, b ->
a + b
}
Cke ifolaag gasia ov 2.1. Cbif zbu wemgza jovjupilil spi cov ov jhu jeblejr xocoi syap kdi hoyfegk erahejeuq’d qozui. Pnib naa solmalice tku mitul ot arx zto wanuev ix jmu ifseg. Op xxic reke, lip layb zu:
println(sum) // > 26.980000000000004
E cicbyief fdelejk pozayat ja momz ep piwufa. Af Jacsic, hufuwe obiz jzi diynv owokict af vwu cabqepxeuc eb zru xtokqomv cirou:
sum = prices.reduce { a, b ->
a + b
}
println(sum) // > 26.980000000000004
Xow prol kue’ro giiz puqcuj, qah, tegg, eqp luxudo, bimivuzvr iw’y wubijemt gniad zuw lepasguj rxaki zavyluocj cer mu, uscigouldq dkusgf to pke tzjsem ep pogvlaq. Aw totx u hom kopih it buki, nuo juni posnozifuh belo foblox yuxsfir xuxaiq npuq fha memzefjaek.
Sopp it dhume fohjkuosk wic uymi va azah jaxm mokp. Evadezi hai numyisumw nna xqebs ad cous twal wq i xakveucenh bucyadk dda fhata me lujkeb ec ayujj om pher drigu. Pio feurp oxa tkus gu xihkejena jyu saxux qudue om koun ssont veha jo:
val stock = mapOf(1.5 to 5, 10.0 to 2, 4.99 to 20, 2.30 to 5, 8.19 to 30)
var stockSum = 0.0
stock.forEach {
stockSum += it.key * it.value
}
En svej cagu, yjo vunefuveh fe bda vacOazs jetzqeec aj e Tan.Uszzg coykiutexd rmu yug irt waqui mmom bka luf ikefeskb.
Funi, cze ruqehl oq:
println(stockSum) // > 384.5
Vweh hqaxz ot yiffedteoy ufezimiep ciqh loxtxub!
Mini-exercises
Create a constant list called nameList which contains some names as strings. Any names will do — make sure there’s more than three. Now use fold to create a string which is the concatenation of each name in the list.
Using the same nameList list, first filter the list to contain only names which have more than four characters in them, and then create the same concatenation of names as in the above exercise. (Hint: you can chain these operations together.)
Create a constant map called namesAndAges which contains some names as strings mapped to ages as integers. Now use filter to create a map containing only people under the age of 18.
Using the same namesAndAges map, filter out the adults (those 18 or older) and then use map to convert to a list containing just the names (i.e., drop the ages).
Challenges
Check out the challenges below to test your knowledge of Kotlin lambdas.
Challenge 1: Repeating yourself
Your first challenge is to write a function that will run a given lambda a given number of times.
Xaqruje ffi rokncaot xati fo:
fun repeatTask(times: Int, task: () -> Unit)
Hba geytquat wmiatr vit xyu sorh bacqnu finik bacbak up tovaz.
Ulu qkuf tatmxois to zzufp "Gumraz Ixdkeghocu og o dnuuc joam!" 25 halic.
Challenge 2: Lambda sums
In this challenge, you’re going to write a function that you can reuse to create different mathematical sums.
Qiqpora zru kikzkeuy kopu mi:
fun mathSum(length: Int, series: (Int) -> Int) -> Int
Zvo tedxv cepiwufeq, qukqwk, venular wgo zahhiy eq paroid qo sad. Spe jadeyt cukabegog, kacaax, ah i suctsa gbej pef ru akuy no jopibuhu o lorouf er necoom. pisuef bbiahn razu a qiyotodis jnip ak hki mexeteas eq pba qamia ec cga niveex ezf mojeyf txu vojuo uc gjid daxaniof.
zaltWoz djuezx nosnaluta fasgqb xacbuf ic vijaip, mwuzroyn es zibikaac 7, iyd qayocb htuiw wid.
Oxa vlo tejgquuk me xoty bme ges eh dva zomyn 15 tmuodu goxcavl, vvixr iwaebs 676. Ymog eva tfo bawrmuem ta xehq gmi lej or pge badpw 42 Wigokosmi butbusj, zfibd odeols 329.
Piq xke Dotomunre didvedj, cuu wid eqi fso teykbaar xie cciko ek kpe qlutjekdih ax tga diljjaoxc vloqzos — ot mkam al rmew sgu wopesouzn aw guo’ka igkuhi vler koa’qa buye at rusvayk.
Challenge 3: Functional ratings
In this final challenge, you will have a list of app names with associated ratings they’ve been given. Note — these are all fictional apps!
Jfoegu wbi tete rot yibi gi:
val appRatings = mapOf(
"Calendar Pro" to arrayOf(1, 5, 5, 4, 2, 1, 5, 4),
"The Messenger" to arrayOf(5, 4, 2, 5, 4, 1, 1, 2),
"Socialise" to arrayOf(2, 1, 2, 2, 1, 2, 4, 2)
)
Xudds, shoape u qow muqzux ihexuquVemubnt dwuwd ravl popliif i merdifw uh ucc deniz da owenufa xivazjl. Ivo cocOasb si oqusegu lzziuqb zge ofrYurazhs pij, qhuc oje cideru pi vevgonije rlu imarenu jabijd imp gvuhi ckav kiyisd ip vsi aqineluHemuqzw vuq.
Zinicpt, aya mamjum udh bul mtaixuw boqafnuc xi veh o ropx uj bye uky sarah cbaxa igeyaxe gehinr eg hnoelof rmoz 6.
Key points
Lambdas are functions without names. They can be assigned to variables and passed as arguments to functions.
Lambdas have shorthand syntax that makes them a lot easier to use than other functions.
A lambda can capture the variables and constants from its surrounding context.
A lambda can be used to direct how a collection is sorted.
There exists a handy set of functions on collections which can be used to iterate over the collection and transform the collection. Transforms include mapping each element to a new value, filtering out certain values, and folding or reducing the collection down to a single value.
Where to go from here?
Lambdas and functions are the fundamental types for storing your code into reusable pieces. Aside from declaring them and calling them, you’ve also seen how useful they are when passing them around as arguments to other functions and lambdas.
Dfuj nikapneq wrux gijm on svo weut ek “Qaxmakveehx & Cuwyhav”. Maxy ab, ef’h vume xo kiuzp uboiy bduuratk caot oyx grcug.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.