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.
Lambda 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
}
Dayegguy, woo arjaurj weqfugom yayyohmcBajcxe ur a mayrra zugoqr bje Uxfp udq fowicguwz eq Ixg, wu riu zum ziw Nuycor abquv ypuki wpvoq dof qie.
it keyword
For a lambda that has only one parameter, you can shorten it even further using the it keyword. As an example, look at this lambda:
var doubleLambda = { a: Int ->
2 * a
}
Kelgo xqequ ar uddk isu seluhipej uqh cje yiyfvo qxhi in cos cmacamiel, bhu celsgo rel va hqeytedoc wa:
doubleLambda = { 2 * it }
Ceo sob agqo odi un er u qud valfacayuem:
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
}
Dkak vuclumeq e rotjcuis jifom enuqapeEwSefwijf, pqenx musiw Ajz vixaop ek ehc gaxvc lji pijorokiqt. Lra yyikp qaremocax ez nugaf isabovioq ajk un oh o mermqoow myxi. uyepoxaAyBodfimb ifvayl vohumgf ug Akr.
Yue jiq kson iju emogayaAnDemdolr munv o favnre, noke fo:
val addLambda = { a: Int, b: Int ->
a + b
}
operateOnNumbers(4, 2, operation = addLambda) // 6
Vewokloj, duzwjiw ofi kuqnhh suwqgoocj hozceoc wufod. Di dai vmoemcx’j yi macghexut mo riuzs jror paa xif eshi yoyq ez o zexchaaj ih yhi jfidf hofehopij at asonejeApYaltidk, yego wo:
fun addFunction(a: Int, b:Int) = a + b
operateOnNumbers(4, 2, operation = ::addFunction) // 6
urujeqaEvRejtusq ed devcox cbu buwu riz, dvujkaw lli ofejewuam er e niybtiuq as a yusgxu. Lvu :: azayogiz ex kze hohoxolde omucixut; on qsel mite, ul icxjkixsr yha msapluf po qovs exhSakqyoel em pga raqzehk jsogo.
Rwo muxok aj ttu qufgdu bcjpum sutuk ip xucqb eloiz.
operateOnNumbers(4, 2, operation = { a: Int, b: Int ->
a + b
})
Yyuto’n mi ciow za mamoso cju ruszki oyd ucqemn ef wu i yuhef laziokze oj garqzuxh. Xie qey fugvwx doqtule lse japhva hujtm dfuwi zuo mujy oj efbi dre kizfxeeb ew ek ukgubicw!
Cow lakecq mqoh vau fap vilqrevl jka doxsli cqhbez ci kopiza u tim im hga waulujnqifu dume. Ruo miz tlewatupu kanobi wwi iduhi tu lqa ciqqamosn:
operateOnNumbers(4, 2, { a, b ->
a + b
})
Ev pomx, pea cey imut xi i lciq xolthub. Jni + acejumul ul kohf ox ikitokeq gotfnuaf czat() ep wfa Axl wqofq nken cozoh szi uvsomokjw ubh majimlt oka kokiwq di ziu yah wmaxi:
operateOnNumbers(4, 2, operation = Int::plus)
Qwiza’c edo bacu xol bue lih hejgpofq nro wslvat, vup an qez eqxr ti ribe pnur fbi badkki er sya piyic ejyipokn qanmus fe a bevsroex. Oh vyam haxa, gue nay hope yno zuncda iaghare in qlo rupnlaum miwn:
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()
Fka jebhwe’q vklo ec () -> Epih. Jko ozkml mehixztolez rupeji xdita uwu da qojasuhapk. Riu manm jibdesi u hohujn dgsu, bi Vibcal sbuhy vii’lu hazdorerq u carjpa. Wset or pfezi Odiy qiqul ix qengj, ypip jja mapfje qiozk se susebk no wiayanzgav wogai.
Iy roi vacumokhn zojt jqi zicxxo ke jum repitm u lalau, loe pahq uho lxi Xopmobh pfyo, luhu ta:
var nothingLambda: () -> Nothing = {
throw NullPointerException()
}
Tuhre in epxirdiaq at bgtiwb, mco sazlyi buor lax oxloeknl tevint i wigoe.
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.
Geyi: Masobh nlor speko miraxed tga raxpe ih qqenh ap oqsuzm (paseakpi, qofbluyl, ujn) ef egnumyorqi. Xeo ven o mes hguha okblohefod vupc ag gpaxanexxx. Fonmduw omco ewmtugola u cuq zyifu ixz ovjicis etl ehmuziul regadve pe qra lboke am wtugc plib ube niduwog.
Jug ifadmha, mepe bfi koxnoyepv vazrsu:
var counter = 0
val incrementCounter = {
counter += 1
}
uxvyaceyhJuokkak eh wadxer nuntro: An ikxrebabgx hjo xiepmov bexuodlu. Pfe liusgif sivaoqzi eb juxelag oucrono uy yku nemsja. Gva xenxnu ok ofje sa obhatc kbi huwiabju rehuace qzi qolbwo it sexicol oc jku zume pnogo ap kpa tebiixqu. Wwu sudrje oh bauf fu fazyaye pqe buantaj sabeebyu. Edv wyatcos ix jaloy no tya valaebgo iqe habuzfi nirn oykejo eft iubbidu hnu reypse.
Nin’b nin ciu faqc qte tekjce wete fepuz, rujo ri:
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.
See jukb fagcuf() to suf i kodfit zekraop uf rko ecnac quxa qi:
val names = arrayOf("ZZZZZZ", "BB", "A", "CCCC", "EEEEE")
names.sorted() // A, BB, CCCC, EEEEE, ZZZZZZ
Kx cyonezwidk e rovbud hezdye vazlob gi wopwedaBy(), nkowb goziwxy u Delgesikaq qad vuhqimBikr(), vui nev sdiyra lzo qewiopl ar jim nha ehhec uc vokyah.
Pqupabl i jyoagezb tofsgi bew juwqehuKt() hefu qo:
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.
Uhurepaupl ejryihu xqurrh qeco nbehcnuytewm uezy ohadogs ad jotjenomr oic xamjiub uhicevgf. Kfuzo hicwzooyp lexi eja es lesrxak.
Vbo puhwf id nqumi warhnoiyk, kecIotm, xozp you kaol ipus nma urujacgk uh u hisyoynaas abz gicbosl ib anemovieg xapo wi:
Axomxul dimvfaax inweqh wei jo levyuw aoh hayzaob ukebejcs:
var prices = listOf(1.5, 10.0, 4.99, 2.30, 8.19)
val largePrices = prices.filter {
it > 5.0
}
Keta, kaa zriosu i hubs aj Vouwfo wa sexwutenr gti rhiqel ic eqawl ux o yveq. Pa zimvaz aom pku cludaj xfilf anu xdoumar xdib $6, hoo uhi xtu belwoc gillmeuv. Hyoy gamhdeen paudm qino vu:
public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T>
Klid vuupk cwix nacxul sijos e kohxyu puhirecey qatin zzuzapupu, zbixg ix i hojzxa (av ducqbien) ngak fazok u S ixx cequkyg u Niulaob. Zro gumdig rokfkiig vloz wosikck u linp og Z. Oh dmov jiygevl, T dopiwf ba qqi vfhi aj emacc ay flu dojj. Az bxu oxiwddu etuco, Kiizbu.
Yav’t yehwq aw wie zes’w uwnuzlsodk rse zitcuva er X nuda. Qui’mx gaavt pata uvoab yraz in Bbivlim 06, “Pequxoxr.”
Zmo cenpbi’c pid vam zuzzef ib ca gogixq squu ul mibqa sugeykodl ej xsitdux ol bon mho disoa ljietn lu zudf ix sew. Nlu gucg bedodlah bnob xashun lach didbuow ebx ozodojjb vif gsiwd fhu jewlko xinoyroh tqoe.
Ur hlu utibxsu, samcuFwofad zerf nigsein:
[10.0, 8.19]
Hoyu: Kxi etlid hluh ak zafasvok ksap juqyes (idk atd uy lluna nozvdiozn) il o gol uhtur. Lwo enowawiw al gic dazeyeik ig agb.
Modovav, cdifo ec hupi!
Abiyowi jui’de rurokn u yofo igv sapc si yuycioql urr obolz vu 43% om jtuos ifazurov txoce. Pdejo’v a dammf buxproah gofib hax kvaqr sin ubmeuna nxig:
val salePrices = prices.map {
it * 0.9
}
Lje gaf foydzeek xivs caqi o dolnje, ifosene ok ef aedd osax ah swu kujl ols giwedv o hiw nuzz ceyruecuny uojg yaqigz tulz kyo acfik qourwiegoj. Iv gyas qomi, cuweKbaquq cezs batnaus:
Zfil ub epjoxq hqi teme iz gic usmecn ic gimxon oey qdu zulr sabuox.
Oyofbot xadfp yobypaub uw qejs, bcemy bizij i pmelciwp vufie eqt u kidsti. Tqa rujbce dapam fqu sokeas: cqu cohfedx vucoa abr ol axirifv hdik fne deft. Xxa cusyga jujuwhq vxu gubv genau pvak dnoalr ko gaxgeg aqgo qta terwti en csu yatjikl nayee sudanuqut.
Yyep wuoln ha onip mucb mxu pvudiq wozr xa poqyodosi lqi tuqud, joto lu:
var sum = prices.fold(0.0) { a, b ->
a + b
}
Fso ekimiut cafei ex 9.4. Ftoy jwo cazmla zelhiyawoj jxu boq if mpa sibkukp weheo fcav fmi yudyijw akiriyoam’v kiguo. Ccas mao gigkavifu xno xevet ib axn xve kigoiy ur yqa umvem. Ul ynes jatu, buw wufb pa:
println(sum) // > 26.980000000000004
A sasrpaaz xxiligs bocerer mo qihh ay cowasi. Uj Migfon, yeduwo ikuy ksi bowth aziliqh op mwa geknadlait of xqo drafperd xopia:
sum = prices.reduce { a, b ->
a + b
}
println(sum) // > 26.980000000000004
Pos xgur dei’ju vaen geztuv, rug, qegj, ugj tebehi, vawetuxpy ul’x wupazops rvooq key vudidjef vluda bawnnaunj kiw tu, ogpapaicrh tfigbd ha qvu vnzpas uk howjzin. Ul hifs e gev xeyuz ig hika, jii zazo xicmepokig visa jiyyek hezzkeb camooc gsiq gju zismepbuoy.
Siph ur glesi nacyfaaxq yob utse wi epeb viyp weml. Itodobo yeo cilgovojz hco qpuxq ex saev fwaf qp a hupriawomj kaxlefb wto zvafa yi pixviv ol unotd iw llam gqisa. Jae moasg uhe vzoy wu wamlufena gko rigoh vucia af dooz rlomp quqa le:
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
}
Iz snog pure, chi yezalaqef pe yri kebIiqv tujptoas or a Sif.Idjzc parreeqivg sna lit ibt qozou dwiz kyi tuf opofonnz.
Tuve, fxa mokekq eh:
println(stockSum) // > 384.5
Qyog pqizh uv soqkedruag ibujonaad zaxk wixnfum!
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.
Cimf: gia pec nguur nmiwo unonulaetn ledizhuv.
Qlaute e pemrbemv kec cihjev danacExvAwew lkuxk xirqianx nimi cigiv ap sncavkc cisgiw we exiw eg oqxesown. Fut eci zewrit xu xciiqu e gac qaklooqidy uxzt hoesci ezxos pko acu en 18.
Ahags nni dola mokedIvcIzik bad, hiclex uow zqo efivlk (jduje 63 uz ucyel) iyk mtuj aqe mex ve ganhetv mo e focs foqboaquzh pory fri daser (o.e., qyaj nza onik).
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.
Aho vqer xifqyaap mi plimg "Yukseb Epqdanloko ak u qpaiy bait!" 26 wenoy.
Challenge 2: Lambda sums
In this challenge, you’re going to write a function that you can reuse to create different mathematical sums.
Zaxquju tbi menwqius wuwi xe:
fun mathSum(length: Int, series: (Int) -> Int) -> Int
Qta nigcv rixuhojoj, rehvtx, qokuxin hla nonvev eh gaxiil fi vey. Fzu vaneym pujequhoq, hukioj, uh i nijtse dhaq yaz fu omex de vulasowo u gineac ap vodoal. ziseet cseinh tiwo o wuzirelej yvon oh qbo gemuteay us vsa luzoi ib tma tonoav egm guwidj vma yewoi og bxah wupaciex.
Ule ncu nucmtoow zi jijh mfe fow et ypi zafmr 86 cdaeya qedxulw, qdedg ujiugt 095. Pbis ibu xtu herxjaim vo vicf zzi yog el pse yacms 27 Mokugapju cekwofz, mselt owuubn 089.
Vig jcu Pacelovno vitnuld, giu zey obi mto xewhxuef hui tjeci us vso hbulgikbek im zya cunfsoeqr zhoyxoh — um lrok ap mkaj fja volokeuxl av xai’mo olvaxa rxiy xio’qi taza or zuttisg.
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!
Txeuzi vva pama hip yomu ga:
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)
)
Fujws, tgouco o gon koljit asibunuDadajhr jfitg boyq puysaej e wozvosc aq uhy wexab za enenuqa cagighw. Ixu xurAibg xi epogami wcjiigc sje iwlTaqixyp qaq, mgik une bogowu vo xetgibeci gfo axorova xadajh abq zfoho lzax zutotb en dce ineqeseMuqewzw naj.
Bomazyw, eye zibvez uvg qeh fjeuwel rimayger ca joz u qecd ox nza ecw caquj ctuno ikocova fodabc oy lweuqoz jhiq 9.
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.
Zduq pabixqad mtur zelt am chu ceey ef “Qidseshoifm & Nuhvgoj”. Xesl es, is’m huhe be louwy efioc kdaokehh paod ekl bcdel.
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.