Enums and Associated Values
In previous raw value examples we either let the computer infer certain values (If Kershaw is 1, Maeda is 2) or we assigned specific values (case Kershaw = 0.579). But what if we know what kinds of types we'll need (Strings, Ints, Bools, etc), but don't have the exact values yet? Then we can use associated values.
Let's say we want to breakdown Kershaw's pitches to Posey by pitch type. Here's an example:

What's going on here? First, we create an enum called KershawPitch and a case for each of his three pitches, Fastball, Curveball, and Slider. Then we setup our associated values for each pitch. In this case, we'll focus on the number of each pitch he throws (that is, the count) and the average velocity of that kind of pitch (averageVelocity). That's how to setup associated values for an enum.
To access these associated values, we'll use pattern matching which we'll address along with other popular patterns in a future post.
Recursive Enumerations
First off, what does recursive mean?

In the same way that an infinite loop can crash a program (or a computer!), something that is recursive can also repeat itself indefinitely to the detriment of your program, namely by hogging up an undetermined amount of memory. Fortunately, there is a way to avoid this through recursive enumerations.
Why are recursive enums important?
Statistics in baseball give us a good idea of what to expect from players. That is, until they put up numbers that seem odd compared to the rest of their stats. Recently, Ivan Nova put up numbers with the Pirates that were significantly better than the ones he put up with the Yankees earlier that same year (2016).
After eight great starts with the Pirates (ERA under 3.00, WHIP under 1.00), I picked Nova up for my fantasy baseball team against the Reds. In three innings the Reds lit Nova up for 10 hits and four earned runs. I was ready for certain good numbers (ERA under 3.00, WHIP under 1.20) and certain bad numbers (4.50 ERA, 2.00 WHIP), but as the Reds hit around I wondered, "When is this going to stop? Or are the Pirates going to hang him out there to dry even if he gives up 10 runs?" Not good! Fortunately, they pulled him after three innings to stop the bleeding.
In terms of coding, for each app that you write there is a Swift compiler. Among other functions, the compiler reviews the code you've written to determine how much memory that code will require. When it comes to enums, the compiler reviews the enum's different cases and assesses how much memory each case will require. Sometimes the compiler finds cases that require an uncertain amount of memory; an uncertain amount that may be very large leading to slow load times and performance. As an example, let's consider the line of closers for a team over time.

So we've set up our LineOfClosers enum with two cases; a case where we remember who the predecessor is and a case where we don't remember who the predecessor is. Seems harmless enough. But we already have an error. The error, "Recursive enum 'LineOfClosers' is not marked 'indirect'". This doesn't explain what is wrong so much as how to solve the error which we'll get to in a minute.
So let's talk about why Xcode throws the error in the first place. Like Ivan Nova's expanding ERA versus the Reds above, Xcode feels good about how to allocate memory for KnownPredecessors that have a name which is a string, but it doesn't feel good about the predecessor which is a LineOfClosers data type. Why? Because Xcode just sees a black box and has no idea what is in it.
Unfortunately, neither do we, but there is a way to handle the issue. To solve the problem we use a pointer which is a form of indirection. Here's how The Big Nerd Ranch Guide to Swift Programming explains it, "How does using a pointer solve the 'infinite memory' problem? The compiler now knows to store a pointer to the associated data, putting the data somewhere else in memory rather than making the instance of [LineOfClosers] big enough to hold the data. The size of an instance of [LineOfClosers] is now 8 bytes on a 64-bit architecture - the size of one pointer." In other words, we make the uncertain certain by defining its size as 8 bytes rather than an unknown amount of bytes.
Ok, so how do we make use of this indirection in our example? We do it by using the indirect keyword like this:

In fact, we can get a little more precise by marking individual recursive cases as indirect like this:


Let's play around with filling out the YankeeClosers enum.

Dellin Betances is the Yankees' current closer. Before him came Andrew Miller, Aroldis Chapman, David Robertson, Mariano Rivera, Rafael Soriano (the year Mo was injured), Rivera, and John Wetteland, and then I don't remember off-hand so we assign ".NoKnownPredecessor" after Wetteland.
No comments:
Post a Comment