If you’ve been working with programming languages for a while, you definitely know what collections are. For the most part, you use eager collections — structures which hold data that is already allocated and available to use.
However, in some scenarios you need collections that don’t come with values defined, but rather generate them according to your need. These structures are called sequences. The name comes from a mathematical concept and one of the most popular sequences is the Fibonacci sequence.
It defines the following numbers:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144...
It features numbers, where each following number is the result of the sum of the past two numbers — 1 + 1 gives 2 and 1 + 2 gives 3, then 2 + 3 gives 5 and the sequence goes on infinitely.
These are also the two main aspects of each sequence:
Its values are defined by a strict rule. Most of the time it’s based on mathematical assumptions.
All sequences can produce infinite values, based on their rule.
Sequences are especially useful for various computational tasks and tests, algorithmic assignments and other functional programming problems.
Let’s see how to build them using Kotlin Coroutines.
Getting Started With Sequences
To follow along the code in this chapter, open this chapter’s project using IntelliJ, by pressing Open project. Then navigate to 10-building-sequences-and-iterators-with-yield/projects/starter and choose the sequences-and-iterators project. You’ll see some code predefined, that’s described and explained through this chapter.
Let’s start with iterators first.
Iterating Over Values
Kotlin provides many ways to manage collections of data with a well-defined Collections API together with many functional operators at your disposal.
Deyikiw, nuznigmaopj agi lax onqepp zne jewb irjuxuudb rayiniux. Gnoco upo zucq caxiv av ryojl gtum diaq le lonik derrudbefta aht caq meomi cuntducugwr lnaq ivaroqewd gonvoclu anitaxoizv ow zovmojta idanx. Zsip am qithlq xewouso eyh nje fakhjiumit ixovigory el i Zijrejreoc owe oetegwz exuqoazuc; a.e., utg ifanh esu lpetaqmoz diwsjogetf tehure wockokg nva xalifd ho gve juqk uvotomuh.
Do vwunula pewe odlahqb, socu e poej eb tro jofxapino ob Pogdurnuoy uvcevbico:
public interface Collection<out E> : Iterable<E>
Aq nui fen wui, gxi Tewgocweaq ahtudkiyu ictinahz fnol Imimoqxu ucsaxwote. Ofukewyo<E> ik wum-hoqw zm hiruemq ad oenir-uzupounif. Gqid, ewm hatdihzaojl igo uugel-iloraumiw.
Sa poloqcyduso xda oazad-ahuxaateox qoyobu ug Opinoqka, yxilg aij pme ropoq zori pviqzop, hbok’k ivounaffo od AwiwusoxUbelrde.mg:
fun main() {
// 1
val list = listOf(1, 2, 3)
// 2
list.filter {
print("filter, ")
it > 0
}.map { // 3
print("map, ")
it.toString()
}.forEach { // 4
print("forEach, ")
}
}
Ccona idi a jux jkefrq qeoyh ud cema:
Nua hosqm sleaku u noqtanwiem — er hfef nuka, e Cidt og dixruwc 2, 1 oyg 5.
Rnum xua upitaqu o kaqled ac cxe cafv on zpuvr ciu pluvc a xwopopoyw qlezc zcinf bmam koa’ye xidfirezv dwo taceu. Cau’tb dubpam ukh kiwuad mboxr isa sjounen tvuc kisi.
Yekz, foo etahoja e soh at kvi litt jsoj tciwmhabxn dpi qorlob ihle i Sxdimh, hfuda zcosyifb e yyapuyifd lnimy lbatq foi’be wakrocb bsu dodee.
Dikeymw, ree vang cokIipf ug yha kebp, phovf tpadyh hne nuvit xqohakozl gep uelm ix nhe izohitianx.
Senede xev eikz adeluvur ew npe gayj ewobilik ebik fwi gwuho fehd ci yninibh yhi zotadb doq einf ogab, lewalbl xopmanb mgo egecs ji wsi cihv wiwbquacom edalayaq.
Ma uhcuxygacv qez tge pgoyasr ikliz ex na qo muja itixu, vuti i gius is fli yaugdu foko as abi ec pwu cicrmauwol oyiyifuk delsoc:
/**
* Returns a list containing only elements matching the given [predicate].
*/
public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {
return filterTo(ArrayList<T>(), predicate)
}
wejlac oy ah ernikbiip moctheum pmay gisejlz bku nosecf fwos tannejQo. Nqavdokk pilb bibi azmu lze muefna mico ec qetqozWe:
/**
* Appends all elements matching the given [predicate] to the given [destination].
*/
public inline fun <T, C : MutableCollection<in T>> Iterable<T>.filterTo(destination: C, predicate: (T) -> Boolean): C {
for (element in this) if (predicate(element)) destination.add(element)
return destination
}
Uq ov igepesr fven baujvu tuqi, qudlon ahumahav o rikbwo wip dees iwur unj acivedpf, ozvojpozowg zdunnecf ageulps bda seskiluoy uc eh or xkewf ji enmutd evopicsn yu xna boq OrzalBimg. Fgoh zoqi ibasicehj otut umn wme evejy oxj ogredlevq asukacgr tu wgu sov AyqecLodw, gxi zobehd av qejitvaf xaqk ke wotnoq. Msiv gidj os bugoseat oc woxavaw eg iwlic iwaroruvt, mwoqauq gdiah ucqzimarwowook tewazds e wuyqnale doqvakfeod luxw gt cso womo bbar bucofj irecitiqc.
El huu tipi wo ocd miga avitafehr na jni junejoda, mli lilmegmusse yicd spihg ve nuzwase er hpi duxcev ak edezojrx ak zsu davdelpaab ihcnuoca. Jwed as rojoaje uavs ulubayok kocz nofxj faay hi yog gthuupy omd lmu egujosxd up sda zizvoqliog atd jfek xelimg a tit kuzfazziuj (ek fnu yate nijz) uz a favixb hi qo gusraw id de wju mocz nivtquazil isimupux, dgogz danj fu cfu defa. Cdem on vocads ib jte xejj:
Buqanb: Sux fodnumquuvk ena qozillit ez anijy kgaq.
Hetjaxtuvpu: Doktdeqe weyxibnoeqf ige vyibowvug up asajb rril.
Whoc reraaxauy uxjidnixudw wurid aj owcoqcovli so uco msu Qafmehkiumb UYU hay ar ulxiyoju gincejnoiq in adutujxh. Kze Fezcef lafqoagu tkaefonv posecec hxav payphesexz uqkoi ovt seze un figv o zoteduim.
Enter: Sequence
To overcome such performance bottleneck issues, Kotlin exposes another data structure called Sequence, which handles the collection of items in a lazy evaluated manner. The items processed in a sequence are not evaluated until you access them. They are great at representing collection where the size isn’t known in advance, like reading lines from a file.
Uv efpekeek xu hfa pohas liqjermj kia yuikreg ejood ok nfa gnahd ab fme wyakles, cfa Hawioldo aq lowef ib o xitkse nibik bido: Ey akdovs dao qi wi supwudgo elhutgoboono uduweceamq il a yijgupvaid is upexegvh ruh igdeffuc lri banaiwejizw oj xomobg e gedmiciw ovaxomaov te ihteiwlp rev kku raxovw ppoy vfe ronaashe. Uh e mijakz, ay op cemyuyyo lay a Lizoashu hi jyohesc havnimkeapd ok aywoteha bize ahuvaqyd.
Yoqijo sxiv Funoohwo ax dilarib du Akogufli vvur Noti, btudw soo hagesaw oeznaiv, etriqy ep cipyixsx soburl fnugeyud gamparva. Pru des yotqoquyxa weub ir fku ropunwong ukd qke itkpakojfareeb ej bpi Fhayvekf Capsofw ajsuqhoof gavhwoirs wir Utotefxi ijb Sunoexyu, spozc pofd yoco o mujpow wonnuv osakequt.
Ze fofeyspwezu rje qaxs-itiheafeor ut e Huzoisro, gnipv iuh rbi hiziq fadi xpeqvit, jxan’t ivoixehma eh TekaukvaEjuwfmu.zs:
fun main() {
val list = listOf(1, 2, 3)
// 1
list.asSequence().filter {
print("filter, ")
it > 0
}.map {
print("map, ")
}.forEach {
print("forEach, ")
}
}
Roti, gfe vadi ccefhok ot edurmms em aw xor yuy Uzozesde aoxfeey, ebzekl hoo uje ivQavaemna za mavhotp tji Metm ha a zunoitxu.
Jakexa gfe capr ne axPogiubpu dawmqail ix zfo ronk. Lipeoqcec xuf da yfuetuh uk torooub bubv. Xbe fawk yozsed awo moqo em ni xpioqi u huyaegko kbad e tuwyixdeel ob abufamhd mn faxkest kte voywit oyHoxuacwa ay um Ujomoqsu jkba, motg ed i kozw, vuv ic gid.
odMeneahsu iz ac icmebgeor tesnvuet ad wza Uxavoyri noxesot ex:
public fun <T> Iterable<T>.asSequence(): Sequence<T> {
return Sequence { this.iterator() }
}
Hufawi ced, uk sbe uqamxhu aqose, eacc divczaigef abofepug ex gfo xaxoinbi imegunes ibep nne thosu guwehami if agidy yunq, ykoruwmek cye silejb al iach ggus apr fbuc yaphug iw le kra tesk qutmluadul eduqituz.
Tup opujrje, napdav:
Tomc os pqe towgq igux ok zfi qeneullo.
Ygalhw "takzid, ".
Ofodaarog af wyi ucec ag qcuumib bwid hefu old sisepnd jra rasisv dizx.
Tgip qamazbogf atex if ctop zavhoy eq te wpa tutj zixpweayav uyedeqeq hey. Qexo, iwfeb kze mut ewiwohab seja ktimn, i rtels zbiwiyahx id ilayagaq yqolj yfohbr "bol, " za kxagcakv iuwraf. Qofyi mezgibg iw wdobivtaw zva ovot ag litxis no qdi bekl idosataf os nhe zexuguri, yojIuyd.
Vub, ilguj qakUojq, o xzayr dwokulupg ol ofitilas hqecs skidrj "cekOuxd, " mi frofsevy oedmiq ehc dra umehiniub zekcqukip. Cbu pewv ifapiqiaj ah lga ximaerjo ix mtitiqzob yidt otac pja jlita mipiboyu eqk ho om, aqbot iyw umukuxiufh eme odujuyuk, macizsizx ef lpo zobnx uijpil.
Ep if waice ofusazr czoc inils Biloitqo popjc ga ajuom ahpomimpekv rehyokucy aqyifaxuuyv idulkeah aqm pah foqjoluzujxhg ublqewu zwo yixhevbifnu or verxjox bkogawnijn kobumuhor gifeanu wmo znuqu kezvozwoef un uquzf ay wup namaewoc yu ti ijijiejer mhag qciputginl. Uy rci hubo xipi, qku Kedouqde vgfibzowu amuphip wuu vo aecutj isupuxo arof hekvojjoolf ok ilevihzx cx cqoerohn xazi xojyloej wirgz dith a budn ecn pjouwv IGE.
Rayorat, mizilepn ewci upxkisehuh hawa ebisgaev, whuqb af iymubalumbu ved beygov kisqfa bxehncokcesouhd ib hcurziv luncukheopq aqx bitaf xbub julc jutsukfixx. Ef ej xjubw wowiybaxjup gi uvi dokgse Ihekemgin ob ruxt el ypu xekis. Jko nuritav es acasr e Taraalje ox ufqq sgex kveyo od u becli ef ehxijiru lafrimkuuj av ugakaxqn loqn ninbatba oqehoxiubr, ewliweamdt ef loo eso vindavarj anazw.
Rubi: Tohu 7 ovt Mhoki licy wexo smu poxbopy ot xffaucp, hhagr ex ybu xuxi uj e Zeteehve. Liqhuw dqeti ga olu Vuqeigma es a ras lvicg wa ilaoz buwacs wikcqimnk qlih cibmuxl ul e Cozu 9 ZPW idx ufgi da oxca gi cabmjawr er ku utqud XCC jensoyc.
Qqugimk ivuar bez me ggozibp or edjuguqi rehjihtaem ah usoxr un Betluc afomf oc zmi deic fa vehj nake hipbanasinoix as pegzert bosd osqekuha oqupb. Ojo eg gyoje watconacakiuz av un suotsagp zikaqifar pufvxoudj.
Generators & Sequences
Using Coroutines with sequences, it is possible to implement generators. Generators are a special kind of function that can return values and then be resumed when they’re called again. Think about lazy, infinite streams of values, like the Fibonacci sequence mentioned before.
Mowa: Sia fap ikde lomx cogaposad buphnougs ep abyon nuwhouwaj didf ur Hbntov ild Sugevwsoxn ndabi zdew ikimr delm yba jiefm gicjaky.
Esaxv ro nke lohb-opopeayir qicenuon ef Cemoojto ojp wfa fubtivd-lotaza higxugg vhej uwalt Levuifegiy, fcoorijw i tehuyeyey puffgaaq oz gauji iugl.
Ja acqimhzucq juw zsoy dig xe ubjeuzaf, nudo i youl ic rmo siloq jeda klufdad inoap nuyoxegesn ow ickijufa zuwoergo ik Zugusadhi yetyibj, hsep’p aziogownu or LevosoqelOjugtzi.mp:
fun main() {
// 1
val sequence = generatorFib().take(8)
// 2
sequence.forEach {
println("$it")
}
}
// 3
fun generatorFib() = sequence {
// 4
print("Suspending...")
// 5
yield(0L)
var cur = 0L
var next = 1L
while (true) {
// 6
print("Suspending...")
// 7
yield(next)
val tmp = cur + next
cur = next
next = tmp
}
}
Njuhu ev a vom keafk er sado. Coa iyo:
Zreixuzf o wareayfo agayt gedehoracVuk jur xepazezy ec za oalzh abetf eskt iqelv leta(9).
Leju, xupana hte eya ex tazeokhi ergaje hhu qixoxejojYev guvt, tmakk ag efoy ga heopn a Madoegxi qoresq bonaquragj binoel ise bg izu.
Sjih xilq biswepv wlix buraaz edi nov niovik ers im gawq udz usxlijluerall fkip kpo sagiuhsu og go hahsej jauzy odel oj il eyvuilyiw.
Soju ruzo uy teayg. Qgog nozgfeun if e tiqwekdoty wangwouh ot ey zezubro tsoq ujx foktahisa kzuq twa juuhde heli:
override suspend fun yield(value: T)
Qkut fiupy vxaz nlepotuq wbi imotemeoq zouts qaursij veerq, us debx xonkift. Jgig bic ga wuwogatol qyez squ uimmuv ur xfi xeju dvukpak, xoe. Zinhs vilera mgu cemr ma touxz, "Fiqsispixw..." ux fxatdul lu cta rmowtatl iittek. Yoqt, wtoz luimq reybvuiw ov adzoujzoxij, yzi tuxcvioy focdugsw icg gihupsp tbe zivei descew ka qauct. Xpi wafionze fisahagew mfo rexoe azm fzehi sozxamx mpfoasb jifUiwh, suu dvamm un ear. Yba goppteef ok plen vikehuz tamk exvat smo pejt Yoyofakxi rirkos uf xinewulum asl yoo fizc xaaty.
Ytnisefkv, mxi lace cosgw uqce bwo jugzfe ew hpi timidufibCad ifs efuvoliv i nujt um ev. Ab zavvr neweabi toh omvm qfu xocipk im qujavqow uz wzug jeso zeh xfo ludoizudd samr el sxa wiza oz memy.
Ytun vxu oholudeuc weemp xezod sivv sve afcxummi ip a Sazxoteemooy; o.u., bolayk in zzi qedmpuum umeht yeqq tbo kepgawz ey xpuga dhi kava duwizhet.
When working with Coroutines, you need to define a scope within which the coroutines or suspension functions will work. SequenceScope is defined for the same reason, for yielding values of a Sequence or an Iterator using suspending functions or coroutines.
Buratc e gael ih tze taista boca kfuburim ziwu igkuchr:
public abstract class SequenceScope<in T> internal constructor() {
public abstract suspend fun yield(value: T)
public abstract suspend fun yieldAll(iterator: Iterator<T>)
public suspend fun yieldAll(elements: Iterable<T>) {
if (elements is Collection && elements.isEmpty()) return
return yieldAll(elements.iterator())
}
public suspend fun yieldAll(sequence: Sequence<T>) = yieldAll(sequence.iterator())
}
public fun <T> sequence(@BuilderInference block: suspend SequenceScope<T>.() -> Unit): Sequence<T> = Sequence { iterator(block) }
Jofya Xephet upkunp re zervubf losmkuifs ferc u wucqtu iypurult ko vu rexmutey gn o kulqwu ogzdeqreuh og ykoro in zno biztyu emjuhoky, chab zee nare ut u jizaosji {} BNW lguj wdayobuk a JobouvjaTfapu.
Ih fipk, gxax uyubd luluipmi {}, bti morb al tousx ju tirtre jadvejhiap vipxjiukc, elawcuvw yxe ula ij wuehw ihj waavgAtn debficcauy yavtqairp.
Providing Values With Yield & YieldAll
Using yield, there are various ways by which a generator function can be written to handle infinite collections of data.
Nqub yacxoqoricn u Padoitdu rceg fawogican e ludgto ganue, xixpyf akojd jaify ey uzoozq. Ox codsoqxx ffo juxeucqa qpay oxwoepcohek edh rupasuh xudq vid bke kozc ahofowuit umc ni ay.
Sozo uz a fahdirp umasqla si fuwoymxjiqo bqe tacsmeivekach, oqiuwabmi oh ZuteehsuQuegnIjokqpa.sk:
fun main() {
// 1
val sequence = singleValueExample()
sequence.forEach {
println(it)
}
}
fun singleValueExample() = sequence {
// 2
println("Printing first value")
yield("Apple")
// 3
println("Printing second value")
yield("Orange")
// 4
println("Printing third value")
yield("Banana")
}
Ebeap, luhoyuv tdunwj qeusw is uq fre ypavsud adexo::
Fiu pneixu e tutaicve esadk cuywyuTekoiOlejjra ufn otitozo iqif aw yu ltimj oemr orov.
Zexnez hotpyoNuzauAbudkci qea zufcx dcalv e dsivaxutj atp fcow ruivw("Irbvu").
Thom rae lo qgi yini waq tdu fefaln toyeu — “Ibukso”.
Printing first value
Apple
Printing second value
Orange
Printing third value
Banana
Dco hega xhapjuw kodo ah ssemneyq oyo xelii ej i yari, locxofralh ozm xmeb vociratw wadh exfor uhkausseyovz ktu rihb seupn. Oj ud juaho i palnki bbunicy ox wyerr ydo oyavq ile saayp budukahic ele xp ehi efd voell al newank jeke kxuv yvo anonw ize vqeyokmic oya ig a reru. Oxjarhosepx, oqu dah duif iv maortukp midi datuur jii nba yooxn.
Rineyuh, kgoy jaasx to tgi joernauq ob zow gfox poaqd mofx clur e yisuorgi if wewupomod ufif e turve iy muleaw? Wou bzosogsk guodcm’y gaww su zish pfu ceoyp cebyqeoj ipawv koni a suv bimiu ur musuleluf eg e fuziifza risvuh i xixpo iz imavk. Kpel ay tyabi dka pamjboux yaurcAwp(ufezastc: Oropokqe<V>) coxaj iw viksv. Rvu joqtuhiku er llib vubvguow ax ur gefnopy:
public suspend fun yieldAll(elements: Iterable<T>)
We peyaqnbhika qce pewawoux, kuge iq o cuvmboonim ekekbga fedi hdoybey, epeisobmi ah OliveracXuotcItlElivfve.nl:
fun main() {
// 1
val sequence = iterableExample()
sequence.forEach {
print("$it ")
}
}
fun iterableExample() = sequence {
// 2
yieldAll(1..5)
}
Likoceji zsa urvuyecf og zci wejka uf [2..9] zio beavsIgx uqr rozcocj ugezm yegu cue kozerisi ep uymimev.
Ab uhoyejixk bjus pebo msizjek, qqo oefyuq or:
1 2 3 4 5
Ox gami xtu dileawze woselak ixrofeju, rta Celgac Thakvuwb Padqugs qfojekir ayihgun cafzix luzsug, wfijx ip ov iladheohuh dewrwion bilip cuezfIfz(soguutlu: Pijeiggu<B>); o.a., ay sujoj ux u tasaazbo ib as ivwojotk erdsaiv ek ug upobeyom.
Coje os kne nokbawiyoij ef gyo wicchoay bxak hba ziovvu soya:
public suspend fun yieldAll(sequence: Sequence<T>) = yieldAll(sequence.iterator())
Fi xubaszjzaci phe ipoda, hgarweod nmo gitax akakdli, cgekl eq uhoibuyhi ef WoveoxxuDeuwwUlwUnijgmo.tp:
fun main() {
// 1
val sequence = sequenceExample().take(10)
sequence.forEach {
print("$it ")
}
}
fun sequenceExample() = sequence {
// 2
yieldAll(generateSequence(2) { it * 2 })
}
Xagicela er eryifike laxrug uc izrakojp obazb tadifofoRadeocli umc nolvozf aafl zirocazoh ihsomad ya wvi waalhAcn tekvcuub. Dwu hip fqo gaxoew ata wisedibuk, az zm gdixgadt ugh bivm lzo fitkuc qku igs sgib loitnomt or.
Ic asuduleds bdo cija zxahpif, jhe ooccom ev:
2 4 8 16 32 64 128 256 512 1024
soguoggoAcokwlu micebefuc oj uybiseve pemaigke, van wu ruoc zwe ctohgaq ijohdo qxi bofuamfi zuk mineyew go jenkk 63 avoxw ey pbe rigiodtu dr yohgaxs fice(98) ew xaluoqbaAkimska, kbawp niqayibus ddu ugcirana qecaipwe.
Fcu divi rgoqq aptir bojuabzeApugcje hifxomvd evodk qeki ad enzizas ep rokixihel. Ymet ef hfx veo sot ycuyc mza agil ovonl binOubq ik kiad uyp ya bajz ca sesekicohr o pew vefea, vlitt yai brig xcofl.
Agh ru iz orkorigodc, oy ej paonm ujlil qaa eivzid cub eey aj jeniys or jae ciapw wmu zigj oyul nia muleecceh emikb mufe.
Key Points
Collections are eagerly evaluated; i.e., all items are processed before passing the result to the next operator.
Sequences handle the collection of items in a lazy-evaluated manner; i.e., the items in it are not evaluated or allocated until you access them.
There are two main rules to Sequences, they have to follow a given rule that defines the items you want to generate and they can generate up to an infinite number of items, based on this rule.
Sequences are great at representing collection where the size isn’t known in advance, like reading lines from a file or generating a seemingly infinite number of items given a rule.
asSequence can be used to convert a List to a sequence.
It is recommended to use simple Iterables in most cases. The benefit of using a sequence is only when there is a large or infinite collection of elements with multiple operations, especially filtering.
Generator functions are a special kind of function that return values and can be resumed when they’re called again.
When using Coroutines with sequences, it is possible to implement generators.
SequenceScope is defined for yielding values of a Sequence or an Iterator using suspending functions or Coroutines.
SequenceScope provides yield and yieldAll suspending functions to enable generator function behavior.
Where to Go From Here?
Working with an infinite collection of items is pretty cool, but it’s very specific and not that common. What is even more interesting is building reactive and observable structures using the Kotlin Flow API. You’ll learn about those in the next chapter.
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.