[Swift] Shared enumeration type

1.First of all


Last time I explained about enums, but this time, as I told you in advance, I will explain about shared enums.

If you haven't read it yet, please see this article as it is very closely related to the previous article. (https://qiita.com/0901_yasyun/items/a87c49438db68241778b)

2. Overview of shared enums


A shared enum is a type that can have both a simple enum that does not specify a substantial type and a structure of multiple different tuples. This type aims to be able to handle several different types of data together with a common concept.

First, here's an overview of the definition of shared enums: It is a simple enumerated case name followed by a tuple declaration.

enum type name{
case Case name tuple type declaration
case Case name tuple type declaration
    ... ...

The following is a concrete definition example. In this example, the color used for creating a Web page etc. is specified by the color name of the character string, the color code by hexadecimal number, etc., but since the purpose of specifying the color is the same in all formats, this Consider expressing with one data type.

python


enum WebColor {
    case name(String)       //Color name
    case code(String)       //Hexadecimal color code
    case white, black, red  //Frequently used colors
}

You can use this definition to create various instances.

python


let background = WebColor.name("indigo")   //Indigo blue
let turquoise: WebColor = .code("#40E0D0") //Turquoise
let textColor = WebColor = .black          //black

** Information described in a tuple-type structure ** is referred to as additional information or payload below. You cannot write a substantial type in the definition of a shared type. The value type and the shared enumeration must be described as separate.

In the previous simple enum and value enum, we were able to compare elements, but in the case of shared enum, you can either define the == operator yourself or Unless the protocol Equtable is adopted, ** cannot be compared with each other **.

python


if turquoise == WebColor.code("#40E0D0")  //error. Can't compare

Here's a slightly more complicated example. In the following example, we assume that any of the several types of tickets and prepaid cards sold on the municipal subway can be processed by automatic ticket gates, and define the types of tickets and cards as enumerations.

python


enum Ticket {
case ticket(Int, Bool,Coupon:Bool)  //Ordinary ticket: Price, child, whether it is a coupon ticket
case card(Int, Bool)             //Prepaid card: balance, dwarf
case Keiro Bus//Keiro Bus
}

The parentheses in the case are tuples, and you can also add keywords to the items. Use the switch statement to separate the processing for such enumerations. If you can assign a value to a constant or variable in case and do not use additional information, you can also do not write ** after parentheses ** like the" .card "below **.

In the following, the description of the structural condition ** placed after the case ** is called the case pattern. Also, the condition can be written under where is the same as when dealing with tuple. Check the following example.

python


switch t {
case let .ticket(fare, flag, _):
    print("Ordinary ticket: \(fare) " + (flag ? "child" : "grown up"))
case .Keiro Bus:
    print("Keiro Bus")
case .card(let r, true) where r < 110:  //Minimum fare for children
    print("card:Insufficient funds")
case .card(let r, false) where r < 230: //Minimum fare for adults
    print("card:Insufficient funds")
case .card:
    print("card")

Similar to using tuple in a switch statement, you can use let to be placed inside () or outside to assign additional information to a constant.

python


case let .ticket(fare, flag, _):     //Or
case .ticket(let fare, let flag, _):

For tuples with keywords, you don't have to write a label inside the case pattern, but you can. When using with let, you need to be careful where you write let.

python


case let .ticket(fare, _,Coupon: flag):     //Or
case .ticket(let fare, _,Coupon: let flag):

3. About if-case statements


A switch statement was useful to find out which of the multiple conditions ** a given tuple matches **. However, there may be times when you want to ** check if only one particular condition is met **.

For example, suppose you just want to find out if the Ticket type variable p used in the previous example is a prepaid card. However, the if statement cannot be described as follows.

python


if p == .card{              //error. This description is not possible
    print("prepaid card")
}

A switch statement can be written as follows, but it is a little troublesome because you have to write a default clause that you know you will not use.

python


switch p {
case .card: print("プリペイドcard")
default: break
}

Therefore, there is a syntax that can be easily written even in such cases. The above example can be written as: This syntax is sometimes called the ʻif-case statement`.

python


if case .card= p {
    print("prepaid card")

For the condition, first describe the case pattern, and then put the target expression as" =". Whether the expression matches the case pattern is handled in the same way as in a switch statement. Unlike the switch statement, the additional condition is not a where clause, but a comma "," and follows.

This notation is the same as described in How to write conditions for if statements, while statements, and guard statements, and you can write general expressions, optional binding syntax, etc. by separating them with commas.

if case pattern=formula,conditions{.....}

The following example checks if the content of the variable t is a prepaid card with a balance of 1200 yen or more.

python


if case .card(let y, _) = t, y >= 1200 { .....

As a slightly more complicated example, let's write a condition that takes a Ticket type element from an instance of a dictionary and checks whether the information matches the pattern.

python


let tickets:[String:Ticket] = [ "Shikura" : .ticket(260, false,Coupon: true), 
               "Sasaki" : .ticket(220, false,Coupon:false)]
let name = "Sasaki"
if let t = tickets[name], case .ticket(220, _, _) = t {
    print("220 yen ticket")
}

In the case of this example, the information obtained from the dictionary is optional type, so first get an instance of Ticket type in the constant t with optional binding syntax. In the case of nil, the if condition is not satisfied there, but if it is not nil, then check whether the constant t is a ticket of 220 yen.

The above if statement can also be described as follows by using "?".

python


if case .ticket(220, _, _)? = tickets[name] {
    print("220 yen ticket")
}

4. Use the case pattern in the for-in statement


You can also use the case pattern in a for-in statement. Similar to the for-in statement explained so far, an expression conforming to the Sequence protocol such as an array is placed next to in, and instances are fetched one after another.

In this format, the code block is executed only when the retrieved instance matches the following pattern of the case. You can also add a where clause if you need to write more conditions for execution.

for case pattern in expression where expression{
Sentence... ...
}

For example, to retrieve instances one after another from an array passes with Ticket type elements and display ticket information higher than 220 yen, do the following.

python


for case let .ticket(fare. child, coupon) in passes where fare > 220 {
    var k = coupon ? "Coupon" : "Ordinary ticket"
    if child { k += "(child)" }
    print(k, "\(fare)Circle")
}

This statement is the same as writing:

python


for t in passes {
    switch t {
    case let .ticket(fare, child, coupon):
        if fare > 220 {
            var k = coupon ? "Coupon" : "Ordinary ticket"
            if child { k += "(child)" }
            print(k, "\(fare)Circle")
        }
    default: break
    }
}

You can use the "?" That applies to the optional type in this syntax to handle data strings that contain the optional type. You can also use var instead of let.

5. Recursive enums


You may want to specify yourself as an element inside a tuple with a shared enum. For example, consider the following case.

python


enum message{
case document(String, String)     //From, document
case data(String, [Int8])   //From, data column
case transfer(String,message)  //From, message
}

This data declaration does not seem to be wrong, but Swift can't handle it.

In Swift, enumeration is value type data, so to explain intuitively, load information is stored directly side by side in the memory location that represents enumeration data. As mentioned above, including itself, that is, ** recursive data structures cannot determine at compile time how much memory is needed to store the data **.

On the other hand, reference type data is information for indirectly referencing an entity that exists somewhere in memory, so there is no memory problem.

However, there are many situations where it is useful to be able to use a recursive enum, so in Swift if the enum includes ** itself in the additional information, write the keyword indirect before the corresponding case **. This is called indirect specification. ʻIndirect`, that is, indirect means that only that part is dedirected like a pointer.

The current story is shown in the following example. The CustomStringConvertible protocol is adopted so that it can be displayed by print.

python


enum message: CustomStringConvertible {
case document(String, String)              //From, document
case data(String, [Int8])            //From, data column
indirect case transfer(String,message)  //From, message
    
    ver description: String {
        switch self {
        case let .documents(from, str): return from + "(" + str + ")"
        case let .data(from, _): return from + "[data]"
        case let .transfer(from, msg): return from + "←\(msg)"
        }
    }
}

An execution example is shown below.

python


let m1 =message.documents("Ito", "Take a rest")
let m2 =message.transfer("Shiroishi", m1)
let m3 =message.transfer("Yamada", m2)
print(m3)   // "Yamada ← Shiraishi ← Ito(Take a rest)"Output

If you have some recursive case patterns, you can put ʻindirect` in front of the enum to make the whole indirect specification.

6. Conclusion


This time I wrote an article about shared enums, but I will not explain how to handle protocols and optional types because the article will be a little long. From next week onwards, I will explain about classes and so on. Thank you to everyone who has read this far.

Recommended Posts

[Swift] Shared enumeration type
Enum (enumeration type)
[Java] Enumeration type
[Swift] Type type ~ Structure ~
[Swift] Type components ~ Type nesting ~
[Swift] Type component ~ Subscript ~
[Swift] Type component ~ Initializer ~
[Swift] About enumeration types
[Swift] Type type-Class sequel-
[Swift] Type component ~ Method ~
[Swift] Summary about Bool type
[Swift] Type type-Class first part-
[Swift] Type component ~ Property Part 2 ~
Great Swift pointer type commentary
[Java] Express Enum type without using Enum (enumeration) type
[Swift] Converts a UInt64 type integer to [UInt8]
Try to sort classes by enumeration type