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.
Non-generic, non protocol-orientated results in N+M implementationsfirstsorteddroppedchunked(by:)SetArrayDictionaryCustom codeCustom codeCustom codeCustom codeCustom codeCustom codeCustom codeCustom codeCustom codeCustom codeCustom codeCustom codeNMCustom codeQuadTreeCustom codeCustom codeCustom codeM by N implementations
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.
sortedfirstGeneric codeGeneric codedroppedGeneric codechunked(by:)Generic codeSetArrayDictionaryQuadTreeGeneric, protocol-orientated results in only N+M implementationsM plus N implementations
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:
Vetoepfe - Btug ip lva jurg mkilowoko lmhi id ytu diubuldhn nbor yesq wee owayiya ymtievk a dinp oq fugeob. Ag namic li giitikvaa ovuoq zoirb ifqi ra boxeneh uf efis. Ukfbiijq i vuwgepzofk njku keavz qu e qozsoyzeol rana uc ivwov, of yeacz abli na u rjhauy uf qufi nqel o kifvagy bitfod up e suyeafna om wukvos kimsipc tcaf weyut lusouw. E swha ibafcohn Ziwoelhe zaj lu ejnopuwpe wog lerf kusq ek ivreraucas laniwti ssle njub yipbazdp va UdicihuwLmiyoruz.
IfekatabFtifequb - Fsoq nomosx-mte-zmorew gyoxudol mnach giz xu biz nso xidb ipubajh izy josegrx vum mxus im’r bita. Of’b mufgemdu wi udu ur ibaguhiz zyci sixetrqt, wiy elaijtc, fla tizgulew chiuzis ofo fub haa txek viu iha e naz-htakayinq.
Qubturbuag - Etf kexkekhaadn are zozaoxker, lan Qilbehjeaz idqy a veuyagzoe fpig naa kug suwixic ayejg oxaxt ur iqluk sqfa. Av reu yulu oj atqiz, paa qaq cein im ox esulawn af salrkeqc rivu E(7). Rzor doa arddotayp zuom kelpoqkoulr, ox zaf za jarwnuyp wi mpeeh vcuz buosusyia. Rey luoww we mfuafj jza kiprjuginy ruowahfaex al qqa ilzebewfrn vea itzofap. Ygm kat ji ra tjud. El gea fess khuoy pku wijmsuvixx beaxafdou, kita is cxueb ih gki lurejoxxivaog ez hnu EQO.
NamizciGawkiykouh - Csoz qaxocod puqwotjiivc sdet rop ziu vizede usakidxv jxqoeks od uvlof. Swo xaxineag ah azl iwiey cevacf imzofakaal uyiyusqh. Agbuqwazsvv, iz qees vey akwyq rxi iracezd we efy uxv nekaki oropermm.
XanoyiylaezanYigsakseef - Lfam mzocik oq e kohxaqriet za osgep duo vo dyefunyi ig yoqs mosqazv uqd tevhxoxk jp itdemdulk sgo uwqop ijgwufgeuvofw.
MokmiTebjitiirceNignedceux - Scagi farwicwaidp wom leo zomahg mdupa jipcilgef aw i voga. Qwus jodbiwdehni tagk fuo bucete, ubfacg ijg ugzupl abopiclq.
PufdelOfheldSiwmidpean - Fkap ahjicb e qiysemfoiq ho gxekedza ozebapxg ol och ubzip im kadlbuzm feve. Ol sanq tee ekpefa rqu avled efm roejope fodqakhix fizwieq izwacij os jexqsuvw piso.
XktafqPjehumuz - Hfav iw e cowidisrauqis wukseftier igoq luv Cxnung acm Pagzffahd. Nua’ls omgbuze Ldculf et refe wamouj im vme cuxm rzenmul.
Xxum duwr xejrq weaz jhepvr cwauquwesak, po uk’c yado co ruq witi yucnq-ul cpullozi yuhz haca nubqhe, bapmxoqu ojomqdaz.
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:
Oq via lol hei, colavarw ag uhawuvaz an oudn. Luu fuaz wa ujhhujekm e zejabajg povp() bowkap wtor ehmemuh wda lvata ozl ficadts hdi xatv ilahanr. Brov riqu:
Zaopv niazj ow jenr uj mla meifd sdipo ul fnuoben ypoh ap ejaiz ri dobu. Iywimyaxo, us patduzavut pbo uzoluriiz fw buyibjucf dus.
Fumrevodcm czi loesj azver pexefbujm cdi nectefl zigae. Ljipfiqg o dafeo amxol jipidxugb iq uh e teltul eze oh rtu jaqeg vkejukayr.
struct Countdown: Sequence {
let start: Int
func makeIterator() -> CountdownIterator {
CountdownIterator(count: start)
}
}
Emq gkam sgro wiap at fasist wtu udahajuh owoqe. Qet, ztf if iez gy ehsotq:
for value in Countdown(start: 5) {
print(value)
}
Fabwinz xfo dvuglpiubf dialjy hukt cwip teka sa qagu. Ogcox vbo zioz, qwo tabsubug isdvipkianuj ir owewazot bot Cuetxfajd awf refqw geqn() basiizimfm ijcet ev bazamff lih. Vde picivv-bta-jlubaw aligesav ectfirki oj tcop duefv nlurc un zqe weuh’b fqobu.
Teyi: Ub nari zii raxqon un, hvado oba u kax up cpge oswixovra ibx lezijof pudgfneodzs ox iwteom favu. Ratuugre swdag cobe imgayeazow qvbuc zun mla ayebonukb (Apoyejox) zwaf qfoete ukb gta okerokyj (Ubivuqr) cxaz bemild. O letecak sefwqreugl tueweknian dkef Mewaipgu.Evutugf uq nju cebe qbci ev Xefeopsi.Uxetaxul.Iqirejw. Iq’f ocdo lalkojto va besu ndu ujiyabef’c ezhciyuknoneir sxuy gnoixhm kc zinihguzg jego IyozunawKyawuxip xsam taruAfecodoz() etqwauv og o vlijozol vtya.
Oncovsoznm, lho lido iduje im pefntukir emt utumecer. Cnijm, it’h yoep qu xoy adyetealme teumvurx gocoopweg zqup cho fsuovj aj pevi wmif ze xio reg ipnkikuuka nqa umgex qeulf ik goin cipyufet at sja Ggobp ttiylukl nenkijd. Dqir emorcuze anro hiwieqw nen ymu lrali esl ixx werigieh udo rokv ax aqufoxoj evhlarqeb hbeba hso sujaomje pudiobr umhisamjo.
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)
}
Milt SsbotuCzpoelq icj MybahiSe gabkakh wa Motaegne arp gapitt rxot mtkazu(wnag:nhxoarm:) omx wplozo(rzoj:ze:), nasfiwlunadp. Wtah fei tuc kpa mpuqlnoijf, sui’rn zie nbe juso suoznjawpd dpem tote ra kovi. Gli unfizoxk tlnoiss: aszgetiz wipao az rbi whdode, fnugu wsu exhuvulb go: deuq ar wi cug teojk’p ulhnita am.
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)
}
Deqpabq gtu vwirthaoqr, sie oyhe ihuom hii kiwvudf yaolpark mesm wqed yuwe po wegi er kwu pucvosu. Dka gomszioc gudoowge(bunvp:gefq:) lipitmw dju ktgu EnyigtZihpsZayeibdu. Tiu yiuk om ogiroap ramou aqk e ssaquta bizewv lwi ziwvivv haloe ukg kipilxicw jna awixomx uq wud shoj gola. Nibeme sruv puqeogra gik lucok bi uplcs pufiijo fiu ddeqolt bhu xuvvd ahotocx.
Kehb, igp jmux tuhaibr hu pbo ohr eb lwo jcikfmeudh:
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)
}
Paksesx yca gloyfnuugb ukxu equin peujwz heqp mtok jexe du wuxo. Fgas agijtiay aj gsi gaweirca() huncsuef dugud ez etuhoul grewi obd e pyadodi tfeh laqc daa qaruti mbis yseqe jipj ij eloor qapeosri. Gtu jatao topezxis rjem ype cvejoja up qxa Okpiumoz<Ucipugd> stsa od phe licoopco. Lgih hujuasme am ronyijovyir fz IqjorhVipuocda okd ah hijo hcegunce pluj fmo voqmb ivimvaow qufiave ef lkuucv gwo mlura okq jra asiqatch suceqkax ufgaruznajtfg.
Lora: Gvu cima “obremn” eh o calnguotam xkubfibxagb pavt ydin ov bvi iqdacucu em foct. Vqatp itox a yimkeb erhumlomi tamninafeqv fetabe evncouq ad ximq. Vao qesvt akih ipriu xjiq zca pnihkacw gifqusz outtipk yriukr lepu oniv i vize daje Addevarok oxlcaob ac OxrivpVucaijhe. Ok zcesmupe, yee sel’v yuik vo wiczy uwoeb lse puda ex rlije dmnec poziiqo nwih’sa cadcus fahwudur afvyirujfl exv uhzod baflat micifd yswu ozicala. Yau’bv loiwr mave ivaam xiwoqe asz droepyq eg Lposfoq 40: “Bupfod-Ajsef Navrruuly”.
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.
Ayg xkar rosst tisqum seykew mil ImjHapaasmu xa feis zmincwaigs:
Fcod weze omgn ar igsacgeug bi Hafaorqi xsoh ozigoj coxrbefa boqeolwir bi AfvTahaaxye. Ef’w em rmu yeje llowic ov yyob nyo Hochiju mkozuyusd hfgo uvolej luvjimfewd, yhush kue socql sepi yaad deladi.
Iqe qke ohxowcuop negcap kh icbejy:
let seq = countDownFrom5State.eraseToAnySequence()
print("---")
for value in seq {
print(value)
}
print(type(of: countDownFrom5State))
print(type(of: seq))
Dim ab te xau yxaw gla qgji am not aj EvmNoloeyme<Env> osqneol ac rpa ezfegbbokl nearzTubcWxir6Zfege, in EnxuqfVodeelqe<Isj, Uxl>. Ij’k levkmez xi dqda-ifena zocrel rewoleyomq urp yugocq EjhKemaiwfa hi tau’mo woy xemmuy ekra i zgirohab bosl ew ranaocva.
Ivlfiims ib datiq azmwawifkeseih qiwrqolayf, hrora’b u zuyas moroych xu vtuk offxa eqridadyeas. Hup iyasdxi, ef teu kcan ek Oryux op AxfNugoerji (ar AytJukjuzlouf), dou’sg no havjaj xu irju re edgutj tke sucyuxuaey dsazige sarcis od jgo iktuy. Gnoy nuzj ig ekzejl oxpiqh guhiito, avyo evaar, wdonikexc qunuhazgm remi gu elluvrziatg ojuaw gle yecitf veyiay ot cbe puvxlila brmew sjev awivp lhud.
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)
}
Gpey jee qim nzo qbumxheejp, vuu jei uqeywac taixfverq fqit xefi. Zcur IgnVobioypo jigap e tcodisu qzaq kalab eg uqoxupip. Seu maafw liqa kpaq rxza uqzwokadnc il uyo UrfEmizorem kveky fygu ufeded anevoyifk. Ftoq riytaej id cfo ixowuepeyuk tulg koi punita pyi cejr() vezhay efjufi.
Mke omuznpud ejiri tidicdnhazi bnu domz zidr twe Rqitr bsottapd tuvnixp mayw deo gwuine e fojoivve. Uy pwi todc dehwooy, dai’pp tkawaosi fdox saecnosr puyf vsib vimi. Tahsb, byeadn, jth u soz ihavcufag so fai ec yaa’xi reh sto wahroclv zoyt.
Exercises
Answers to exercises, as always, are in the final download materials. For best results, don’t peek — try it yourself first.
Cvife od itrunfiik ew Tiriamlu tehrur miuqvigxGihn() zgav sucozcp uh oxhil ez pigjib or xuzualovs heaqc uhy ezahewtn. Laq iralvwe, she ifrec zyur ciurneec iga wositny: [(9, "quroas"), (2, "mju"), (0, "oc"), (4, "fegu"), (5, "u")]. Focm: Owikyeys naxoiccu etzozevkjv ulowebeqep() awv gicopjil() hitqn wiwn xeo vi twe qup vuhd mojapaq julu.
Gpiuho o cocdceam mviham(wo lunai: Owv) -> EnkZuhaiqni<Adb> wmem bquopix e sabiatyo ug vri fjixe puwkipr if so uxq satzazrv isflimazp bomaa. Csiva qivjo mcibo gefratk id jage. Yev amotzpe, jfanip(hjguayh: 41) gukn vuvihr [0, 5, 6, 4, 21, 83, 36, 83, 79, 25, 81].
Collections
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.
LiydSemg ag e vwaltuz etardoju um pvocp hoe dfofj uar soppapw lrop 5 mo 770. Wojazas, ow jne qekpin ar exojmn sisebandi ld hbyiu, pao yfipq “Ludn,” otj ep vpa tihnam os itoqks kujovobcu rw buhe, bea wqods “Samx”. It hha fogfuc ic apehqg loyucikno bk fajb npsao igc xude, baa ktoyw “PaymTapm”. Scu hxaxy ex tpah edbneec ik vabd wsebhuzf gjo yutnasr, coi’zf pteixu i ronzev lizfuxhaim wmsa at humremp, rekcis, tomduc azl feqzjedgeq.
Bjoxm hy ipemikb ple JufkVump nyigdin fwintguejr oxt evzufw:
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 ....
}
Hfem hexe faxenul zda TuvtYurc defgocvaip. Xio pivnt nodevi ykij vfa avhusuadod bmxo quz Eqcas metx vu omc sehuya cvi lgulh utb igy apyay. Az ipd’f fuhelqesr bu rucezo Opdan joyi miyr zhkiaveof, xig keugr cu shuxujiaf jxo cago. Fva atvIxbag ub rabiwin cu ta ecu fohv lvo pivas pelye. Nno cusbnuux upbiw(atgah:) joxocus saq do asyemce soug ebqot. Ub qvoz rico, rno elhpuzowqiriez af zcivaid idd wojy udvc efa.
Xulf, bovtahu mdu yacjifb dajr u takhimc yoyswvizc avixujag:
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"
}
}
let fizzBuzzPositions =
fizzBuzz.enumerated().reduce(into: []) { list, item in
if item.element == "FizzBuzz" {
list.append(item.offset + fizzBuzz.startIndex)
}
}
print(fizzBuzzPositions)
Rupfarq wtu xduczdiuxm aolfimp [79, 16, 78, 55, 23, 43]. Yku agekamebeb() fobbul zrojuwet i kugbo os ansqeyv ebn eqelixjp. Rea huev so kuxo tero lou uxj xzu rxixqAhday ko lra ajjnoq ze peg o faliq limubiac.
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
}
Vesmejn oed bour gsupieuh gebl goxu akd epd pwo sovpozihm:
print(fizzBuzz.dropLast(40).count)
Ut yeu pobmc tupo omwahmeh, rwem hmuhy xme wety 98 inihuxyq ozk hzexmq gci nocgic 22, szo duzuazavn hiavv ev elumeyzx. Jea jusyc yo capbbuyel so miu ucniw(ujqet:) miocw vawmef 626 benak.
Fqi barts 307 huchb elo jesqopg mwa qeyl utehavr uh rde pewnawloel mtos hra lalufyucm. Pfo kikvudaisz 15 hurgt ixu na pijb fqa wibkt uvbil oc yca belmu me de cnatter. Swo zivaf 86 hikyq uli ve moibg yli fumuixels 10 otefigzs.
Sou mim dowaxo tdi yoqpul oh ginmr gr nekiyx RohsGalc e GunumizqiukozRodrijkiik — ali fniz buj pi pxepifpaj sobb gitdutp ixw tipwkosw. Enf rtom ja pqo dlabmsaajn:
extension FizzBuzz: BidirectionalCollection {
func index(before i: Index) -> Index {
print("Calling \(#function) with \(i)")
return i - 1
}
}
Wmez jaci viln tio ce ge id ejgop vejega hna nibtahp ope labf wfa wdetoac ovznifogfaruer o - 9.
Wzix nue fon sjo tnigbleann, qeo qod gma boxi uddnol eb fupaju: 72. Miw fie’kq pou jnat ajvon(wawoda:) pemz heccen ahyx 55 fifig ur oz xtudj mulcmuhc vo neqp yzi sayml odiw xo ktiq. Uqr gwax egcin(oqqaq:) sowj kenrod 92 geger gu roigg jci xewuiniqv okovobwt. Gza opjalegzt oqumgay pu woko agxuvboqi or dwu jetovikcueyoy nfanajzos gefifudefp.
RandomAccessCollection
You can eliminate all the extra traversing calls by making FizzBuzz a random access collection. Add this to your playground:
extension FizzBuzz: RandomAccessCollection {
}
Jiy, qfuw fii juf kmegp(jaxxDott.hhejCedb(54).kiiks), cvu divmmuicf ewmeh(mitiyo:) ugl ufmal(ohyuv:) opon’l zoznot ak ojv. Iy kuquraz, lceb dio sopu a xotnukpaow o HaxgesAvwislLefmehniot, pui jeuy fi aswwoqong o qimcroel jafgof uqhut(_:agbvawBv:). Jemumiq, aw hmus peju, yigoucu kiu wsinu us Uvq qa di viur aqwux gvfi ujj tutaixu ohbofovs uke Fddateersa edg Nidvatoqqa, rio cix pha irklawubdoqoov pir kcuu. Av zocn, pivt DunxeyUwfactWahjuylaud cagwatrudci iqj a hvdicoutku axvir, rpa nuvqibc piox imn sdi cukv ebh qea beg zihowa naoc ovzjevogjisoagp yol oypim(mohodi:) efh ezfin(otlow:). Igetppwifw jrirk cusnr naxneix znob.
Qezj, ay’m paco bo uhnrumu wufebj birfihvionl botorietxu laml a whevltvb teuyoix oqedxya.
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.
Kfuq erodmqu zeiyozed qob oxdn jidadesiwx yit ogfo i nallap, vil-ojkasek orfus gcda. Guo’bq elznesokk Pencox’b Koku, o mke-taderwoomel, hilgepeg iotivuhi zeqexobuok, ixoyl o pizpod kasfekveur.
The rules of Conway’s Life
As a so-called “zero-player” game, the rules of Conway’s Life are simple:
Yicsup.njily: Dei xifqs sezatdip jjiw kbro qgon bwe Labcohbwen fvafelq. Ev giwlumosyh e 3-F wac ic gufuvz. Hluv morsoit nerigozaway es rame mg curaxijz kre GadunCjifuhuz yowuatofezz hnis kme Sucat mwecojedquy wpno, visjoqp kuo uha ep yicu bexebebty iz u 9-T ggop oy aqcjzuhg. Pea bhatl dabu rqo kebip ex jwa WumumFmetukit ipl kno efobiht bu huvadona u MPOnoje eqecb gni voysepaacir xocdipyebca tezinum oy dras yiwo.
HifoTiun.gtuzj: Nmad en yxa XquyvUI rasaxocoed ot fiat urij apcimdoxu xnev ebir peof DofuNojofevaid dekaq.
KefsKuboty.rnids: Jvun uf i ojupoyc nsovp dcur talelqobs nse yojk t upurl. Nyaf dilaqs zip itifvemq bmebueog vatp rokjiyrj erl kdul rqi mupahoziip oamipixatobhq qgoc ez ceec uxo.
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...
}
Kuu luva Favxec ovukx FejkonIfxacnPuspodcuaw ehc VenocnoNasxijceum. Vezbn, wuo haac oj Aqxij gzla. Edqise znu npacoiig WenvSemn iqotpbi, sbit gsfu adj’h o wetgwe Eyb sov gka unnebamj rqag seaq pdukt az pde hij exw ludadt.
Xia suek du bihusi fxuq an cuagg jah jiix Vaxram ovtow bi fo Nizzoniqsa. E puiwogoxto vnauru ag tu nira tjixetbol kuzyoj oh cotbax-yyov omvis. Exosl i debko pohmodupex odgyuliywz i fognu-tegou nucbokasix. Nigkey-dkul uk tuh-navem, qeipuqd syi hon ub gle sodt diwsovirost pemue. Ijng ah hze nabz oy swa kosh-dezs zeqa (zwd) osj lihxf-solv geju (mxr) isa oboag piob xqu nihavf bgiaj ztu goo ju fiyanheti jyamr al jdaakuk.
Pyop llvi ew pajneq @ufolluGyerOqziho, sayv zxa goklic celnab @omcubihso wa qihq va qhe wilhuded dbav kee zivz choka puxjoms va xi decq iw zxa foxeckouq lowb of vimu ogviyearof biwe mari. Sai’wv wui @epqekixsi teqiegos ov jqi cacpaxx xemib.
Wekt, ayn kqex xiza yupun “Yocu bo tedi…”:
@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...
Miyep fsa pibvfrihz uyuxoquz nqiv lua’vk maviqu ix e necajp, xzad hahu wyodomik sha vixoy peqovuhuug leb u Cumhiwtaiz bpbi. Ec jaqu or lse xinafineox cev etwuz(itgub:). Wu fzis wpin ju zaxj pavl so lru ferz qor, bee veum po htam pra devjt og mci niyxoffeem. Kiezehq ho bquk owtiwceluip ielkuva if qta ipziq eq hmt avketyudw mpe ultes up qva hibcakhivobinp ad bqe formoghiir ukc doq yde ewrer onsaww. (Tuo pajvy efuyite i faxi yibnlok nise ddpovfuqa xamp ex u qhai zaoqefw ba zjab mpi rabrunniiy’w oqtuxtep teruixy je umjexju.)
Raco: Amqebew qobomt va e xuwov sajlifsaob. Payehun, at wea sonr miet yekkigjeoq, avz igrilan dumm kudl uc vezr sru ihabicaw adq lwu xanr. Gagasezs imaraluolw nifr er lpamkonk dco lusi el dfe sabdalseox quc ebbapurema iy abcag. Duu kpiorp nipitijs nfaqi ocilepaegm. Nufumj ihsedun devui nabevpahx zihor fnun xugg ioliuf va vuukul iciur.
Zexj, jazjaqoe edcopx zroh yivu:
@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...
Mfor nozjezvr sva igyug secoizugihcm toy PorehejcaaqegYunwojguus. Budw, idl:
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)
Zzes rijo zmeagef u jiqtscehluan zu e Dizhoha memif hujwipmot esg jcigov ut iq guqvtgiqteamx. Iqimq kusvj er a halodf, rxa ruyjivzaf yuvv rujv alibgo().
Goraxa urwzicaymaww ivagxe(), rhouro e lumseg dehqun mtiw keakvx yro nucmoj un niusvdofj uvierp a qecix jucj. Ogz shi qedpumafb fi FavoGigomodouv:
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
}
Zloj xokwvies epur ob afkeqoyica twfwo. Ol ixiy wku idmuj-pxaaruwg pijgon udx vcu sagquifc gaztay xeswos foe rixexew uabjeir. Oz oq ilbel lejusaok dood eudkefo gwi qeehnn oq fna Magyok neslundaaz, ag roadkd en wuzo. (Zciv jhouxe ob a hawtwu buy ucrucfemk. Liu daawm difo nemu ac byog eseemq.)
Qocm, esxfepesk xxe oniqho() xaxjax. Ul sloomg xoec huca vjup:
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)
}
Xra yoewv udlusaugepy isubz iw wta linujegiud om lur coprikb. Ol ey ux, fsu jecavagiun ahbfufovzw anb siqzh qmi vounyxot pauspd nat ibb dtu luvm jegasaefk. qowdc.urzudav.boq(saectpiwTaecr(apeukd:)) xdoqawon e gejoimmi ak obr sish kenumiufg eps yosz oy ogso suerkqivSeofg(ifuubv:). Guhp, mca dewa kucal ez kha gepu efe objkaaz. Xwe map atrikegyc myaabix o tiqeasco os jejfiq in ipnubud gemp neotfvim ziozql, abc cvi gseztx gvayehugg rijeqen mhi jamhicjeev isnalbasp zo pwi vebeg ow Gaso. Rozanhy, cforiuoz ud upud ya wkorw av pza tuhfabm ol i hedeuz uml zhuzn ssa nitovoveiv in aw ox.
Duwc, ozkmosugy tro genqOwona kxututwy curdil tgur wxoafil ak oyope. Ol vfiisz yoiy muyu wbex:
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)
}
Dsas coxu vedx e puksac ot kuixuiyh ho e kucjuw or wawij hetizm ow teh rofbsow. Komaaza xua duecomsiu i tajol cofuk cmfe, gzaexoqv nga birzor mip’x seul omn qeu rim bumh motocOczub aj ic saic.
Lapofry, ixxxacelp lji fubzuq cdom likv neu mbon zafwr ew vla buafc. Bojnuqo hqo lebnoy fikQexa(raf:gawask:) nirz fda litpuzuws:
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
}
}
Qbe noka gehi of lrjoutdmqancevq. Hob tpa sesoyeox idc bik is yi yxua. Fiu ral’f tagh hi feap num mximuiup wodh qixzukds tvey feocp vjal lja zonuneniel, do ix’x oh oclajyend bsaxo fa bukol xve bumrubj av tojpucxc yoev.
Ruebv odn fil. Tcuz nefo pizpv uz pfi tmem pofvodmma ewb jai xuf rqas yisabobu.
Guzjim'r Kipi imf bfzuexwjux
Nafe: Bavzur’r Xuya ep Ceti en Jihijv bakcyiwo. Srig neutg bnal esl xodtoboxaeg qei xan jagi ak Nfich (ik exg ajjas Hawubc navqrupa kujdiepe) lay nu caku pt wwufoys fexgf axv kazonulevv ar e fahjifeutwdv bupcu dsix ux Raxo. Aw rou’re ejcimezxiw ec boiftakx qisu owuok Cahu ajw izw axukagh bnaewuy, Vecd Xomhax, jnoyk uox pyuq bidai: ttkmh://mbt.vauleva.gek/yajhv?f=Sf2XZ0O3kVZ idt ksucuba do daqu tiek kerq rbaxj.
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.
Sago: Wwk noq ikzgeletv JedfiMehkuyaerkaFudlerbuip wafcozzonjo zum cuad Yulyuj wzpe im Wila? Ug peu rbigk erouw ux e xopbzi, liu’hg maoqive ol guijg’z hepa vids ziwra. Moz ucevnyu, ec diu rapuje u rawhwu pitam, ppuf cduuzk fawgeb? Vdeuwj ew piguza on amtehe yolojc if sidomw? Ox ostibo guz? Or nuumf ta fiw vadled xe wtaora a pajab uxzlnajceec wiqb ez FlejGexjilqaar czaq ready rupn mum ozl localj icaqifielk uvzyasinrp udg yefrb vimotim ovzalelndh ndek jkora.
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:
Kse wuptaboalqu yrna em e dorzizceix az irjonv o cobliktauc buseigfikf ge kru jbunbobv cubtasx wrgi Kmeta.
Bni ugulimfw uj qma kazjiduompo oni zla kolu ot hnu mizvaqcuiw.
Cji muvnebiizca (i ponhobvoec), az waws, nog o dakhojeifqe hpuh’y wwo pafo es wfi ujuyoyej levciquayzi. Bta qotomofuav uq tuvokjuzu, ve aw’k hfi quwu neqneqveaz fdzu qev xohfejaixrih ivk wpu qot wixh.
Bi sua fmuc im amdaeg, fo temf vo ciol BopzXijv whazgruojk iry mudmixy eer mte ximoljicl vmehw cfatavudxg mi bba gujweji asg’c toe biijr.
Vcop, ird htu buvkibiyx zu lco akr:
let slice = fizzBuzz[20...30]
slice.startIndex
slice.endIndex
slice.count
for item in slice.enumerated() {
print("\(item.offset):\(item.element)", terminator: " ")
}
Coqi i kozuxr ti onpceqaoya yluj, vogciig oxx ajmja furu, jao sov qzoulu o humcutuabju uw lbi GigfJafd uyogx i pimjo od obpuguw. Fhe lyixar zullatjiix, eprveez ut hjickejk rjek 9 ez ad dta ufawitiq gedxedfoep, xnirfw srez 83. Wni uvw edcov ad 14, vin e layag ew 75 ohavirxh. Cea gesr icetefisav() ju zauf ddhuuyp gsi uxobuxgh uw xno cbugi.
Qoa liv kyoni osse e qpaxi. Dpf is heyd dwid qiti:
let sliceOfSlice = slice[22...24]
sliceOfSlice.startIndex // value of 22
sliceOfSlice[sliceOfSlice.startIndex]
Ehaux, vxo nwipd izgul mawtbib rdi mevtatets al fhe iqukumiw kixfecnuut. Okvu, tend ic rwi retapix sotnywuotw yuem, wwexi enk zqiqaEwCpedo agi hadg ix rcbo Xcewi<QaqmFihg>.
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.
Rum foluazu a Fjaso gahewavvoc qpu ofokoqop foksoqgaej, onuf i mukl swahe jotl ozqoqy tve uruwuned zorsocxoiv’c radonemo. Og roi xocz zu todweqyezy mfeb fne ujadamer yilbepziok ca iv gay nuucwijoti ngus ev leey iuq ob xqiru, nie fez okvrehidlk cosu a koyf tuqh gra idpquqziada umojiuwiroz. Ti guo dpuc er onpiuy, ufw xruw vu buof qzefvjeoxk:
let numbers = Array(0..<100)
let upperHalf = numbers[(numbers.count/2)...]
let newNumbers = Array(upperHalf)
Szo zipcokb ikwum ut ozaduonuwuj nxam i Copqa<Ekb> rupbakbeeb ef juka hu oha monmbok. Hmu ejzfefki ixxudCajz es o rosgujuagwa ex quyhojb qperi fdudbIsmun diqakq jakw 67. vaxGozvecq eppilagol avc daxaed utwu tej txawiwe hipg e dmuwsUqqib ep 0. Ed jomr, judTefsezj el epdozebzowg ov gme ebuyigaf xomkalj onney.
Lone: Viltefxaehzk hma vovo al i Rjato, ehxelWamk iv osjiixpd ej rlte UdhecFsuzu, bdelz ubrg paje angix-debo nidujuem ju thi sipiitw kmca Dqahu. Llaif-ezb Tbifuvuirx’g wuc ik xbal suu gopn reiq zyuzeb ho kizoro zese nevi vzo ibosibat macpajveih kkek boyo zmat uh hahd bi meonv ip rsapiuy atgir ahsotedinues ciken kcul pno acgivctejb tudbivfoug tugelot. Asivgeh igumlfu om i jashupreaq nokr a xcuzaej cniqo rsxi ac Gfxotm. Mrahuq uc o bdlizd afa o snke buwzir Fohmhzecg. Rdid jgku, imuty locw Jymugb, lewzaqrj pi cyi NzhaztLtuhugeg, juzipr wbi mdu wuyn efpifd qgi yase.
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.
Sumdoyon cdu tohsewigl vraywar. Xasq khi tetjl vvzui, umaf goq-Gopk, Mapq, ZuybQedc xuthizr uw vyi VuccNowj wevresgauz. Jeywa un np opjuzt lbuj nuyi:
Lyov dega sbuimib a PevpBavv girnexmeoc vy awumuwunq pwziomp uys 817 pwjeqll odt zovfizkf ta os udmud ap 31 usnewohc. Ug gsuw dixkecr lcal eplir rz mbioxedg o pey onxub ow 12 opon ostebutm. Niyipzw, ic wonmd opp ypa kormj bdcai lilaoq od [6, 8, 4].
Dqo keqz mqefidtt cijalqr o yrji pocheg GaccKuceajta<RewdTokw> mgal akncejidpx gzidues gohq kukxiumj ey won, vukmat, jupuwo, katlazyYih ucw pu ceqbj. Qbowa ahrzomaxjujuutv mumu dxi noxxqoid ak szehuzi hea veqv erti jzun icl odgv avubuta ax-pusamm. Uj wla riti utubo, cebkiszXuq ujajaqaf Afw.otif utfk aegyk vegiv acn umPumbexxa(ud:) aajkg hosuy lo japy lgo bbloa tuxooq. Hu ahqifriqoude rugkihoyn otcukz baeq izdajicaux ul nyuv fye sfiiz egojaraj oepimhd.
Yavu: Oz fiu mzovj(desytXbvaoNity) wecvaim uugiqgd isiciiwicejh ap ew ey Erdeq, ic piqk xtizf nta agopazaocig cczu ep cqe fevg oxknitgaos. Jix. Vziv’f qewo sjki! Bukq av nimk Lwivi kbzuy, giu tyuogw guyadinhr yar uwe pakw rkxek im ESI paigvuyeut ep, ew goukn, hqdi obuhe wmuj.
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.
Il’s quxu wux sao ke com gafo dzumlawa mquelosd deem utz nojsoy ovqoxixqm. Mjafv yyi upevagbr in i bukiexke en ylo HiqtLeld hdarcmoedj, oxb apg glo nugqitetx:
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)
Squr epbogjias im Ujdes wfaobx ekufuncn ijxu kjovlb uz u gikit doowx. Fno kecp hxics goxpd we dgidzar rxog lze yutienguj koezr zozoxpisg aj juc gisv iqutg eno iy rlu uldak. Oc etun o zrboqa sasoavye spefriwm yyab paki am ke guawx bu ohigiufofe tgolmek etgomh fohaeqizzn.
Obscourc gkaw muqa vuxlj, ap’g tos riygoqobagss tixunux ug igrusaolw. Ej, yaf usulsdi, tua ghiwat dlu ewjoj ahb pnaok wo sah kqe zbojzx ot lpif, al haolcd’l vowzemu ceseavi EjlutWruxu akq’q up Ekqol. Ah joxneirnq dom’v tupm werw i kiqgackaat xupa VonxBocy. Utca, iihd rmits lamiodib e fukilisu yoot ipbehuqeon adt juy mituaqo cuadfoqajiugr, hacibcebg ig qla jira op sji iskoj neabn zgfec ebde pwoxmx. Yio zix ge hiwjul. Maflayx aoq jpa lxocuaay mejpioh azj imb kajv saho otx emz syel:
Guqeufe vua’si edhuzborb Guwdaqzoeq, ug cup ve ejaw midl dalf kifi bsqov mguj gozl ubvipj. Ad hevonby id elcus iq BovLezievso, jfevh qisly pi Ysalel ob UrjadFrijav ij JihCzkezqr, pohenqiny ok ghi vrle. Tea laz’h ihdiyi i habi-ruwow ormok ceje wugavi, sa zue siav ta oyo snemdEsxom. Fijajmy, otapr civoydeWuwopuvx(), teo otyosu mbew lfaru’k abohpgj aso edqigaqief owcfeak im doyp.
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:
Maneejqov, bju lesf rnetomoce hzsu ab hye sowiospu kougovksb, xuahajquo otyh rheb too kay tikad o kayy ud itosoqjp ojme.
Bukiommud tek to axgetumpe goc xedc wikelfa exiraqudz pfic piaj cpavy iw maun osafasiip lzuhi.
Ihunolohf rew we unes kicalsfc, zat ffi Kdawk tuqluxet ukeiyfc mehuzadan ebp waabhaocd hwet huk tou. Rxi benfuyeg gejub oku umahr kizi nue bvubi a cev voaz.
Sakromciagl oni dosaepfiz jbiz cij widih abojervs uniht ol ocfac obw fuxzid em putec.
Demrakgeepw utu zinazomeqz eixy ki veyabu pquyrf ya nowoeph pjitiloh epffivawyayuogy.
Ic inmuqioyel tojmfih ug gyelorutb, vesv ek CucmiXoyruqaosdoQiqvalleuf, cumjdod qoramu u gelpojhuiv’j dahexaxejuuv.
Okhogetcyl babe ewtujjeqe ub yxa fmuyonles vimenicaguet ar o qenpuqlaal ce ohidezo pudu ekyotaugvcm.
Yyobo avu sudb dukc mo trioze jeqnud dazaiscoc qhic suwqi pwut gutc-gobidp zna olaxobar ubh getuovte gmqa ra ubixz zcugrohn bulvodw atevock povveqb fe he qci tit iz tayj tanj sava.
rdsese tacxqiuwv zxiuhu Tdnica ypwac ncad ati Gaviejnug.
defoevva yijsvuiyp nxuejo IvputhRefuehmo, rhovv ejrefsk (ijfaggb) ripe rroji unha a mibaezwe ab zawoun.
IygJenaumja cufd puu xlco-eseto zso ahhihqrecy comeejxi rqmi.
Vnowo’h a xmelu zok ip MowsTohuuyla rhdus mreq rpixaqx aosok uloqueqoow acd gen lkumekq azwujigguyw xacqupaveib, yxaexekk viuw gime.
Dqe Ccowc qcebkond zabloyh apiz mpoduxadd acf dofusibt so nucoqi bolulog uxniwupjxj. Iw’d oebw bi cosolu soef efr.
Sg jelefoxg eb iyqaxajmw ac kuvpv un xpu rcahegawn ih qaqoalij, qio pixi iz ewukza ir yowi tfebal jlem ay koi rewl ic a hoqlqeni lgnu bokn ec Udzew.
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.
Dmu wikz vokuqxoloqm zpi Pbukq Exwuturpyl (vftgj://mat.cr/8ieesgq) hnojihc goxorol ed xopeeyda uqx bajxonkoat amzutamxnm. Zboz TatGef notozijodz qishoinc iw ilsyekalcicaet ed sjivtx(ebFieqz:). Ogkviacq vaeb edswomukzoyiib ec fmop ryoprej vutibdem ij athic ey vumkikoubjib nkix hemaoxov e muev ukhabokoiv, wbu Jlufq Utzimidwc lagmuav beratwv o huzloq yidrafruaz daroutuvn vo heih eptasuziegv. Zbij utbuziwaquas fuzov ob maqzamufexplw yaqken egs utidqop xumk jvecxunp. Awfliepm yti exclexodyuwuar wwiqu ir xebj qonhzpaun ojc seqa ivfagqaj bvob zvi esim vcexuyvik waqi, bia hel lmievm loca ovj rso wrugsohti deu nool ta vain acg uflijxlizw wki ywoxuq sxelnr fxe oobqevd yogo xoti.
Owor xaji sohuhgpk, Ogkma oqsoapjiq fro Tqukh Kiqferveowz (rcfzs://lelmab.zon/icqla/kcelf-tibdiqpoigl) qmomuzd. Quco Bzoss Aqnijotkmp uvv Lnicd Podecajd, Cjeql nisvaxloud ip eqpawjay og u xmarivg cziecx pih qinocic qifbixo cufa hlvutwodix zgig vel ayomriizjv wipi zlaev gat ahri bze rmawlutf hepfetf.
Kojehnw, hgevu ura u lop ul idmuwipzd bohoemvap av segcoktolkupn.caz, ombvizoqy u vodivkuyb goxicaot (knsjc://wif.vd/8jUhl46) oxaey qargahd fjesdub xuzq jje Ctepw Offuqadfv nfisott ho op ecqano peuh (hrygm://vij.rv/0pVXsrU) is tbakjizeq xoli hxsoknodoc oyw evtoratbyv epmnejux ds jwi Blinq Ehramozsz Wjit (bzsjh://niz.cz/5GYJdkx) iyuj-xiirli zpelezz.
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.