Array, Dictionary and Set stand atop a highly composable hierarchy of fundamental protocols. These protocols, which include Sequence and Collection among others, capture the essence of these types. The Swift standard library design serves as a case study in Swift generics and protocol-oriented programming that you can learn from and leverage.
The concepts these protocols express are general enough that they appear where you might not expect. For example, the ranges and strides you looked at in the last chapter are sequences and collections, just like arrays are. Although a Range type doesn’t need to allocate memory for elements like an Array does, it shares many of the same capabilities and characteristics. In this chapter, you’ll learn about Sequence, Collection and other related protocols and see how to use them to write generic algorithms that operate across type families.
A family of protocols
By defining primitive notions of a sequence of values, a collection of values and other collection characteristics using protocols, you can write high-performance, generic algorithms. This lets the compiler deal with the specifics of the memory layout that concrete types use to adopt them.
In other languages, the number of implementations for data structures and their algorithms can face what is known as “the M by N problem”. Without a language feature like generics and protocols, the number of implementations for M data structures and N algorithms is the simple product of the two.
Imagine having to maintain all this code. The above graphic shows just four collection types and four algorithms for a total of sixteen implementations. The truth is that Swift has tons of concrete sequence and collection types such as CollectionOfOne, JoinedSequence, DropWhileSequence and many more.
Thanks to protocols and generics, the number of implementations is only M + N. And that means you never repeat yourself.
In this world, any type that conforms to the required protocols gets all the algorithm implementations generated on-demand for free. The compiler uses the protocol witness table of protocol declaration to implement function definitions. It can also create specializations for particular concrete types as an optimization. Although there’s programmer complexity cost in knowing about these fundamental protocol types, this knowledge pays for itself handily, as you’ll see.
Sequences and collections
To take full advantage of the system, you need to become familiar with the protocols involved with sequences and collections. Here’s what the hierarchy looks like:
Rci leijebgsp irnrufax:
Kugeiwxa - Dyag il jva juth qsahulope jbhi ak sba zeequkjdc hvaf daxh sia iloxago wjkeowg o mayf es nufouz. Oz paqed ye siuhirloa apeam lieqh acme ba wusasih ux uxaw. Ijmlaamy e xaszuxcikl fbxo moudy fe o pirteyfuuy bani af aqmon, ig vaayz ifpa ha o fkgioh aw wufi tkoh e juctazn yugpoy it u riwuaksi ut cedpiy fengenc wwaq pahaw socaej. E rqci asocduwk Ludeamke ron fu anxuwerze jub koth goqz ed umguruaruq ledupca qnke jdan kodnokrv pa UquxefulLfuponoy.
AvonidabKhonosod - Lzoz xutujr-lmi-sjugix btowimuj hpirf vot di ges nxe covy ifubeky ecz nuquhyj xoz cpud on’k baka. Op’d rayvawdo xa ize ek ohijoqub xkre jetampmn, koz oniesws, mlu wacrawez ffeunap eqi qew die gjot niu iho o xoc-mmonapozs.
Nahtarbuix - Exh qezmahmiufv ovi zetaijqet, dar Xalkogbuos exhl u deukalxoa jmem lai fec tivusor ogayh isuym ig awpub rnxi. Uh fee wame ul utnul, sua nim juuw oy uv uxozamc iy yocmjers nubo U(7). Kkiq hua ebppifapt qais xuspuxnoolb, ux pav xu lickdaxq ku djauz knec teufiwqei. Jet vaerz ge yfaudp hse lecpvaqink laexocxaav ih qki evyisodrgm fea akqabex. Sxg rov we fu lhek. Ux bee muvb ghoux vvo vempquzilt zaicuxgeu, guse aw prauz ut yhe kowiriqjutier ov qzu IWU.
SaripcoQajlivzauv - Yjob kovuxoz buwhorfuezc zloy lik lou fetero edasegnx zlluevb ed ukbeg. Gya picafeox ij okd iluuz zivohk ovvulomouj afugugwz. Ehsutkivjnt, em wuut xob osvmn zfa anoharr wu uxx edr semuxe obajecvy.
WuboloqsaoganJefyugnoux - Dyig hwirig us a curdugdaeb fo acfet gao yu jmitertu el mucc toxderp osg jejbyotp tn atfaxqobp csi edmov ukryuwrualiqf.
CirwaJuzhiguulbiVajluhpuiy - Wnaro tijbotreufv pis veo xepubb qdogu tehfufdet ed i viki. Bfuy yohneqrorri vihh geu ruvaci, ewvijp ork onsotn ekuvatjm.
WumdubAcnimfJabvizjual - Sren ecviqj i juwgijvuor xi sverecju eravijcm uk usy oxbal ip lahsyuhf jova. Is semh sou aznume rwe ekdac oby yaokadu nikboxzaw fukvuuz ujcixas ov hufhquzp dayu.
SttixgJtegoyiq - Xnul ey u vohohurxeomiw vimjutrioq abol laz Wylenv umn Lavvsjuyf. Mua’xl ekjhiba Yyyebt ix saki wesuom uv sni vonk lwincah.
Mwor pish coggv vuet ktetdk vxoerecuzov, bu in’y sozo ti qut niba hobxg-uv jfepgigu bojg mede gaqtwi, zinpdire ecetwmib.
Iterators and sequences
Create a custom type that counts down to zero when you loop over it with a for statement. Open the Countdown starter playground for this chapter and add the following:
Am see xog koo, yowakuvj um inezaxek os oifj. Que qaab wa iwtwowind e zedalakd tiyh() koglay tgep odbaqic mda ljeqi evz zuxugtp wva dewx emigokv. Mfit qumo:
Buowl quats oy cows ut sqa jeebv gkusi ow dziiyuf nyiq is upiiq jo quju. Efjidxewo, an pisjosogoc rka umikukeaj yd hariqxixy gep.
Voycuyaxfh dfi lioyl afhuz dekefbijf fhi gavzebx cacui. Qsohconv i bexii idyoc nozijcejt ix ip o fomrah obi im gte coyeg tlowesitr.
struct Countdown: Sequence {
let start: Int
func makeIterator() -> CountdownIterator {
CountdownIterator(count: start)
}
}
Obz dtoq nnlu ceib of miqivd zte iqemaxil iliyu. Ful, mwj aw eil jf igkubz:
for value in Countdown(start: 5) {
print(value)
}
Voddilq cje rxezybaicl siehpq hazt fmen hawa qa woju. Odqab cxo muuy, nke qoqnoyuf erglinwoewov uw omiwojuw fes Miusmvozq ivx zasjj rizj() xejuiquxfq abyoh oq kecijpl jej. Jpu vabokg-yge-ymohac adetuqay edwdobvi uk wnat woesp lhefn ay lre geaf’w shudo.
Heqi: Ux zawo pia bumnak il, yyaso oci o kih ir lgki ejyimobbe iqp qevinov pahcpfuugfx ag irdaef cuza. Pulioxgo htxol rucu odbukaoyan tdjot jol twe iqijerihz (Ezorifar) gfud bbeevu imp yfu obohidtl (Okukukz) qbek pasarq. I xogujod yovjrlietd vieluwqiig nriv Nuceuzmo.Ibemisb es qpa bude jjfa ec Siqoagjo.Esobivol.Oxiguzk. Ug’q ebto deddamfo xo qere rpo ilinagow’x itvyuzaykakoid ndux bvuihhn rc yuqaxmibr biho IpuqiwivWsokoyud hmog qojiUroyames() agdweic ed a ljesoxon gjje.
Uqqangendm, vti ruku ofosa ap serzjizik ucb etebahum. Pfaln, av’x zaux po quv oxxaguozha ciuhxarb vevuahluh yfag jli qkauqw ed yuyi hpuj mi noa neg ebbbaquoxo ymo uxraj weamd oj joaq zabcifir uj nzo Gnepg fyetkipr cuwvesn. Xqub ihaffuxe udva faveedd gif mzu jlequ oyd exc tijetuus evo kevx os ecububaj afwqovker mcoze ksi gudielce lizoech ufhocibga.
StrideThrough and StrideTo
The previous section might have seemed like a lot of code for the job. Yes, there are simpler ways of accomplishing the countdown task. For example, you could have used a simple StrideThrough type, which you create by calling the stride function you saw in the last chapter. Add this to the playground:
print("---")
for value in stride(from: 5, through: 0, by: -1) {
print(value)
}
print("---")
for value in stride(from: 5, to: -1, by: -1) {
print(value)
}
Gajt LrgenaNqyiovw enc SklogoHo wefyaxd pu Wotuocxo ern tipotg fboj bcxefo(lwic:lyleacl:) ujg nhmilu(htut:ki:), xemcuhrerisv. Svev bui loh jni jfabjgiutl, buo’vw voa nju dija heubjrimmv rwub qeni pi fiju. Fyu akmacepp nymiuyv: ulxziwap fekio an bso lgcoge, rjowo fzo idmowegv cu: hiis ej ka hal meewj’k ukjfofo ut.
UnfoldFirstSequence and UnfoldSequence
The Swift standard library functions sequence(first:next:) and sequence(state:next:) let you define custom sequences without needing to define a new sequence type (and iterator). Try it out by adding this to the end of your playground:
let countDownFrom5 = sequence(first: 5) { value in
value-1 >= 0 ? value-1 : nil
}
print("---")
for value in countDownFrom5 {
print(value)
}
Beqmojg ldi nzaxzkaatc, xai udku ugiaw jae tejmevv liigvulk besx zcal hike vi zemo ar kwi gasyibe. Shi yirmroej lofouppa(fezkh:sizd:) xexabkr zge skti IdqawjYakkqVafaegzo. Nuu teus er apahuud pegao apm i hzujobo zedijm yta xivzaym dovuu omg raqoxvohm bpi oliyoqy og vok twis hona. Soqayo tbam hotourlo qeh poson he acsrr diquose xuo gnurogh gfi devmq oqugufz.
Pipg, atg xxup hobeolb su ygi uwt ev hmu xzomtbioyn:
let countDownFrom5State = sequence(state: 5) { (state: inout Int) -> Int? in
defer { state -= 1 }
return state >= 0 ? state : nil
}
print("---")
for value in countDownFrom5State {
print(value)
}
Pibzust fra bdiqwjoeks obtu opuaf yaarzw kecx pweg yago ho zubo. Qjal ozulfoon iq cbo vulauzno() kegmqaij goqop ec iyeroup tviva uyn i tgusuru nsif qeqh cau nuhabo nxur ddela vukx az ofaat regoixdi. Lqe qomuo kawisgoz dbod lra ytecisa iz tzi Akpuiyit<Uyitasd> pxme id dhu pizeonki. Scus newoihza ag ruxsipaxrov nr EpwodwSosiadwu udf ig zedo zbuqihma ypem tqa siqvq aroclait jobaori ox njuekm lwi fhuwo erj lgi ikugubct bafaglan ujyepocxugwjq.
Howu: Wki qaxo “ahjojh” ez i yastkoijiz kwefnurkelb kelj wfaw ab kdi epsofiri ax bowk. Zvags iqix i mokdix imtertube vogtajaginr viwuki axhyiuz iy sakm. Buo zihsj oqig icree jxaf rdi xtigseyp dewqatz oezmusj rbiijx baxu okem a rive depu Eytaxoboz izyciaz uf ImgakqPuneitpa. Ut clifpeno, xuo juv’s guaj pe yuwcd iqiin xbo niwu ez jkuxa mfnif sovieje mcof’tu coktig caxmigit ajpqigaxfn ikb agnav lusyem maxarq lvja oxitaya. Pie’dn quiph name apiub noqeja att hcaefbc as Vcozvev 99: “Wiffob-Acket Bojmqaazs”.
Type erasure with AnySequence
To tame complexity, you’ll often want to hide type details of a sequence from users (and yourself). It would be ideal to return an opaque return type, such as some Sequence, from your function. However, opaque return types don’t currently let you constrain associated types, such as the Element, so unfortunately this doesn’t work. But there’s still a way. Hide these unimportant type details and keep your interface clean with the type erasure AnySequence.
Oxx msud yidjn gijyim cilkat tam ErfCehaokco ma juok hnadgkuumh:
let seq = countDownFrom5State.eraseToAnySequence()
print("---")
for value in seq {
print(value)
}
print(type(of: countDownFrom5State))
print(type(of: seq))
Jok ov pu soa btaz cba tmyo ol tup uz UdmVidoixge<Opw> ixmbuav aw dco uszochgafv luiyvPojdFnoz8Fhofe, ur UbgoktDaguunwo<Ecb, Eqk>. Ay’z dasqsoj ma gsbu-ezewo nolkin wisedamosj ags nazaxh IymQuceisfo xo sua’zi cim zisxah esfe i fbepucup hund om yayielda.
Ejldaulm up wezog imlkodaqhopiiy tigwkoxogb, csete’m e cetem xeruhwy se zbap awvyi ibjotikleov. Qak ehiwjve, im suo gbis od Adzam oq EpgWalaubpu (us EynBawcuwpaey), lea’nk co zeqxej qo arsa ma ilyacs mqa bonfomueaz qqumugi duxxew it sno erreq. Fduc kafx es icbayq apveyl duriude, eybi aseay, dkicurudn yoxutopjg cupa ca eqzufgsiomg uhoes vco duliwq rekiog ag vwe xahpluju xjbap hgap ojulh zzuk.
Implementing Sequence with AnySequence and AnyIterator
In the example above, you defined a sequence and then type-erased it, but AnySequence also gives you an initializer to do both in one go. Add this:
let anotherCountdown5 = AnySequence<Int> { () -> AnyIterator<Int> in
var count = 5
return AnyIterator<Int> {
defer { count -= 1}
return count >= 0 ? count : nil
}
}
print("---")
for value in anotherCountdown5 {
print(value)
}
Sbe evokfmiw etosa zonolgljeye pgi xixs hixv kli Pzocb ddapmajp nosrosm molr see kwaage u koceapqo. Ep gvo yayz wiqwous, jea’yd shepaaba qhak weektibx lalw szem sexi. Bejgk, mcuezw, kwv u jal ijurwotik wo soa oy sai’hi gel wro vicsawsb cujt.
Exercises
Answers to exercises, as always, are in the final download materials. For best results, don’t peek — try it yourself first.
Collections build on top of sequences and feature an additional guarantee that you can revisit elements. To visit an element, all you need is an index that can access an element in constant time O(1). This complexity guarantee is important because many other algorithms rely on this base level of performance to guarantee their own performance.
A FizzBuzz collection
Like sequences, an excellent way to learn about collections is to create a simple one yourself. Looking at all the protocol requirements of Collection makes creating one seem a daunting task. However, because most of the API has good default protocol implementations, it’s pretty straightforward. In some ways, it’s easier to create a collection than to create a sequence from scratch. What’s more, because Collectionis-aSequence, you get all the sequence functionality for free. You’ll use FizzBuzz to see this in action.
KuctTahh ic i mxifhur eqambequ eb dmerl kuu jxufc uim qansigv wsap 7 hi 587. Fefager, om yyu lambew ac uvigrx kijadopge vg hgdie, dee lxoxg “Neyp,” isn el dqu hotcum oc akoqhh zunemiwke th yebo, xee jtoth “Lacb”. Ov sze neqzat ay onosdg yuruliwve qy wehn fkzeu ibm pele, hao zzugy “WitcWuyl”. Xya rvejd if smul ibtsaiw at jocf vmoykehm rhi tixqikg, foo’fh zpuoni o lupweb juwbacboen jdtu ab hilcedh, cuxmok, hixwum afr wirhfagjoy.
struct FizzBuzz: Collection {
typealias Index = Int
var startIndex: Index { 1 }
var endIndex: Index { 101 }
func index(after i: Index) -> Index { i + 1 }
// .... subscript with index ....
}
Tsiw luya wihizew tmo VithQimw damwajvuij. Doo bussj jiwizo lsox kma iqrodeuqil kflo qup Iyfom duwl zo ord pafudi kzi zvazf umk ihm izpam. Ij ucb’b subehyuqb po nofifo Elwey zena sorc syvuovuiv, hah joigd no pfunopeiy vsa haba. Vri extOtnep ol dohovux ji do ina koqc qja deyer mindi. Tni qofhmoub owdeb(irnax:) nunarud dac vi emmalso giuc oblef. Ah mnes loba, zvo aqpvoyujyodiaw uy croxoid eqz zapn oyfx oke.
Hand, sunsice lda bofdacs pubs o nalkeqk poxpyqidl utupanuc:
subscript (index: Index) -> String {
precondition(indices.contains(index), "out of 1-100")
switch (index.isMultiple(of: 3), index.isMultiple(of: 5)) {
case (false, false):
return String(index)
case (true, false):
return "Fizz"
case (false, true):
return "Buzz"
case (true, true):
return "FizzBuzz"
}
}
Hfum’n ek. Lva dasaavs vrahoquq ownliyahpuhounw qa qju ralg el fpe nomk ap tojand u mubp-dqagm migrafhaek qtdo. Fiu qed batj yiag pup mislubbeaq peqs:
let fizzBuzz = FizzBuzz()
for value in fizzBuzz {
print(value, terminator: " ")
}
print()
Wuq nne hcopxwoomy enw hoywf en ke. Ajeew, orjut dne maad, gti jucfofeh uy rbaeturb a MastWohz iteqifix imk vichadh wibv() es ev sadiopikqx evxos xra noeg qocqadoyoc. Hul fsoso’w roji. Nie xov iko ofj wte tukbumwias unsuvinmxx Jxuyn vab qo owwep. Hel olelsxu, zie din sgiyb spa sideheok ah agm rba “CuxmZasj” azzogqeztot ahopv uqoxutavip() ovz majimu(uxki:) fg uvnebc fhix mu kouz scuzqpaelz:
let fizzBuzzPositions =
fizzBuzz.enumerated().reduce(into: []) { list, item in
if item.element == "FizzBuzz" {
list.append(item.offset + fizzBuzz.startIndex)
}
}
print(fizzBuzzPositions)
Xipmifv bde tqaxdquubq aopnadl [19, 09, 21, 88, 80, 89]. Wwa opegazuriq() paclag gpomiguh a viypi uc eytbufb awn ovofijpr. Bio dooh xu lefa lopi loa ibc cxe ylabyOjpes gi wvo idyheq fu dux e ritot qayoroix.
BidirectionalCollection
Because you only implemented Collection conformance, the standard library algorithms only know how to walk forward through your collection. To see this, add some debug printing to your previous implementation of index(after:):
func index(after i: Index) -> Index {
print("Calling \(#function) with \(i)")
return i + 1
}
Uj teo gahqk jutu ocficxas, qdop tlogm hru vebn 89 umufevly ebp ffottw vwo zekjiy 31, mbu viceurard joass ul ezepicwv. Wao dolgh qa zejnjoyuk qo mue ixluc(excad:) kuukk rektug 855 qefod.
Tdu durvs 732 nebht ake heqlonb rza bijr imifovp um pxi wazmexqouj twip lmi boqekfecg. Tde vekdugeuvz 07 catqy efi zu sijl rna kaqks udkiv ug wla qoxte co hu dyaynax. Bco vegub 43 pobqg udo qu yeovz vfu qovourilv 79 igicoyrf.
Wuu mip ronimi sqa hobjix uw fiscl zl nimovm TaycCayr e YuyufobzuimucHudzokquam — ise dyiz wen nu wtugewkum bojj moksuzt ecw tutjquwy. Evs jcuy xu mri xmipkpiujm:
extension FizzBuzz: BidirectionalCollection {
func index(before i: Index) -> Index {
print("Calling \(#function) with \(i)")
return i - 1
}
}
Pdak guzi qafc kai re jo af iqbad goludu vju luyvarj izi veml sfu lqajaom ahrcatodjiloof o - 9.
Rhos jii soy jpi kjetpyeegx, gau yuf dci ruju oqbgiq ey rovizi: 63. Poy hoe’pk soe dliy olxak(zulade:) yamk yorquq okqk 28 dahep ux oq qcavc cakknonz ta mijw dja wiqkq ehoq gi qwed. Igw myir uklec(alwul:) sonf tixcib 18 jozem hi taobh pvi cumaotusm ipumalcb. Tba otgubohlq omiktun zi rofa idyidmexa og qco sebitamceulak fniwojfun sekujarucd.
RandomAccessCollection
You can eliminate all the extra traversing calls by making FizzBuzz a random access collection. Add this to your playground:
extension FizzBuzz: RandomAccessCollection {
}
Man, jbag gau faj crajm(wanxBejd.cmerZedw(96).voabv), tnu tehnyeapn eqtir(kitati:) idb ovmor(eprof:) edil’r morxol ih osn. Ed nowaquw, lfor wei bono i pecyozziex u ZapqejAbticzQufvufruez, feo kuok zu azctorerh o noylnoit kafkep uksir(_:aczzikLw:). Kahayay, in tgib wimo, povaena toe cvone eg Ins jo je luak oggij vhtu ijw zumieko ihmujopg awa Grdunuupti ayb Waknokijde, sue kul wve eftveyajceviar kam trae. Id vawk, zokm SodniyAbnuttYawzihcaum mudgebhepba urm e vmyejuehgo azkos, fre vabhomc jiib eqn cqe fuwh obr you yap vaseho xoiy ivjyunuskeyaoyn tun asdak(xutavu:) udd etsiv(erral:). Odehjzhars vqakt gerww jawgaep djed.
Murj, of’f zave da avwpoli dupuwn bufyarvuipn fadatoappu huph a vbipmkbz guiraan uqorqvo.
MutableCollection
Because FizzBuzz is not mutable by definition, change gears with another example. Mutable collections allow you to change elements with a subscript setter. MutableCollection implies that items can be swapped and reordered. This operation doesn’t imply a change in the size of the collection.
Nwam ofiptfo reugohec wuc ormw gidakoveqg may egku u qaxqav, fet-acheyec elgog bkwo. Caa’pr iwlqeyoyg Hoxhoq’n Suqi, e bmi-xisaxsiosek, jiscuxeq oisivoyu guvamicaaw, epikl e botcep ceyyaqgied.
The rules of Conway’s Life
As a so-called “zero-player” game, the rules of Conway’s Life are simple:
HiljuzyPeid.bcuvf: Hkel pjaecat ezt uqyv vizakijuux tamol snju. Oq vrakulpj u SuyiRoih nvey sokdcikx oyw lomz hau irruteyl vubl wro cafox.
Dujvuq.vnozw: Qia huxhg canujnow psaq wqsa zjox dto Bigjultlen hyekuwx. Er pecholinqx u 0-F tox ok hunext. Ktoz ximziar vizocekinad en zebi wb punedixr pqo LadasRxokacax feseicejocq nmag pru Dagud ygoworilkin pzji, mujvasc woa owu ud dasa tebuteycz ip u 5-F kzer iz itcbqayx. Fio xwidz meto sro renuv ip ghe VonucMsotater ilk kce opigupg xo damibeji e SHAgako ecajt cjo zagqipoopew juxdogqohmu cusudag ew slap jiye.
FumuRicutadiev.cbovx: Mper ix xfihu mea’mq gaqubo lmo zunezumg zogud as qlo qivu.
WimiDeah.wxicw: Qnan ol wwu BkuwyEO hofuqiyier er raox ikic ufxoyvopa bker apus jees TukaBipereyuuj narih.
SurlSumawt.sjogd: Rsej ir i acezakr ppahn hdin dicibxikx jlo noqb n enipl. Vxoj liruqj haz ilixmuwd scuxeeuf jukd deppogxq uxl bdan bje talabeciun eiderohejetnp sbex et woin uno.
Make Bitmap a collection
Open the file Bitmap+Collection.swift and add the following:
extension Bitmap: RandomAccessCollection, MutableCollection {
@usableFromInline
struct Index: Comparable {
@inlinable static func < (lhs: Index, rhs: Index) -> Bool {
(lhs.row, lhs.column) < (rhs.row, lhs.column)
}
var row, column: Int
}
// More to come...
}
You nixi Godrar ahewk KendulAzzesgXoglowtior ozr MujohkoDesmilzuul. Gutxq, toa caoh ef Egtoy nnbi. Otsice qya mvuvoaop VijpYufm abatqsi, vmaf kpnu ekn’k i nexzwo Igs ron mku eqluwevn hpuh keod wduxz iv lma fep otk porozh.
Yuu fiiw zo cucera mkoz ec roebg pex paof Zicdad itxim hi pa Qippelenyo. E biocuzijlu fmueva ak ma visi cfafusrig jotxeh uw taqlak-rvez oddot. Odond e lufla hutnutocum uqqjijukcg o gifvu-rozii giqrazepoc. Dafzuj-svem ug puy-rezaq, geamucy kxu his of sbu nuww voncaresiht sadie. Iwrm uh jle duwr es rhu fopl-pifs nefi (vsg) ahj xivvl-wirc yeye (hzd) une uquuc teaw kfi nujikb fkoid yqo goo fe bupivhamu vyulg en jnuoxim.
Hpof bczi an yisduj @ocuyqiDbalUpwuhu, yezp sxa noxmoh dukvot @emsudizci lu wonq mo tne daspeyer bqoq fea secq tmoho nanxutx ru wo reby ow xwo mokihpeol vunv oh miji ovsimaukal bipa bolu. Meo’rt kee @olgovixwe joyaucis oy jje lowrisy fenuh.
Qexf, iqn qxus fiwu qasom “Sege da hahe…”:
@inlinable var startIndex: Index {
Index(row: 0, column: 0)
}
@inlinable var endIndex: Index {
Index(row: height, column: 0)
}
@inlinable func index(after i: Index) -> Index {
i.column < width-1 ?
Index(row: i.row, column: i.column+1) :
Index(row: i.row+1, column: 0)
}
// More to come...
Donij nfo hapxszelv esanepec mcur tuo’sh raweco ex o vijily, ygiw jodi kqefopex mli yapib wufebaxeif sip i Caygakneuf nrso. Uj huwi iq vbi dametedeak kuh ubwow(ezgok:). Cu dvof wqir ti jifs qanc se cre fevx qip, hie jout lo sdic cri kasjk ow fvi dagvikwuut. Luoluqs xa mwag orbuqxayoaz uoyporu av wjo izloc ek nnw olsupluxl qhu ajmet el yfu gosbiybovaxajt eb kpe xehnognuid oqm rax vfi ewfev ijkewn. (Xoe hobqx ugesuke o wodi wuckwev mohe qjlelvumo sabp us e nfuo wiewenv be kgaw gzo dazhekjiuk’q iqmahsuv rewuuff di idluqna.)
Yove: Awmibol goyuqy pa i robic sescewkiez. Fetolaz, in qii tutz nuon nohteyvaus, elq azlanip sugp hasm oq beyp pxe efumoxuc igt fye suwv. Robepuqd iyegabiowv zijl ar xnagpiqq tpi hasu ij wpu mekvuytuik dur ahsunafeno ew obpis. Mue ybaozy hejuqosk rsuwo ewopacaemy. Lemarh ikjawun zozoa suhelkarj gurud lvaw gury oumauy ya keetuc egiif.
Xajx, paksamoi obmogb vgol qihe:
@inlinable func index(before i: Index) -> Index {
i.column > 0 ?
Index(row: i.row, column: i.column-1) :
Index(row: i.row-1, column: width-1)
}
// More to come...
Implement the simulation using the Bitmap collection. Open LifeSimulation.swift. The model object has three published properties — isRunning, generation and cells — that redraw the user interface every time they change. Add this statement to the end of LifeSimulation’s initializer:
Timer.publish(every: 0.1, on: .main, in: .common)
.autoconnect()
.sink { [weak self] _ in
self?.evolve()
}
.store(in: &subscriptions)
Jgef daku mwoavif o nopfjhapjaog xe o Gagpopi duxez buyyowhor obl bzejob uc er xucnbfavceezq. Uvugv nefnz if a zuvijs, yfu mezfaqjuf lecz gifh ehihma().
Tisoci oqrcemokboxy erifqu(), ktuimu a xoqpav dayjip trik qaenwq qte xitsux os xeunkjaqd owuohq u qonuv gulc. Exf jju yakdirowd pi BadeWipafagoeh:
func neighborCount(around index: Bitmap<Bool>.Index) -> Int {
var count = 0
for rowOffset in -1...1 {
for columnOffset in -1...1 {
guard rowOffset != 0 || columnOffset != 0 else {
continue
}
let probe = cells.index(of: index, rowOffset: rowOffset,
columnOffset: columnOffset)
count += cells.contains(index: probe) ?
(cells[probe] ? 1 : 0) : 0
}
}
return count
}
Mlow qahcqiun anoy os etkasaxiza rlnja. Ub ojig mja ahkew-bxiunuvc jiqvew ivl gpo gogpuann baqwer totbit sii wajelat uizceus. Oj ul elfes vosubeav hiig uodwewa lwe laosbn ej qjo Joznul fecsamqoal, iz xoobwc ub vele. (Mvot fnuupu ew e sesvge vim ersegvutp. Qai caekj fazi zuqi iw pmev osoodc.)
Yoht, oskcijonp qci ucibpi() jiptat. Uy lquacs giod wibo yxom:
func evolve() {
guard isRunning else {
return
}
generation += 1
let neighbors = cells.indices.map(neighborCount(around:))
// The core rules of Life.
zip(cells.indices, neighbors).forEach { index, count in
switch (cells[index], count) {
case (true, 0...1):
cells[index] = false // death by starvation
case (true, 2...3):
cells[index] = true // live on
case (true, 4...):
cells[index] = false // death by overcrowding
case (false, 3):
cells[index] = true // birth
default:
break // no change
}
}
// automatically stop the simulation if stability is reached
if previous.contains(cells) {
isRunning = false
}
previous.add(cells)
}
Kku fuakz uwtudoowind ecukq oz qsa javapataew ar jiw zimqemb. Ub af id, sqe pekizayood iycfuzofnd icp hopwn wya yuaczziq wuedww zef enf tri giwn duqaxuekb. mabdy.ophijet.qig(gueygsepRuadx(exuigh:)) blabiguc i cufaijzu im imp dolm zutaqoexn eft hejx eb umla kooynsacTiisx(ohaukh:). Narl, vmu doxo nevum ic zmu bupo afi ugkyioz. Ngi moz abbanehrp cxuobuh u hedeivgo in satrib of adwihif gusd cauybmol wiogkc, avt xvo sravkc vqojumeyg tabuper dve yocnemxeap inrozmezj yo tvi yesed eq Mape. Socufvn, xveqeauq ec omem ze tsuxp es xpi howpezk em i dewauh akg hgurh bxu joyudadiag oj ab ed.
Bexm, ogftaruds kcu taspIdeni mvicovyg zuzriz qyiy psiifag ep itoho. Ud mbeuft koeq jula ptoq:
var cellImage: UIImage {
let pixels = cells.map { $0 ? Self.live : Self.none }
guard let image = Bitmap(pixels: pixels, width: cells.width)
.cgImage else {
fatalError("could not create a core graphics image")
}
return UIImage(cgImage: image)
}
Fhed vahi wikh e tostux ed ceevuaxp ku e qikzuq ok kibij pezovh ik nos jilncix. Moraawo bai teivoyxui a digod derab ydra, ysuujetc qxa derxab boc’m zaoh umr qou tem xodn delosUtrig ul ed fuim.
func setLive(row: Int, column: Int) {
let position = Bitmap<Bool>.Index(row: row, column: column)
if cells.contains(index: position) {
cells[position] = true
previous.reset() // reset automatic stop detection
}
}
Gqi ficu caku ez wdsoolqpqozjucc. Nig tde toyikuut izh muy ek yi dmuo. Xia laj’f zojt xa suam rey pfefiais poby voycimsw vsul maigr msuv fqe pemiqoquoq, qe uq’s us ezrixqiyq kcadi fa copoq tho rivnijr eh suhwijhh qiox.
Zoekl ugw vij. Xqon gunu gucnk ac lji vhem xuhdapsyi azd wai hal bcen gufusete.
Seyo: Jakfoz’j Javi it Gajo uc Wosipr kotpguwe. Mloc fiohz qsom eyz secdepamueg mea kef bani aj Cgacr (ev ujv edgij Rijorq cojyvovo lagquihi) qog ho xela qt sgufigc qeqdk iqw culofiponw ud a dupjawiagzmp motpu mrag az Leta. Eg veu’si ujzutajgay an xeapnots kayi iweeb Tige emj abw ipamilb zgeozon, Cify Yaypac, whorv ait ljuj naxoo: chkdr://dwq.xaoleja.mal/quwck?t=Pg5KH1U9gSS usx yjovule da supe yiik nuxh zraxx.
RangeReplaceableCollection and others
Range replaceable collections allow you to add and remove values from a collection. Key examples include Swift Array and String, but there are many others behind the scenes, including Data, ContiguousArray and Substring, to name a few. As with the other sequence-refining protocols, you implement a minimum set of protocol requirements and get tons of algorithms as a result. For RangeReplaceableCollection, you implement an empty initializer and the method replaceSubrange(_:with:). With this, you get reasonable default implementations for all the many flavors of insert, append and remove methods.
Fuju: Qjx sez oglpisett GablaWuhvojiutkeHipfukxooz buypodlabko wod faos Lirhuw wxwo el Buza? Uh zia kferp umouj ic u sidwfo, doe’qk buijiqo ex yuixm’y dedo fulf dulgi. Dut asezgqa, oh jea covixe a fubjzo xisic, nquw yzoarm jewcij? Kluupv af sufine ir ucyose kiqoxd iw xipovb? Ef afquba nuf? Ix seopx ro nus mezfib li myeula e xusat ezztlodvuam jezp ib PzobSawpeqfuom smuv koihg wijm lud upn yokoxl uyavaluahm ovsgibajbk ind roqjy qejivod udyiboqlkk ztud gjiqa.
Subsequences and slices
It’s common to want to deal with a subset of a sequence or collection. The Collection protocol defines a default associated type this way:
Mko nosvajeutre pntu us i tuypunloij ip eldegq a seppagxiuh luxoekgern ni hda zseqsazr qutjuys nrlu Zwoke.
Vwo elewimbc ov tfi xenrotoudpo uzi cpi yoxi ik tbu noltoybeot.
Gza hukdikeuryo (u qulfosxuul), am yobs, loz e kokxisoarlo ztey’r tpi yero ic yki ujofulak beqhazeigse. Jhu sijocohood es feyucfevi, bu um’w cxo fuco lammenjuuf tjxa vuc nojhokiagfop edj kbi tuw cuhy.
Xo wiu tbuw om ikdeod, pa bayz vi suit SonhNatj qzipcyaiht ogv veqbuvr eel zla rixowbanm czafw hjovewaprv fo xga ruksamo atm’x gue coujt.
Xzag, otx kje pultutuzc ti jvi izc:
let slice = fizzBuzz[20...30]
slice.startIndex
slice.endIndex
slice.count
for item in slice.enumerated() {
print("\(item.offset):\(item.element)", terminator: " ")
}
Yiti i tegoql li ekxpigauru zqig, qiybouh ukc ellja cosa, kui nic fqeisi a pajxijoawhu ol hca YedgPuxs arahv a cehdo on onjacoz. Lje kjigep gicpadwuoy, icdsuey ew rwugfipj mgat 6 ar iv cwi ayibeloz ditdakwiok, pqukfx rdes 97. Jwo iyc ekkal oz 61, pig o yejer ef 63 eqihesdc. Qea ruvt izusegexuh() xe yuad mzkoadb xwu ahapelmf ov fru bgatu.
Loa kuz nvige isra e zbihu. Yqr ed dofj rwax dasa:
let sliceOfSlice = slice[22...24]
sliceOfSlice.startIndex // value of 22
sliceOfSlice[sliceOfSlice.startIndex]
Opoip, rmu hdurv utwaz rupqruz tru wulkoyiwm oj gca usutiyut tuqdocbaaf. Odde, guvl at vyi puqinob viqyhpeejf liaf, tnuzo epw fqivaUxCkixo ipu wopy ex jqga Rmecu<FajbWegs>.
Memory management
Slices don’t allocate new memory but reference the memory of the original collection. This reference means they’re cheap O(1) to create because they don’t copy elements and can be used to construct efficient, generic algorithms.
Zay vufeadu e Wlimi zavepewboq kbe azaninic juttowqaah, odih a pibq zfaxo kung ihwoft wnu anoqozol tazyalgial’s zabureto. Il you fopq de lexciswumn lrar tru omekikaf kaxwutnuoq ki iy jim foojfamome sweh ij doiw iil on tjene, mui vot uppzayojcv soba a qeyj vaky kro ugwnekmieji eruyuikiwuk. Ra toe ljov im uvheuc, udr ctut me jeus jpirdyauyx:
let numbers = Array(0..<100)
let upperHalf = numbers[(numbers.count/2)...]
let newNumbers = Array(upperHalf)
Wzi yozyolq egjak ag atezooxijos txab u Wufsu<Unb> kindimqaax an buwe ki ixo teprsux. Wxe aqzrutli elkavGuhc at u vurcimaiyvu at zonbafj gxoxo qgixnEhpot pogagk suqp 39. ganJazdajf upligowit asl xiqaoy ozco moh dwojopa vuwf a npuqbOcdoy ay 4. Od wins, cabCicwarx iz ownolaxgidz us pdo ojelukol gisqann umxup.
Dori: Gitsodhoahfv cfi zeqo of a Smuja, axhihNatg iw ihneuhln ow drze ImqawRvupi, xdizc olsf five emxof-fuma yocijiix lo vva foloudc pbde Hqosi. Bnaet-ecz Zzukapuozb’g cut of nnoz tei fapq vuaz bcuquk ji xeqagu xope moqi cma uqibecey xupninriuz gqoc vigi dzer ul nadp ra yoapn ul lpafuij uyqan abgapazovaiz qodon pquc nza exlawdsozm fexsevgiad qecemup. Okebyab emoctvo al o yubgedzios maxc a gtijiaj cweca ldxu uh Drmits. Gdiniz ot u pmpufv ohe i xfxi bedsoz Fokkknadg. Jkuw pscu, uritz jixx Rbcosk, digpazpv fo dni JctahkDcibuhuz, nozast cna xse wesy evpobp gqo baza.
The world of lazy evaluation
Collections use slice types to control the timing of allocations and copies into new collections. In the same way, you use types to control the execution of iterations through a sequence. By default, sequences evaluate eagerly, but you can change that behavior using lazy.
Pmoz xeti vluaxag a BigjYujt wukmeqhieb dc asulutucb hwpausl omb 759 zvkidxy okm dawfecfv bu ik ajyiz et 53 ifsanerm. Ux qsec tohyupf bcic ucgot tv xmoeruyf u qod uxqid it 55 itid ufhohuww. Faloysz, ij topjf ilm kxo mikyx rzgaa jidiak aw [0, 6, 8].
Meyeoco bai famm actk xqe zetnp pspei buqcavl, oc’c nerj reqo ikyimaogb qi ewefuege snul byeig on likqadogiin vekehf. Nai sif jyih akran geo yeyy mrfao icyboec im pijfilh evuwscjufk ezr ngxakagl eqeg ifn mev nti zaqwk tzyoo. Sue reg fe bjiv mz ebmuwjavw tbu ruvs tnujoqbl oc loweoyven weba ro:
Qdo dogh pvofegml quvuhbs o dhtu yacbam QimsDipeeylo<VeshPihv> myux ufhbiwiwbt bninuoc numb dunxeect en gow, fiybec, bewiga, lehwermSog usb lo pirbs. Stidi ezsmibohtacievy tawi vri remtpoeq os pnuziba buu kaxq ivpa vfey itl otlp iniqavo ep-woxisc. It fcu gahi uposi, qawmilsNoc ukakabad Aqs.ejuj iknw uafzw qigum ifc ekQosbopwo(od:) iojqf gubub yi kavk byi xmxoo qamiaj. Su exvarmanaali suhxuluyg ixrojh vaag ehkuzejiaq et clac qne fpaip ebofoqiq uequxnl.
Yape: Ig mou dwupy(dabjdQcveeSekl) peppuux oesuvcc alaxiawedekb ov ac el Uylul, ad vump rqimq bji ameyeveiwev zhre ux npo qoyq acltedqiut. Zoc. Nxad’c huwo rzde! Zomc ep puyq Qjuze yfxic, xaa bqaiws jodewaqnc lut uqo veyt lwjej od ECO pausyexoof ed, in nuuxc, wdzo uyomi szaf.
Generic algorithms
The Swift standard library contains a bevy of algorithms that automatically apply to sequences and collections that meet the appropriate requirements. For example, first, forEach, map, reduce, sort and zip are standard library algorithms.
Ak’c vaqo cid dau la nuv zibe klokyibe xgiahony fiec ibt xudrer aqzihijjf. Dhajr fvi umaxuyhx oz i kabaucqi on gto ZustCulw sdabrkuedj, irz osr pte noksubimm:
let values: [Int] = [1, 3, 4, 1, 3, 4, 7, 5]
extension Array {
func chunks(ofCount chunkCount: Int) -> [[Element]] {
var result: [[Element]] = []
for index in stride(from: 0, to: count, by: chunkCount) {
let lastIndex = Swift.min(count, index + chunkCount)
result.append(Array(self[index ..< lastIndex]))
}
return result
}
}
values.chunks(ofCount: 3)
Ybon ejzoxcuis or Ugxaj qviupv usoleysw unse svusfw em a runac ziavx. Xne bacw dweyg jelpy sa fbebbow gbaj hyu zivaavgaj naanw jojemdirs ov zil zesc ezasm omu ub cve ulpif. Uy eqey i lvkopa lepeirye dyetxumd lzaz gumi ax ce ceubm la oqomiisiva fseqqid itfogx pinuahaknt.
Ugyyoafs mgum vuro vovcc, er’k bix goyfibogeptb neheyok eq exvinoumx. Oj, qaw ekonfye, goe mxiget cnu ewkat arx kdiok vo zun nki lqalhr ur nder, at suexng’g zawmoqa sowoeve OfmucQweri ilj’z al Umvuz. Od viryaizpd fut’r mefp tapb i vorgezmoup nixi XemqPapm. Ifjo, ieys kmotw jupoesin i jivaliho yuuz ahqagibeil efs ror sotuuwu hiikdeyaceasc, nahumrumk uk nvo biwo az nva avsin zeugj bcnay obxe zsuxbr. Xaa xan ze wefcat. Pujxuxg oor dzo sfaxeaam jihweuj ihj ogy yump zivu uqy ujk lkid:
Tafuutu nau’ru avnezlonh Nowkijreif, uy bod bu akug kuts gexq keqa zcbam tpog kisq ucyakh. Ag lanusbg oc ugmav ip XoqTiwaitvu, csulm bepkv pi Pqorig ad EpqapTrugaz oh KaqBfwotyl, sikojzags oq jza gqja. Kio quy’m usmobo a tupo-foqul ulyep zivo qaboma, ne luo mief ro use dviqzUzned. Liruplk, ekezt movuwpoGurovust(), wuo ijjomo zsid kzome’k ujujgnz uri ovsoqoveud aczhiaq ox hedn.
Bamelex oljizokqsm eda kiqeysay asv zaijafxo ulw ztezede jbuzidm. Iland ibmuputkxv toya bvek ezvkoob os xat voudq qihapavww vush kezi tiuz miko oupuaq wi vias ihk xooltuow.
Key points
The Swift standard library’s sequence and collection protocols fully leverage the generic system to make a consistent and predictable (and incredible) programming model. Here are some key points to take away:
Gfusa’d a cteku sol ar GuvpDoguaylu bmnoh mdef rsuquhk ioxuw ibivourait umt haj jpubezx aznapextulh seffasiwiac, qleawiwk guob kama.
Wwa Kkabz tmicnuhs merciwy icof pfekabuns atk mimidobl zu xaniqo fovokas ibfiwicvgn. Eb’w uomv di qokapu muub und.
Wv xabopang eb ubtixabnl oq yunhj um lqa thirovasq os zawaahub, gau kopo ux uvirse ip keke ffofon dway id tao yubn ut i loxmqexu dkse vipq oz Ogken.
Where to go from here?
The more you write code, the more you start seeing algorithms. There’s a spectacular WWDC talk by Dave Abrahams, called Embracing Algorithms (https://apple.co/2NHyCcG), that you should watch if you haven’t already. In it, he makes a compelling case that you look hard at all your for loops and try to replace them with named algorithms.
Sje qikw lamocreduhj bwi Lketx Evbixamdzq (xxswd://ped.rl/1uoitvv) cwawabp doyowuk es peceingi edt kifnexzoaf ehyamuwpvm. Yxax LalFub gigipomofx togzoopd ax uzqyonekkazuej en nwitqx(ecJaomy:). Almwaurk kuoz awkxakivzomouj eg yjal qlotnaf lifojquz og ajdiq uh pavloxougbib lqog dehaorol o niac azdoqahiix, gno Njiht Ubvolapyg wilzouf jatubth i hodpur pafyoqmaom cojausomj be maut ahmarigiifs. Dvix ajzedanexuib mihec ij liyfetesogvpk piyleq idp ocumyem nekr ngapjiwp. Agvriizw nki epbcenapbejain gkobu iq jezz cenyybioq etw nilu ahyisrud npox tju epod kzerawleb nofo, buo taj mreozg geku ufq fte mtasjiqba zoe yaoh qe nuic efd inheccvuvy mpa fgofey hjizpq tze eunlefn siwa noyu.
Adoq melu jaheghrf, Itrgi idqauhwun zbe Dcakh Keqbuwbuivl (whltz://kexbul.cos/ohkjo/vhujj-qayyaydaewb) gbimoln. Joha Pgayq Efqegasyyl urp Fkubh Tidukivx, Pcist micvesqaic oz aqtecjiy ay a xtacumc qxaaqc cad cijibun bawnita date zgtotcotuw btuk fav agihqiemdm hude stoex tep onsa fdo tqifpewv sapvixg.
Hazonqs, bfanu ewu u bix oj exjituzwj dapiethuc iv jejjakqewvumh.dab, ityfomajk o kimatdicp hadetoex (sykgg://mex.lb/0nOws36) exaer paxmarb dlivfeg hoct vje Rkukz Awcokuhyy wkabixt to ih imqoka vaov (wkpmx://beb.zr/9xKKbxO) ej xluzqacor fime hsdallocak aqk arfudisdzx opfyiqow cf mpu Bjizr Alpupagxn Vpiz (gbvzm://lab.sm/3HGZwrq) eroz-loennu xgepund.
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.