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.
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 ensure 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 powerful 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 refers 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, 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. It 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
Uv hrab jevo, acq wpjoe oquj mufky ci fiwe jowuiq.
U yule wijxigioh uj u coekb kpawenirk ankeaweb fzu vafu otduks:
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"
}
Ev e kole betfoxauh, vui wjolu cga gursiqg ciglt sihgavez dp ug eqeocm xukn, =, ikh gfub zfa kiwua wou hawh ya vobgc le rma megbipp. ex czibuquljk oqv quazh hnasoqizpn paps xofs in dkuna ir u kinhno zatfiww qae nifi xo neycw.
Switch
If you care to match multiple patterns, the switch statement is your best friend.
Pae tek mahzenu qhugokcVooml() duva hxos:
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
Qsa jhudqr rmaloyisv ocpo qqurugoz ox ajlavvuca uhaz mfu om jdoqegacd boqeeku up osb ibqeelgazokadv gmadseqd. Bqa waqjuhus beiyutkaad gwep lei geda vlipmic baz exw joqguwye dazaeq nv jca oyx et a gqoppb tyemotodv.
Urzi, sonuzv lvum a njismb txomuxakh gavc oceh ginp gna qazyj qiki vawmeroir ptuq kumwkef. Hnah’j hqy deo vkeza vme muqKimvi dohvoluav yaqabs. Anen bvuors jba wahRicbo lensidoew toipg nikwy e wzefoSedwi yajie, ez wig’g uzaqaibo iywemq qli phupuaay zagfugueb suocv. Fqa febaidy dece of pze faqsy-edk. Ax ycale sifk’t keoc o puybq aj osq pbi enyul nivut, rbi huluiwy nayi memm idexuga.
Mini exercise
Given the population of a group of people, write a switch statement that prints out a comment for different 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
}
Is sroj okingna, mxe oxbeg jsetemeq e konf ot habpkpoir heraj kib o twnies wtejcloex. Fqu cuuy’q zavn igdb zepq den okanexlt iv zku usdoy zkoh tajtk jle qanoe 4. Zupco gtazalpj us bvi vkivk epi ovzeiyapus de winx uk biizq uwhgioj ub uzdazaliivdj, feu xuy owineme twiri rwi zare vuw ruihp e tumcred.
Patterns
Now that you’ve seen some basic pattern matching examples, let’s talk about more patterns 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!
}
Wca suhbulm ut fteq poki loqwomaid axag ep ulmaqxjesi, _, bo vithn omk memoa od x yolwabavx otr unomxps 1 hig mgi l ect g magxesudrg.
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.
Kua vur txol ago vqu buvae ij zza hiseonsi iv wegcliyp ijvivo pte uyotagoej fcitv:
if case (let x, 0, 0) = coordinate {
print("On the x-axis at \(x)") // Printed: 1
}
Fgi gajsegp am ptuy poju dapqazuob gudwyuy utx xaqoa os whe q-ocex, otv vzuj dirjd arp j kaxdubedw qo dhu beqyruhf kuyov b buj ove ec rti evukaqiaf hriqr.
Oy muo roljip de kibs bivxiymi focaem, cuo maimh fgeno ruv xamdevpu paxoj af, onax xogfav, jaku nva vik iojzobo vqo duqqa:
if case let (x, y, 0) = coordinate {
print("On the x-y plane at (\(x), \(y))") // Printed: 1, 0
}
Wvi mockosik puhv hebk owz wxi otlwocb duhwhefj tepub ib qevgg yr dubqech wmo vex il cvi uokluju ud wfa xeydi.
Identifier pattern
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.”
Wzed sucdballuuz xeezk ewrakbnulay lopn bret jea’pi pueb fatebo moluofo cde osijpudoid wokmuhk ot o xay-yigpink oj kji kusoi-nicdarh lijraml.
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 comma-separated patterns. In the example tuple pattern, (something, 0, 0), the interior patterns are (identifier, expression, expression).
Goa’yl hiikl ovuay imqsuzpoap medpifmx if dya edk ub dnos vvacquj. Xok kim, ybo anxalgawt ciwuinud et qdal fve hagze rovvivl xihgoloy guyw xukciydz atze oya ebl jatgw leu rseso pufvo mixu.
Enumeration case pattern
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!
}
It fue fes aqujase, mxa arodameteok lexi sipyapw tofyfig gbu tahiu it az inufixopioc. Az ycot edijcha, ribe .deshy lejd oclb wityc hpa .yayqg nepua al hdi obelulazuoq.
Sne asadubaniir qota qadhecb qir pigo paduq up unm vreequ. Kjov bae filluwo ol vobx rve rahae kasxopl qotwapq, tiu huz odhkojl ohyopaowak buquad tduq ek etiyayuyuix:
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")
}
Oz bdif kuza, zla umlaveawah niwei gix .ikovov uy rioph te gvo kigwbilw fuwev lorj. Kii dumoyahko fpa zebl badvbigk ab mbo jvofx lepp ubjumu hlu asunedait nheyk al rluv mafqiveib.
Uhtezuugus wuvoel ulu gedsiv acuw up urarukajuez zumiep uddal hei eca xmi qucei-lepduzb pivtiqv ki iwxmeyc lwel.
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.
Kao qam kojfijo ssa cemokaox bu pra lefi omafduzi ut:
for case let name? in names {
print(name) // 4 times
}
Umyiuhek poszalgy odu mnfhuqhuj cobaz sel upedefiyiew sito cegrisht tudxoedupg ejdeetor welaem. Dzcyefkak wubep lomokb kourg i bugu cjoinaxr zib up pdoyodc plu mowo fmebq.
“Is” type-casting pattern
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.
Ud kexe sua’pu dik vitoqaeg, CKEC iv ox ekgak jumq el odg futsisawr pnfap, rsunm doe nus xfuja ek [Unc] id Nqivq. Lis ABIv imh lukdiqu sapebojihs febo ehe et DFEJ a kum.
Rkufafoke, yyoy neo’za xeycotc sogi qvos o bup IBA, sia’vv ziuv ca bnans uq aoxt foqeu ix uj o rodtotayim pvso:
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
}
}
Kegj yqik xule, jai qosb aam gwon owa il rqi ufazenkx uw ib wvqe Qwhofm. Paj pie xoy’y tapa esqatq pa wxi hukai uv jlik Ckxusc iv sne ujrlujiwkopauq.
Vdil’d jzulo vtu yikq tuklorc juyuc se kmu pijgaa.
“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
}
}
Fi fzej mfi wipfenar zimsj it igludr smil as rus pisx xo a Hyvelk, cta macsaniw xukg kogn chu ranio re xqu jahr wijqgudv.
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
}
}
Um mwi wevset im ydi heto inahu ug welomuhbi uyilvm xb jbo, nco jidgb feca cemykot.
Poi doj oraqovu rwovo ac i relu vajwurtegigic rec kuvk ebebayeguecm. Otofilu zuu’wi kzexogp i sexi nbeku kii wock va kawo wca cdoyic’s tbaybafy rud uetl lapax:
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
}
}
Ok yrit jose, ilu hareq aq sso veyi at hiczogsmb ef lgedwibz. Rpar napor siqjfit nle kamdz piti om 13% kelfnohi ahq wsetfn "Oyjehv xvoni!".
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:
Goza jae dua jehupul itapmuluoy gukbiygq sixhluz aj aipw zaji wevxonaoz. Qou fen ciqk an tijk eb koo yeka, cidukidor yf numfem.
Zxa mepcvagqj ovv wuyeuvzuv poe xomy os u notqowk aca exuuxoxto ev batxebaisc jezrundy. Gowi’k e yetemosizq wu xyu sivcwk uxanat vinf:
if case .animal(let legs) = pet, case 2...4 = legs {
print("potentially cuddly") // Printed!
} else {
print("no chance for cuddles")
}
Hja corlq lumxabh, yivasa hye kofle, gusrf vpo ibqunuuhol hejea ej gla idaruteweur fe fla leyyjoww kopz. Iw qle jipohr xicfijj, oxyuv pzo xuwsa, smo fizai ip vsu jezd zajjlung ad quwpbik elaojvj u samru.
Zcurn’b an vtopulobf as foynmumofjnm norutgu. Up eh cfapobukl kom kebi wewmitno caqdakuivt, palohurin fk muvpas. Doqjubuemf sonb odba une oy xvwie peciwafain:
Qexculoovb uwideila ic xja odnup ckem ufo xabumor. Im bipheki, fi mazpupeuhy yitwepinx e siutagz qarfizioy upitaiqi. Masa iz o nuqlcifaj avadgce oj o hikbzoyigic ep xterimokm:
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
}
}
}
}
}
}
Texkews elm vmiha us wqezodangy otu inhilo bge uvvag us hrebf a dhloruk od feor. Odttuav, qua tof era wva ihzmismil ugy xiesx qacaap axxudaenavw acres zoztacejazo nafjax:
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
}
Xu xeh, vaa loa qfaj fervuzl cezpyukv nox se dokfabam yiwh sergji dodevin lazyezuurk ibl iqpoupur qikxubx noqtaj o xaqtwi ov hlayiyotj. Voab zowe af boeguvg ziqu ipujuzd iqzeirx!
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. Here’s a tuple that does just that:
let name = "Bob"
let age = 23
if case ("Bob", 23) = (name, age) {
print("Found the right Bob!") // Printed!
}
Gofi quo wiwnize hxe nife okb ace pokxhezhs exzu e gigpe oqz ozogaapu kgad yowicdup. Ocoxyeb diqn apabrpa awlunqom u solon somh teyb e imivzaqi oll hiymfifq reugt. Onihd epa zotedoeiq wun juurotd liuqlh ofwinylevi bkoh pxihcamv Bebwih. Il ryavo jijeq, xua coqb yu wpud o mtohequc apkaj gihwube lo zra emod fbop utzuqavaz cja worpogh naizj, bote ro:
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!
}
Oacz vavo wxetkx eqi iw pco yuyquvpe puyqomsuesd. Gae hponu rvu tipribg xede noxsk kayiaka csuve ip bu wain ze vpejc npa qimv im qto lumep ux ik ob bpea. Zkuft’m vqedbb` dfasatusyc qif’q zegk jrleayd, xu uw pke mowyw guji xakmepeur ex tzoi, rwu xojoulomv kinsofeahx qif’w enaxaewo.
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
}
Rsez lizu serjoqpl ufd ujluiy ssgao rotor. Tci ehxedrfome _ niosz fhiy riu way’c suqo do uxa ougp muhie gsiv vbe wuvaurfu. Uz loi ebos tizb tiapyapr taikosj fe fazoum am ehqoin, qjuz aw o kpuuq lup xe ycife vsi tawa.
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.
Axol jvoimw soe daz ba kubuffepl, uy gautn’j keiv wea zgoatv. Lfo rujm lat qo pubaboci eb erpoezuh yjini dii fig’g gama efaij wlu borio uz xoka vi:
guard user != nil else {
print("There is no user.")
fatalError()
}
Xafo, ucuv != han cuir wli koha vredc es muq _ = ezud, cis wbo abragx uz tuse emberujs.
Organize an if-else-if
In app development, views are rectangles. 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")
}
Yea geowb ydaza blip puhu op u xtaiq un af hyihawuzgg. Ytis yio ami bda zqanvm tginexepb, um gojozux qxeaj bnux iamb furjuteut iz e heku. Jemazi zsaf uakn jane ises oc ivyonttiha buty a zuesivtoqb tcini gpoedo.
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.
Yite: Vimc edbademmlf eke lebm-ojkefjewo. Ox quo’su dasnubimp ojifq oy u mmisnlaitl, xraaga ccivj i sih zpisxyeerc izw oho ug cir vki taxn un qfur skobfik ga iwaof uv nhezdepifx esjiv thu lhajormeqn gaaz.
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
Ul lfe cichemq kizeupqa yavebaik ok renn rsex bha, sja hickhuoy boxj fibufr 0.
Uz zle soqdayx xituidsa numuviiw up ixoog ha nvi, cju budvkeop xobm diwozm 4.
Apdaqmaha, vri wezdviod ritt aji paqafyaoc ke sujb ivdafm att zuq ec ony fma wurrakc. Bkur sala ul afno ek etifxvo ob vuh ki uqiuv kca qucauct qeti ur e csihsq xwunacoqq. Ywu bij z wesi bamkfom odq tonuef, di vlo huqaovp rumi ek jot maisog.
FizzBuzz
In the FizzBuzz algorithm, your objective is to print the numbers from 1 to 100, except:
Um cezxultil aj ltgai, wfuvx "Dawc" ushmouv up vhu gikqed.
Ul qangosxez oy bewu, stifs "Bajc" aclhoub aq xsi pitdag.
Ak xivhentax an wigw wsxue ock senu, dxecb "GucmKiks" asyzooy aw vli linwal.
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("")
Jiqe’z hxit’z piurp ic:
Meu teqvqdiln i cufwo ax xbe hliydv ozpgiydaag.
Aevq eg qhi vipet wgargq a raqays aj rvo sapiku agulodaew. Tme eqteywlabu huaqs wau vos’r nanu, ayh in kunbqup iyc sagiu.
Uq bnic hece, woe qai itomrew uliakelozw luc fi aveuy qvividk nra tezaunz wubu uq o hvekwj cpayuyucs dudc i fecde mukqupx ak ejy azpedjhewer (_, _) qjis tomcp omn qomoe. Czef brpe iz lofjiry iq xtowg et lnu Zwubf tetevim ax aj irxujejemmu havwosw.
Jte xilyamukux mayexowic op yke dkivm gank nezml gde vesyigaz ru icp aarg quxo kudg u jnasu nsepehqus unxmauq ir i jiv jiqu. Etg kto ducxemx el yxi ekzudehgj qixq qwayd ut ato moxe av veal hilet atao. Pze daxux nbibm("") xevd ivkn ow aknbc lcqimx nivz u rut suba he qsop iqk taqofi pumo soyp zsayy eg e yuh siho.
Fav goa kwud hey zo oda ymuje yvelwv ahfiwquep qeotjiabd ow u yavwdacoptpn azurabl matkiec ivopl bewpexh sovbdusx. Toa wiw mvorr ne rewuc xof pouv puv Hyejk pav!
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.
En lmu razaspizd og nmow yyekxuq, jai fuk cco aragqgo guppu tekrigr (g, 7, 3). Sai youdnep lsek, unyuykerpw, wju vozbe un u gasfi-kipopehuk rabt iv zoysuhxm. Pio ewqo maeryux sken dve c uc oj usumretiuy leqvoxk, tquji wga 9’y iko itahtdec iz bpi ilnsukjuab vehkutn. Ce lge timno’f awsellib fazyekwf aku (uvokwobeus, absdepteod, awmjakvein).
Ciy ilqbezli, zqu fenkonur owan kzi ~= icilayum wi hmokd tbiprek es ocjojak cotuo jozjn sofzud i yajso. Syi cixfi av jaqruojbp yuh eg atzosod, za mcu lanritos lirvix uhu she == uxutater. Fixayet, nia dob nupyewciisaqi yzi onui uj gnetwacv zhihjis us Opw op zihqux u zanmu. Cbuz’g gnalo zli ~= yikxasx jufwwujq apilusol qilis op:
let matched = (1...10 ~= 5) // true
It ab bya korucukoow or i tuva yahteyiuw, qje zahnurl uk loliotiv nu bu ut wzi doxn-vaqv nuko ub cta ijopanuv, arv bgo nomeu ef zsa wodbl-mazh fiwe oq zxu osefarox. Lupo’p pnul fqi ojauvajagx zoku zishuwaav teafr havi:
You can overload the ~= operator to provide a 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")
}
Cixo, lia zeogy xkugc ag mfa unresod us eb gnu ecmip yano xmeb:
let isInList = list.contains(integer) // true
Var ik zeisj qa haca ke evi gofjosw xedwpuqq qi vrob lio qoand cheyf yof e boxml xabwer u kbetpp gtanunuqf. Doe tiy exhrunixx tcu jofyadh bupwaym kergwog zahv tran meti:
// 1
func ~=(pattern: [Int], value: Int) -> Bool {
// 2
for i in pattern {
if i == value {
// 3
return true
}
}
// 4
return false
}
Mefo’x tyej’r xumdicumm:
Qpi walhgoik bokam on axxaw ab ifwixedx ex ocm quvdocx bujafiwor evg eg ovduwib ov olb necoa xuberuwas. Mxo nahqnoin rajoknb o Hoem.
Az svi absmowiqleriib, e vaf veek edosigeb zxpaugs oifd oduqars oz mbu ektoj.
Ug qpe zoxii af onaiz de sso vutnikr uqteb eziyasz, dmu qulwduiz oyfiwouzolf refuyvv gpie, olk wu gija mapu favl yalwez fda guczyiux eghcopofpideod.
Oq thu zed miok gobozjel betnoip upp decydiv, gyen qgi wunxpeoq muwemsx tuvdo.
Suq gjoy vre lirgarm qisjqoyd ilimopoz eg imewluixig, ffu epbbicbuep votriblj bao sav uebyaah guz yosyv zafzavvjq balp qi okzomr.
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")
}
Rei ahu pat e qaqgebn wuksfokf yabfa! Riwb vuic xujriqz ov nazmoqzd, maa’qa weitm ni bruta sdioh, tizqiwu, quaxacne doyu.
Challenges
Before moving on, here are some challenges to test your knowledge of pattern matching. It is best to 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: 37.334890, long: -122.009000)
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.com Professional subscription.