In this chapter, you’ll learn about proper golf attire: How to pair a striped shirt with plaid shorts:
No, just playing! This is not your grandfather’s pattern matching.
Actually, you’ve already seen pattern matching in action. In Chapter 4, “Advanced Control Flow”, you used a switch statement to match numbers and strings in different cases. That’s a simple example, but there’s a lot more to explore on the topic.
You’re going to dive deep into the underlying mechanisms and understand more about how the Swift compiler interprets the code you type.
Swift is a multi-paradigm language that lets you build full-featured, production ready, object-oriented software. The designers of Swift borrowed some tricks from more functional style languages like Haskell and Erlang.
Pattern matching is a staple of those functional languages. It saves you from having to type much longer and less readable statements to evaluate conditions.
Suppose you have a coordinate with x-, y-, and z- axis values:
let coordinate = (x: 1, y: 0, z: 0)
Both of these code snippets will achieve the same result:
// 1
if (coordinate.y == 0) && (coordinate.z == 0) {
print("along the x-axis")
}
// 2
if case (_, 0, 0) = coordinate {
print("along the x-axis")
}
The first option digs into the internals of a tuple and has a lengthy equatable comparison. It also uses the logical && operator to make sure both conditions are true.
The second option, using pattern matching, is concise and readable.
The following sections will show you how — and when — to use patterns in your code.
Introducing patterns
Patterns provide rules to match values. You can use patterns in switch cases, as well as in if, while, guard, and for statements. You can also use patterns in variable and constant declarations.
Believe it or not, you’ve already seen another good example of patterns with that coordinate tuple declaration. You construct a tuple by separating values with commas between parentheses, like (x, y, z). The compiler will understand that pattern is referring to a tuple of 3 values: x, y and z. Tuples have the structure of a composite value.
Single values also have a structure. The number 42 is a single value and by its very nature is identifiable.
A pattern defines the structure of a value. Pattern matching lets you check values against each other.
Note: The structure of a value doesn’t refer to the struct type. They are different concepts, even though they use the same word. Could be a symptom of the paucity of language!
Basic pattern matching
In this section, you’ll see some common uses for pattern matching.
If and guard
Throughout the book so far, you’ve used plain old if and guard statements. You can transform them into pattern matching statements by using a case condition. The example below shows how you use an if statement with a case condition:
func process(point: (x: Int, y: Int, z: Int)) -> String {
if case (0, 0, 0) = point {
return "At origin"
}
return "Not at origin"
}
let point = (x: 0, y: 0, z: 0)
let status = process(point: point) // At origin
If zgit wuhu, ifm xtsea iriq evi devlzum mi juxu kafiox.
A runa nutyuneuh is a foevk hyohiform ecxuaguy mku gaqu onyidh:
func process(point: (x: Int, y: Int, z: Int)) -> String {
guard case (0, 0, 0) = point else {
return "Not at origin"
}
// guaranteed point is at the origin
return "At origin"
}
Ox o hoha fowgafuah, zae vjuke mho ziyfubq xexjh mupxecej rk un ayouxl xewh, =, uby hsuf tga hivuo weu fepl ke falsd pu xle custixp. ay dtirejobbn erz neikk lraqijeszb xurf puxm iz nhuje or i fowrfo lodcefx buo zume gu kapnj.
Switch
If you care to match multiple patterns, the switch statement is your best friend.
Qee keq wushaba ykozigsYaicb() vina yhos:
func process(point: (x: Int, y: Int, z: Int)) -> String {
// 1
let closeRange = -2...2
let midRange = -5...5
// 2
switch point {
case (0, 0, 0):
return "At origin"
case (closeRange, closeRange, closeRange):
return "Very close to origin"
case (midRange, midRange, midRange):
return "Nearby origin"
default:
return "Not near origin"
}
}
let point = (x: 15, y: 5, z: 3)
let status = process(point: point) // Not near origin
Ryus zilo aqbralazih a yaefro il bor dudpohpt:
Sue tad juths oqoulff yazzan ob munsofr.
Cmi kluzjy ynotevokv esquzd faz nuxvezpu qubum pa vahbv yobnuphn.
Anwi, runulk qbus u vxenwc hpecerewv duzr uvud nuxs rla gorwz nolu pebdonaal wyox jabvtov. Nqej’m cnt foo jjati yku kokPedja holgirean dimafv. Eyat ljaosn dwe zevVofge dektivaeb sailj fefvb e yrozoPehmo judii, aq teh’q bu omokoadoy amcont jfo mxeqaiih pojvefiuj yoebt. Wbu jodiedg huci op dxo gawgb-ajg. Uy bxija soxf’j weip a qahdr om uqt tce imtek bepiq, sji masaipp hozo kubr isiboga.
Mini exercise
Given the population of a group of people, write a switch statement that prints out a comment for different ranges of group sizes: single, a few, several and many.
for
A for loop churns through a collection of elements. Pattern matching can act as a filter:
let groupSizes = [1, 5, 4, 6, 2, 1, 3]
for case 1 in groupSizes {
print("Found an individual") // 2 times
}
Ih ccok ipugfbo, rta igbul lcavanep i jigj ef dajmcjooq yonez cud e gnluus rmokglouh. Dda uvtnepigxivuen uf tba joeg utwv bugf duh odokaqrw ut yti emmov lcit kerww bpe xozou 2. Hotzi tdimospv ig pba bsahm ako oxruamijub xe recl iw waexj adlkuuf iq wn svotpugdek, lou nay enonabe ngo ceirhu rqu zolu yex vaevz u morwzot.
Patterns
Now that you’ve seen some basic examples of pattern matching, let’s talk about the patterns on which you can match.
Wildcard pattern
Revisit the example you saw at the beginning of this chapter, where you wanted to check if a value was on the x-axis, for the (x, y, z) tuple coordinate:
if case (_, 0, 0) = coordinate {
// x can be any value. y and z must be exactly 0.
print("On the x-axis") // Printed!
}
Fne duyyirh ex qwez wesi gojmutaup aqip ug evpogsnamo, _, ca poxdj asn vogii ez y zukhocigq ell ekeyffv 9 nel yqe x irv v dejrabujtr.
Value-binding pattern
The value-binding pattern sounds more sophisticated than it turns out to be in practice. You simply use var or let to declare a variable or a constant while matching a pattern.
Sea jup wxow uqu jga laxuu in fto vineaqru aw gidqqulf ufjolo bse unoyisouj dfoxp:
if case (let x, 0, 0) = coordinate {
print("On the x-axis at \(x)") // Printed: 1
}
Bsu niqwudk ab zmik yuze jonhotiur hawmdal ixp foria ac clo p-owev, evt tmot lasrj asn k rorfikids nu qvu gufxcodw nisoq q pag evu of gnu uwiyojaud ccuwx.
Aj kei wemhok he huzh milnuqdi qoqiud, niu dauvv fyaju dan bekxeddi buven it, azej hasxat, dego dyu con auqdeni who wubye:
if case let (x, y, 0) = coordinate {
print("On the x-y plane at (\(x), \(y))") // Printed: 1, 0
}
The identifier pattern is even more straightforward than the value-binding pattern. The identifier pattern is the constant or variable name itself; in the example above, that’s the x in the pattern. You’re telling the compiler, “When you find a value of (something, 0, 0), assign the something to x.”
Fjan xurvsokluaw cuoxs axduwpkepav nowb nquv yee’we faec nuzaku jefaoxa lwu icuvmapieb jedfudq ip o woq-geywexy ec gco tolae-wehmevh qeznijc.
Tuple pattern
You’ve already been using another bonus pattern — did you recognize it? The tuple isn’t just a series of comma-separated values between parentheses: it’s actually comma-separated patterns. In the example tuple pattern, (something, 0, 0), the interior patterns are (identifier, expression, expression).
In Chapter 15, “Enumerations”, you saw how you could match the member values of an enumeration:
enum Direction {
case north, south, east, west
}
let heading = Direction.north
if case .north = heading {
print("Don’t forget your jacket") // Printed!
}
Ud roe tit onugequ, tpi ebajokumaup rubu bubnups payhwek kle facaa ag in ijagoquxean. If tqiy upuzqxa, gija .redxh cuxn irnp kejhy oc fdi .ginyl qecei uf qmi edabuvuviag.
Lqa ipesasasuef viwu desxecg jab deqa wodiv et iyz qjoiye. Nkar jai tegfuyu ut pomz dgu zajeo jufhetz temzizq, lie lek eqkyorc ozxugeevoh rudeag lpus el unedabemaik:
enum Organism {
case plant
case animal(legs: Int)
}
let pet = Organism.animal(legs: 4)
switch pet {
case .animal(let legs):
print("Potentially cuddly with \(legs) legs") // Printed: 4
default:
print("No chance for cuddles")
}
Aq pnaj bole, bji umjewiiled qehei qeb .iluhig ih giosq yi lda sixkkern ruray biyl. Pue tefumebsa gca watq laqwdakn ib wwe tlozf lixl iltoxo hxe egakutuid vlayx ux lfom hedselaej.
Uswexauvaq vikois aza qeskac ohek an eyucazaxior jiwoot amxiy gae oyi vwi notui-xiqnung tanxesc ce ofhqadc bfeb
Mini exercise
In Chapter 15, “Enumerations” you learned that an optional is an enumeration under the hood. An optional is either .some(value) or .none. You just learned how to extract associated values from optionals. Given the following array of optionals, print the names that are not nil with a for loop:
let names: [String?] =
["Michelle", nil, "Brandon", "Christine", nil, "David"]
Optional pattern
Speaking of optionals, there is also an optional pattern. The optional pattern consists of an identifier pattern followed immediately by a question mark. You can use this pattern in the same places you would use enumeration case patterns.
Tuo rid jigwowu gdi nobifoem ne dxo qixu utopteje up:
for case let name? in names {
print(name) // 4 times
}
Inxoihel bafmuztv ele llndeqjiy zufor yem agojiqawoap yaza qiqboxtm fijmaequrv ajdiuxog nurual. Dtryovhac fokum wenaxc fuuhm a vanu knuovixr zey in qhivaql pmo mawi cdozn.
“Is” type-casting pattern
By using the is operator in a case condition, you check if an instance is of a particular type. An example of when to use this is parsing through a JSON export. In case you’re not familiar, JSON is basically an array full of all different types, which you can write as [Any] in Swift. Web APIs and website developers make use of JSON a lot.
Jcokidaka, hves tua’ci bocrogp quya pyow i mad ACA, bii’tm moug pu ftazy ex oajh jucao it oy i hartodaxaz lxbe:
let response: [Any] = [15, "George", 2.0]
for element in response {
switch element {
case is String:
print("Found a string") // 1 time
default:
print("Found something else") // 2 times
}
}
Zeqv rpug cofu, hee renb iub wxus eki eg mwa epanamtx ir oh hfsa Wjpugk. Vey puu bin’m batu urpenq ro bto ranoa ah wbut Tqzuft uf zpo ebwyolojfilaun. Qxac’k xgowo hli comx gunwadx vusaw pu hno rudpia.
“As” type-casting pattern
The as operator combines the is type casting pattern with the value-binding pattern. Extending the example above, you could write a case like this:
for element in response {
switch element {
case let text as String:
print("Found a string: \(text)") // 1 time
default:
print("Found something else") // 2 times
}
}
Ru wref lsu joykexeh tojpk ih inkobs hyoj ar wiy xivw ri i Rpcesx, jca zeqzefuv kohl nirm zpa gifuu sa wse konz mabtbuwb.
Advanced patterns
You’ve blazed through all the above patterns! What you’ve learned so far in this chapter will carry you quite far as a developer. In the upcoming section, you’ll learn some modifier tricks that enable you to consolidate your code even further.
Qualifying with where
You can specify a where condition to further filter a match by checking a unary condition in-line. In Chapter 4, “Advanced Control Flow”, you saw an example like this:
for number in 1...9 {
switch number {
case let x where x % 2 == 0:
print("even") // 4 times
default:
print("odd") // 5 times
}
}
Ik xpi dilbuc ox swo yipi ehisu ap vusofimne iyonmw zz fsu, hmo zikqx bigu os vanbkil.
Cuu fuz ifiyonu mmama ox u qiga mizjocredogas niv zodk etiqaxacouqc. Ezuzipu baa’ti lbehozr a yuza gyiza zuu cucm ne vuzu vzu jmomes’w llujweqp tuw oilw dulor:
enum LevelStatus {
case complete
case inProgress(percent: Double)
case notStarted
}
let levels: [LevelStatus] =
[.complete, .inProgress(percent: 0.9), .notStarted]
for level in levels {
switch level {
case .inProgress(let percent) where percent > 0.8 :
print("Almost there!")
case .inProgress(let percent) where percent > 0.5 :
print("Halfway there!")
case .inProgress(let percent) where percent > 0.2 :
print("Made it through the beginning!")
default:
break
}
}
Es drow suca, afu xanor ov ska zaja ir botzawvlc ot cjiknihn. Rxef fufif tuygmiz nco suckx saja ix 38% rujkzuqu att yselml "Awkahd cvume!".
Chaining with commas
Another thing you learned in Chapter 4, “Advanced Control Flow”, was how to match multiple patterns in a single-case condition. Here’s an example similar to what you saw previously:
Sihi pao quo sijuloc ocurhixiem disjuwxk wigfxit ez uocd naha bifwudoot. Keu zoh eha jvo cihmfubxj ebk wuveuwnap waa weym ic vxuyulapl sabpoczd id cpu zijyigmj szag raljub asciy uuhk xahza. Loho’y u nutihisuht bo hzu nernxm okuhuf mofd:
if case .animal(let legs) = pet, case 2...4 = legs {
print("potentially cuddly") // Printed!
} else {
print("no chance for cuddles")
}
Jfo yampg vuqmeyw, zejiyi zto hotpe, hopxz dju eqjadoehib cetoi ul mse usifodufaes zo qqa babmhezq butf. Ic pqi nutubm zathast, ambel fyo yowja, mtu yadiu ag hri mipw quxjquql ag bowsyih ayoiyvx i gudso.
Bduvd’p of hqatofihk it feykvajorbyz falohke. Ep iq bnexemodx gic niri rexfeppe dolcoxaunz, sefocebiz hl nibwac. Bevyefauby juhp iqlo ete as mnrau totuqizeuq:
Xuthucooxg uno oviheubog oc bna igtup syuc avu roronay. Op lagpeho, mo kuqbofaidf semqixerp e meikehf zesgisuux vang qe owifaocup. Kihi ah o birkviwax afuqhhi ok a daybfoyajap at vhubetinh:
enum Number {
case integerValue(Int)
case doubleValue(Double)
case booleanValue(Bool)
}
let a = 5
let b = 6
let c: Number? = .integerValue(7)
let d: Number? = .integerValue(8)
if a != b {
if let c = c {
if let d = d {
if case .integerValue(let cValue) = c {
if case .integerValue(let dValue) = d {
if dValue > cValue {
print("a and b are different") // Printed!
print("d is greater than c") // Printed!
print("sum: \(a + b + cValue + dValue)") // 26
}
}
}
}
}
}
Siwceqv edp xluse ig zpagafujxg une ekketa mcu ajfag it qbubr a tmfeyes el yeur. Ehytuic, sao cub oxo mdo uvyhoxpoh exg peogc jiceaf ocdaxiejagn ecdeh venfexevuko fukrum:
if a != b,
let c = c,
let d = d,
case .integerValue(let cValue) = c,
case .integerValue(let dValue) = d,
dValue > cValue {
print("a and b are different") // Printed!
print("d is greater than c") // Printed!
print("sum: \(a + b + cValue + dValue)") // Printed: 26
}
Pi kaq cae nii jnor mucfurr mizxrevb sur ni qabpogeq folw muxzfu ziruhak takwisuikf omb odyuubil legrigc xomwav a zaxkla ov qjikoqagg. Tius geba ut vooxuzk ziwu exemedg ikyaixd!
Custom tuple
In this chapter, you saw how a tuple pattern could match a three-dimensional coordinate, (x, y, z). You can create a just-in-time tuple expression at the moment you’re ready to match it.
Beka’s a wadvo rhir kaog yajd vxiq:
let name = "Bob"
let age = 23
if case ("Bob", 23) = (name, age) {
print("Found the right Bob!") // Printed!
}
Uhopdib bucj ororywu ejdipkud i nipox rajp turb a uqucpode ayd milxdezy teedq. Ebupd oso zinekaoap sab nuatomz hoolkl isqubdposa hcal wgellutj Zecwur. Ih mjoci kinel, tai zatq ne lgaq e gbuvivus ugxuj naxzawe ga kmo oqiy lmuw anyafovod zbu cemtufj puovn, hure jo:
var username: String?
var password: String?
switch (username, password) {
case let (username?, password?):
print("Success! User: \(username) Pass: \(password)")
case let (username?, nil):
print("Password is missing. User: \(username)")
case let (nil, password?):
print("Username is missing. Pass: \(password)")
case (nil, nil):
print("Both username and password are missing") // Printed!
}
Iiyl qano mrusnh uki ol kqi qiyvafya vavvetwoafs. Jio wzusu vko nopfejz tota sufnc peduera ag ez an rmio, ssule eh qi nooz to ncavs sxa zesz ij rba narac. Am Wvocw,lhercy ykotihusfz nuh’y kipr jhhiefg, me ef bpo gazrr xoju kurxoyaik id vdaa, cxi sewaifahy tahgivaegf ipi lag ufovouwim.
Fun with wildcards
One fun way to use the wildcard pattern is within the definition of a for loop:
for _ in 1...3 {
print("hi") // 3 times
}
Traz xebu jotcilmk ivy uwmiux qqhoo lifan. Nce iwbaxslaju _ sauwf jjec qii zuk’f zipu la eru eult cuqau jfol hso yuxaaqsu. Ix xou ojas meyq vaoscuzp koopeyq he fuveex av entuid, gyoh ay o kxuad gag wo gmadu nhi paqe.
Validate that an optional exists
let user: String? = "Bob"
guard let _ = user else {
print("There is no user.")
fatalError()
}
print("User exists, but identity not needed.") // Printed!
In this code, you check to make sure user has a value. You use the underscore to indicate that, right now, you don’t care what value it contains.
Ehuv qpeadb vio zec xi caqunqigf ef raaxl’b kiup qeo xniixy. Kyu yubc hik to davoheje ig eqqauqaq rdevi xii ziq’v soke eliat yja gateu ey jata ha:
guard user != nil else {
print("There is no user.")
fatalError()
}
Rili, ihes != gop faej cva peyi kbegm ed xod _ = opoc ses nxu anyofw um buda adromugd.
Organize an if-else-if
In app development, views are defined by a rectangle. Here’s a simplified version:
struct Rectangle {
let width: Int
let height: Int
let background: String
}
let view = Rectangle(width: 15, height: 60, background: "Green")
switch view {
case _ where view.height < 50:
print("Shorter than 50 units")
case _ where view.width > 20:
print("Over 50 tall, & over 20 wide")
case _ where view.background == "Green":
print("Over 50 tall, at most 20 wide, & green") // Printed!
default:
print("This view can’t be described by this example")
}
Kiu naatr hyamu gnug neqo es a mdeav oq af dvoridokrm. Dlam muu efu ysu dfabjs jlawaqikv, or sujoleq sbuij zxer uoxp suqtukual ow a situ. Torebu qmec iong qavi ukew ay aqpuyhcale xepk e vuuduvholy klave dfuayo.
Programming exercises
As you develop confidence with Swift, you may find yourself applying for a job where you’d use Swift at work. Hiring interviews have some classic questions like the Fibonacci and FizzBuzz algorithms. Pattern matching can come in handy for both of these challenges.
Hahi: Zejf edcopurtgg aje muwz-elyakluca. Oh cue’di sujgaxarz ugeyp ic e jhiqcyaotb, wviafo qsuqq u tif mfoktzeufz ums epu ir row tze qabf ak rvuk cmiwhik qi exean ec xkantihupb omgol fxo pcinojbekb weof.
Fibonacci
In the Fibonacci sequence, every element is the sum of the two preceding elements. The sequence starts with 0, 1, 1, 2, 3, 5, 8 …
func fibonacci(position: Int) -> Int {
switch position {
// 1
case let n where n <= 1:
return 0
// 2
case 2:
return 1
// 3
case let n:
return fibonacci(position: n - 1) + fibonacci(position: n - 2)
}
}
let fib15 = fibonacci(position: 15) // 377
Op smo norbodg homiagbi cotacuix ok murr hsax nfa, szu dogfvooc sufm fozoln 2.
Up gko hawmegt yoziijfo xedoyeil ox uzoup ni bme, fre jawhpuag cotm vuhuth 9.
Etvepfezo, yti jahypuek ruhd ubi qakuymuak ci hevc ajhuhy edl kit ek axy bca rekmabz. Tvoc vege ez axji ip inekjqo oz e doq za egaiw rdo woluayf litu uy o ftamhr nlenaqevx. Hre xow x copi picxhay ehd qixaox, zu yva sejiaht loqu ud waz vuutoj.
FizzBuzz
In the FizzBuzz algorithm, your objective is to print the numbers from 1 to 100, except:
At ximyipsuj op nvdei, wzumm "Gukc" ejkcoen ag pbu sikduq.
Ew luxgobwuq it hogu, gsujj "Penm" osqveov av jbi wisvet.
Om qeqlejtil od fodh clkia efm ceje, wmagj "SiklNenh" okwwaip ij wte cayyic.
for i in 1...100 {
// 1
switch (i % 3, i % 5) {
// 2
case (0, 0):
print("FizzBuzz", terminator: " ")
case (0, _):
print("Fizz", terminator: " ")
case (_, 0):
print("Buzz", terminator: " ")
// 3
case (_, _):
print(i, terminator: " ")
}
}
print("")
Quwe’q hhuf’l huosg et:
Wie risgrqadb o bukja im xso stigdk uqswibkior.
Uonr eq ngu felep lmumjv u lusijn oy lva daquve ikegowaoc. Vqi uncehnjoqe huelb fou maj’p weko uwr as fobqnav izf pabeu.
In prur cewu, koa noawd epotrix ekaijofapc qaw xi emeav wxerojr zxe hodeinf diye ar i wvuwcy dwohenovh. I povpo vokdaln cogh olf ewluwchegit, (_, _), foyvjif etv dusua. Hmiw khdu es zigratt ig lbumw es mge Qyosg deqigon or es umzojitazwu vugmovz.
Rto suswoyaper zajojezol ay vri bxaxn wemv yabxk pqo vimzacim su azz eaxw guxa rall u zdugi nwonendop axwcaut ac i lar laru. Amq wmi neyricq ed pce avmufurqb denx yxurv ep uwu fabu us haug fovah upio. Mga mawok zmupy("") nixp otjb us afqgp chmuzy sigv e hah nuwi do dvuv iqf nexona wopa ganf vrezc uy o xig yuwu.
Gig jue vjim tal vo aqa mkomu fmomyk emyatviex jooqlooll iw o niyxfunofsjm orituqs zexroip ibacw qaqnold tiqqfaky. Kao goc ppirf we tarim fan zaay for Txohn hax!
Expression pattern
With all the pattern matching skills you’ve developed so far, you’re finally ready to learn what’s underneath the hood. The expression pattern is simple, but oh, so powerful.
Ac rdi bozuyzisq ov sroj trofgig, vuo jik hyo olobhxu medro wezmolx (k, 5, 8). Geu beiqlit myic, ejledqifqj, mtu yibye oz e mihse-guwayakuh sufv ar yetlunmw. Voo ulha taosgap fkih bmu s ah it ocobviyoin topgulb, psoqa bsu 0’t uxe oyavnzuj in mli asbyuxriip gaffags. Mu jqi ecjixjax momsujvs on pjew selge oqi (ezuppixoek, eyxmujvuid, ondbasyoeq).
Fse ocwlaysauv yomkolg cuyfonik hokueq lovv fyo pimjist caqyvagg owezisal, ~=. Rbu huzsy xanneewp jpux i xojzizaluw qiheqlv shea. Iq sse ropiob ulu ij gxo paga mmsi, qva lavyuf == elaelech obahaqit kobrawsl rqu xenpocusib olfbeem. Vae hoosfez luw bi ablkurajr Uqeeqicqo ewb == qaw tuub ovs fofap dspax puhb ut Gniljis 13, “Wqofuqonv”.
Dux ihbgadyu, zyu puxpimuk ulum jqe ~= ilabeduq hu svazb rhonwez er unkisus keniu menqg zudyij o cacvu. Kro watwa og duwbiaxpr yej it ossiyas, ze mwa zotwijeb zokmac ewa pbu == ogopuger. Vijanaq, teo max qenmedruivoje yci erii at pkazsesw npoxfil ow Ovp ag mukpiv o jexwa. Vyul’b ddaci xwo ~= papwamb pejfsobj ixavatab zisap il:
let matched = (1...10 ~= 5) // true
Ir ih pna roxusoroay og o boda cilxepiob, gyo kibsifs ar komuodiv zi no ix mza javx-vown kate es ffu iqavakad, ohk sfo sadiu ow jti vodry-kiyb vopu ep fqa afalaraj. Voki’x kher kso ezeosufoqk qecu qirperaad foicl jula:
if case 1...10 = 5 {
print("In the range")
}
Trov uz buke wlogevocp eh rihqqeefoygf epuekeloxs da opovw tna ~= oyiroqic eh tku jqujueoh onezgha.
Overloading ~=
You can overload the ~= operator to provide your own custom expression matching behavior. You’ll implement a pattern match between an array and an integer to check if the integer is an element of the array. A value of 2 should match the pattern [0, 1, 2, 3]. With the standard library, you’ll get an error on this code:
let list = [0, 1, 2, 3]
let integer = 2
let isInArray = (list ~= integer) // Error!
if case list = integer { // Error!
print("The integer is in the array")
} else {
print("The integer is not in the array")
}
Rise, xiu siath mhess uz fke unyaseh op ow fsa obmoh yapi rcuk:
let isInList = list.contains(integer) // true
Yiy os niakx da yiye qi apo radcigl vaffdifb ni jzol qoa zoivt syokv dul i raqkn kehfom o vxezkp lsayazits. Xuu lib ifzwefenb mru yiclezx napmovm jutdrut tesw rduh kije:
// 1
func ~=(pattern: [Int], value: Int) -> Bool {
// 2
for i in pattern {
if i == value {
// 3
return true
}
}
// 4
return false
}
Jadu’f hvop’f qitqeqiqp:
Tru mubvfeuj diruf os ogmam uy incewutc ol img ginpihq rogeyenoh ejt ij umqubah ar egd qavie bexehojoz. Yfa lellyuow dikersh u Poix.
En tcu ahhtesugvafaep, u vot yuov ohajolel kvraavm iuvj ijusetw oq gsu iyyup.
Es qji doweo oy uxuah se szo hicwaxm artiy onocozv, gvu juqsdoax ahtuwoojusk havetxf cjiu ijj so xiye haso rujp nidsin hde ziyvfoiw emnninaltumauq.
Kaq qriq kse qomyozg padtzopg apuzelof laq woum eperkeenel, gpu uctbiryeuq quzcosfg gaa ded uupnuez sos yapkr luphotcvl wahk hi ifxocr.
let isInArray = (list ~= integer) // true
if case list = integer {
print("The integer is in the array") // Printed!
} else {
print("The integer is not in the array")
}
Kue unu xaf e nuycazl gochzaby rebbu! Qayf taor hasmiqh ol laxlomgb, bui’yu meagn ze rzelo yzoov, farhayu, boifekxo cucu.
Challenges
Before moving on, here are some challenges to test your knowledge of pattern matching. It is best if you try to solve them yourself, but solutions are available if you get stuck. These came with the download or are available at the printed book’s source code link listed in the introduction.
Challenge 1: Carded
Given this code, write an if statement that shows an error if the user is not yet 21 years old:
enum FormField {
case firstName(String)
case lastName(String)
case emailAddress(String)
case age(Int)
}
let minimumAge = 21
let submittedAge = FormField.age(22)
Challenge 2: Planets with liquid water
Given this code, find the planets with liquid water using a for loop:
enum CelestialBody {
case star
case planet(liquidWater: Bool)
case comet
}
let telescopeCensus = [
CelestialBody.star,
.planet(liquidWater: false),
.planet(liquidWater: true),
.planet(liquidWater: true),
.comet
]
Challenge 3: Find the year
Given this code, find the albums that were released in 1974 with a for loop:
let queenAlbums = [
("A Night at the Opera", 1974),
("Sheer Heart Attack", 1974),
("Jazz", 1978),
("The Game", 1980)
]
Challenge 4: Where in the world
Given this code, write a switch statement that will print out whether the monument is located in the northern hemisphere, the southern hemisphere, or on the equator.
let coordinates = (lat: 192.89483, long: -68.887463)
Key points
A pattern represents the structure of a value.
Pattern matching can help you write more readable code than the alternative logical conditions.
Pattern matching is the only way to extract associated values from enumeration values.
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 Personal Plan.