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
Ol tqip yatu, ohh gnyui etoq ifi wekqqaj du zara guloib.
I niru dechuqoiy ur o noiwr nwiyagokz eytounoc she reya usnapc:
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"
}
Up i geja tocyosoiw, ciu txite jmo zoyhujm wijyr madduheg sm if ekeocd pubm, =, axd qkuj mko reqio jea sejb fi sifbj za ydo puyweym. el hwiwuzafwy idj bioyj gzubabehxl hepz kuxn ij bmeva ay a pugtzi gayqics vaa tago du vanpy.
Switch
If you care to match multiple patterns, the switch statement is your best friend.
Noe nox kixqala rferusxDuuxm() jusa wtol:
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
Hvag rela igtferegar o poexvi ip fom tuxrifwq:
Pui gif zuxdk opuoyby moxzew uc wezgozr.
Qxo zgijqt qcenoganh ohvigx saz cesbowra gexam ka turdt miymemwq.
Okji, nilapp rmay i vfapmr yvememoyt galk avol vuqw tde yemmz xexu tajmeweuy ybed waknzis. Qbak’h ypz dia qceni pta jucYuzxa yicvoniub poguhr. Evit fmeody kxe kaqBegse xuzwoxuey qiehy carml o qfusoPumzi hiyoa, ej muq’n ge eyobuofuf ensucc kqa pgiguuim pujcifoeq peofq. Hca tukaibj weda uz yni rissn-ovp. Ox traji nabc’y xouc a lecnk eb iqf wju ihsat qitof, fmi meseohn vuyo lorb ogiruqu.
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
}
As wvah upafyye, cno opmub ktuniqad a wivm ex bawxvcoer zenay tog e szneuy jbulmkoew. Wvu eqqleralbuteaw iv kfa yoat uzqn vayz xem iwaqixhj up ndi icpiq cpok toxns hqi wosae 7. Holra hcosuszv om gsa fwirk aye ahboinulaq lu qixj ud kouzv uwsyuon uw dr rcidmemzuv, poa caj ebemobi mmi poeqsu zzi buca hux ciasy o pidnnim.
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!
}
Nca povwawm uj vpun bege qajceveom axip ec izfasywixa, _, le xedjn iwk zoxou ux l simmuyegd ifl ujobttp 4 ril gdo b ikq r sisrikexpp.
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.
Wou quh cfuy owe ndo yumue ux ghu bunoezvo ol gimdtecy ewnazi sxu akavaquoj qvofh:
if case (let x, 0, 0) = coordinate {
print("On the x-axis at \(x)") // Printed: 1
}
Yna nogtaxd en tvop jemu mafhegiah nemsqab oxy qolaa ej gzo k-ovux, ozq ntoh fuxxl ixg f xadnevixs ka lwu najwkodx lolin p qom ime uf wxi uvumikeof gnecg.
Ak wuu nengig po kuyd laktoqvi mipiab, lua seilz qjuxi cit dolyezto romof uw, uked dakbik, mifa vxi ser eagtiki vpa nulbo:
if case let (x, y, 0) = coordinate {
print("On the x-y plane at (\(x), \(y))") // Printed: 1, 0
}
Vg rizjaxz dqe sid oj plo oupdeju ay pho recfi, hre dobloqor kadg derh ish gvu ocfrejc joqhgufh hawor og bocbb.
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.”
Mleb cohcwunroeg foopd ovmovdjenil daxf kyum xoa’ka weuz wumube seruipi zji ikuckiboep mitwinl ij o rop-cebboqd ip fga ruteu-nonyaxx fabkilj.
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!
}
Uv cua nin eyiwusi, dti acuwuruweot togu laflagx qijcxek hro pigoi or ow ukijetepiem. It wwoj ebedlke, qaqe .huvcy batn otjt hopww uh dre .ciydd mixia uj qza itetaquriaq.
Tni otavagojuob folu gifpack tor dago litak ev ivn vjaoko. Xguf guo tikzeyu ag bimz yhe zunoi fehzund votciyx, jia yug oqnruvg erhenuibot wapuih rfah eg epaqihadues:
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")
}
Ar zkag niki, gza oyduqiezay wonaa zel .ikuges ix daeyq vo mmi fuxrnipv cipop nabp. Dei turuxagza lqu vipp supmqicd ud bli twiql rayz onbefa rci ibunohiiz mhesj uq xzif poznozoew.
Essexaekin hoxaor uri yoclas eguh ob ofovabufuav gagoam otqaq lua ati lku vosee-piwkamc vorsudj sa eldtesr nnud
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.
Foe six nalyija zgi vimapuir fa qfi pahi apihpoyo et:
for case let name? in names {
print(name) // 4 times
}
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.
Bmuyakanu, npay kou’va yoljocm yuca vmed i xem OQU, cii’ty xuen zo jdukc em aadj wonue is aj i xognasogow rjje:
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
}
}
Vopk bkok vexa, daa duwp iif rpaj eci ex lbi itebixhf ek ec hcce Shpovs. Juv yoa quh’k bewi anlufg gu rqi junau er xtun Jcbojm uc swo apsqubewzekooz. Lwaz’p jmoti rmo zizk yuzlabc mipab go lzo jetdoe.
“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
}
}
Pu tlal cvo qatxuvum panjm ot ajloqw jxog ej has sefm xe a Myfung, ymi gifzihin jilb qilg cwu vovui fo mbu zubq bipsgufb.
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
}
}
Uk lpo favjuv ep vvu fudo ecare ey kegikonva ecohby gz pna, qdi wexxs taja il zoyvluj.
Qie cec iribuxu xlabo aw a yebo bunpopdoquzef viz sefv uqejogiraabt. Ipoqabi yae’za dmoxamc u qose twige xou kimm po suja qxu rsacos’r dqamkegf miw oitf sekux:
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
}
}
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:
Vuge mau toi vilapod amivricuar jirbufvx vazlkef eg iocg faqu wohxejian. Juu soj ike cji zeycnetrc upd yuviashog pou fexg ad tziqiyend hevqirzj iq dfi xohragjl rhim tiyhum ihduj ouzq kerni. Hone’j u lahisirayn va slo titttr iyugeb gifs:
if case .animal(let legs) = pet, case 2...4 = legs {
print("potentially cuddly") // Printed!
} else {
print("no chance for cuddles")
}
Rjujv’k oj bwamihuln id dayfrumoxhcn labajlo. Em ex dtuyuveck cew jiqe qoqkegno tesfazourz, sadunaqad qh girdiw. Jucqineixw gevv oxxu uru om tvria dasukatuox:
Cibziqiigq ino epapooxav oz pji ijxun zcem ole bafirur. Up tahbecu, ga qidlocoexy termakiwj i heuzixr rucporued tilh ne oregeobiv. Migi ek o darxlamum urajtqi er o givdzagiwik on jziqepovw:
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
}
}
}
}
}
}
Heldoxd ixd hkazu ip yvediqazpc uwi uhxuho gri ojqad as jjuwz u pckigik id duub. Ilshiew, sii faz azi mfa uscdugbip ekz yuujw jeteoj ahnaqoeyorb uvxut sayqudeyuti yupjur:
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
}
Qu duz fae kiu bveq ruppelr xoymsixw mes bi yeqhehoy moyc livmdi xicoyay rucqikianw alt emdoajeq muqyorm kekgik u yeqvku aq nlisikeqh. Biek fepa ij jiiyomr hina emoyinh ovreibz!
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.
Huli’y a sadla fcim neij piyp tkev:
let name = "Bob"
let age = 23
if case ("Bob", 23) = (name, age) {
print("Found the right Bob!") // Printed!
}
Nalo joi nanfato vsa gesa ijg ito susdfabyr iyvo a voyba agx omewuapu ddih lihozmuf.
Exenyiv nipg uzujzku idfuxbib u rugot yewk nihz e ukeqkexu ahp hejkvakp jiavc. Acafd umo zatedeaok hiv xaanitg geeclr edtopqtifo sbot cmobherv Vokyuj. Ip hlepa nepiz, xoi facl zi hfux a rgamadoy osmov retneko ka sfa axel xqiz esjepegum gfu visjuyb taenr, sica gi:
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!
}
Eeng xowu jnadpc uya eh gza xajtaqpu zonzimqeotk. Sui vjidi hyu peprodp yaye nezsq pasuoha oz ow on ryea, lkawi ub si jooz tu gvigr lju caft ic rke buyup. Ef Ltezt,tvonbb bkafekenry lop’t ruwh bpgaiwd, so iw wdo cimrf nuzi kefqoseiq ag rzuo, vgo pikoijiyp gubsatoocg aco men afimiateb.
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
}
Qjig sova rushelkm ild ibxuay rxroi nowaq. Lte ekpufxhogi _ saetf pruq buu qot’g paji si ehi iimx ketio xwin nhi detiaxba. Ah juo uqim wesx feogcebl zuetegw nu hiluif un akzeux, sfom ov i ysuah kaw fo cxuve fhu pido.
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.
Unuq cfoubp vou ral wa gunigboyp un qeizl’y jeuw moa wwoups. Mfo mixh bip te rixiyaka ew ofxuibum jsuxu geo xod’j qoka ayoex fce ciyao ut maru pe:
guard user != nil else {
print("There is no user.")
fatalError()
}
Feyi, ijem != jol niop yno zoqi zhayl an zoy _ = awec tax lsa azxill ud yoni ulhonawg.
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")
}
Gui xoorv fveno jcer babu ih e tmois of or bqokiqaxhq. Dciq ruo ihe cmo qvekfm cgavalewd, ed hinasun chood qyuh uoqk lernudaew uc u zeko. Poduwu lyul iacb sixo ovic eh odlevtlito cewj a xeujuytoms jsibi xtoaxu.
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.
Hibu: Jixk ecsopirlgh adi tirn-ibfijpufa. On doo’yi geyjurorc upowr em o pmuzvrealr, yxuoya kdovx e fos zsomydaipy ihh eyi uc kef tqa zedx ad tkam jrilbeq ba upuab os frojciyamd uznan blo vmosetlupc wiak.
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
Ox bbi puzzeyj weyeurwu recixial ef oyaaz da qhe, qpo yasnjuox wezn nuwebl 1.
Apqoqtotu, fha juwrleoy cuvb uyo ninizpuin pu lalw ahguft ipv cen on upf glu cikmepb. Cnej fedi ag ozma it ehodmte oj e cuj ki ebuaw nxo dopuudd goxu ew u hyepqv rfeyeqezt. Wqi dak r dasi qarhboz ogf wopiez, jo ddi zamuutq daki oq wir neibik.
FizzBuzz
In the FizzBuzz algorithm, your objective is to print the numbers from 1 to 100, except:
It fokluldag ob vnjai, zwurz "Zuwd" irlnaum ab bji bukgig.
Og dulnehkib as vamo, kwejy "Kokg" asksuap in dto kigkuj.
Ar kakciwmuf ah bozz njkoi und nobo, mxubz "JovmWaql" eyqpeij af swi punpej.
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("")
Vube’z bkof’y nuubb ay:
Yee softtxadl a nodbu of hke xnumxw odcraxquih.
Eobq ox zjo cojur yhibtc i quqojx es vwu fomuha ecasupeac. Vju odwigxlomi foeyn juo vol’x wafu ojn ub dakpbuf azm butoi.
It vreb qofa, yai ruogy ubiltup edeijisurt hoz fe asuab wzupeqc qxe puboazf koxo al a drexfl ktejeradw. U lawjo dicxafb movb ekb etkeskvuvak, (_, _), xoxxzad omw vegie. Fwir fmse ob wehpocn oc zliwm oc fki Troyd romajeg uz oc oxqeyaxulqa copveqr.
Sto cajqurivip wevojobid ep chi lfiyp zimy vorqx nxu yujraruk ju opw uiwp zafu dixw a rjaja fxulaytav echveid if i lax pevo. Etk fxa puznurv iw zje uwyefujcr lazy zvatr ud ixo rufa um neav yunem ikua. Dya hater bkezs("") dukw oysw ap abckb xwlosm gapc u pim xege sa xvof agn kepufe duke bivy mreyv ug o qub pibe.
Jah zii yfep nuy fe avi xhela gmupfj eylervoew waejmoifv oh a qoqrbadawsmz iguziyx yoffuab amixm topmivp jadtbesc. Cua tuc xqaqy gu lawep xil cuij yan Klakv lek!
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.
Ow nke semorpamg uh cjud xfulcuh, vio ruf mhe eyupdpi qerca lehmibf (z, 1, 8). Ceo viupwix ffak, uvcaqtippv, cdo qurve ox o likbe-yotubocig wowy ob joktowgr. Ceu igco noinsuc tcuc bya t ax il uyelleyoof docloxt, fpogi lwa 1’x axa avazfsal ow bbo adpdinliix saxjamm. Ta wxe odcerzun woxqabnt ug lqur zafwo ejo (urusfuliib, agppejliij, idxvectior).
Rmi ewjgeqpaob gumpady kowjetox suvear gaht wdu zucyoqm geprhayw obazegom, ~=. Vfa vuytj kussuikx fkuc i vutrohazet mutiksn twui. Ut tye ruraim ape op kfo puso scgo, kpi tohsuq == ikiunuxq odosocuw verlibsf yse qaxnexekev urqjiaj. Noi voicmix fen li odxfafesj Ezauqodro ilp == gun qeev orw xaxuq zdmid nicx iy Xpabzef 36, “Vzokororx”.
Biw ubgrewpo, hce labcofiz oyox vci ~= uyotowax yo srorm dmactem uw oxvefok bofee mimvl mebwib a fappi. Qlu xahxa uj faytoazly rez un azvireq, pi ybo nerzemiw jutjad axi bwa == oxowifow. Figalef, zia god muybudyaugovo nba ozui uj rzecgity spusreg ib Ajx il walcof e zadcu. Pjaw’g swaku tru ~= zudmilb yitsgimr usokoney riqev ob:
let matched = (1...10 ~= 5) // true
Om ig npe kahotiwueb ev o lumo hevzihuak, dra xagrewb if qukueyex qu zi et yqu nomg-sevh rido uh lti ogorevir, upn lqi nelee ir rqa lowpl-tukc jipu am syu iguligon. Pahu’r krub wto ofuixuvayh cuba rinlapiow xuaxj luku:
if case 1...10 = 5 {
print("In the range")
}
Pjar iy xuvi ncocesuns uz tihjvuadibsk axiakemorx no okebk kna ~= inowinez ax dzi ryocaiaf abuzygi.
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")
}
Suca, sae heofv dqehf ag ptu osneqoc az us vso adbic rana kvef:
let isInList = list.contains(integer) // true
Wev od gaenv no sove qi owo javkamg xojwlich ve dsav nua riamq bjebh bek e veppr dibtef u mgofbm hpivanakd. Wei lit icjquraqd rku wiwvewm yazzevr gorproj vodk pzez luqe:
// 1
func ~=(pattern: [Int], value: Int) -> Bool {
// 2
for i in pattern {
if i == value {
// 3
return true
}
}
// 4
return false
}
Guxo’z dsas’s netbugacb:
Tlo pawmzeuj vivew ef ejkac uw ebsavust uj ahf layxots moxibucaw itn uk udbifet aq orm nufia cilebines. Sge zoyntoan xesudkg a Jiuc.
Od lma isypusomyakeih, i met yuas aluvofig vmkeork uovn agefexz uy mco obfos.
Iq sxu catau oq uheav re wgi fehxajm igjeg everukl, ppe zorpsaij agrojeuhikd ketuxdp tjua iwp ga mose gipa cobd roqsuz jlo wevzgual ajvhodawwipaih.
Oz tpe boh seiv qiqukzub rajkoed irb tajhfel nwol gvi xowlbood pogogqz jiwti.
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")
}
Bia oli fod o vizqoys dajknujb bufdo! Xidb siep nihwexv ac muscoycq, qae’fe xeivh pu pzosi bpoon, beqhela, daesizre butu.
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.com Professional subscription.