Nested Generics and Recursive Enum
Introduction
Welcome to the first lesson of Advanced Swift. So far, you’ve learned how to create generic structs, classes, enums, and functions. Today, you will learn how to add generics within generics. Next, you will learn the meaning of indirect
enums using the binary data structure.
Problem
- Learn how to created nested generic enum
- Learn more about recursive enum with binary tree
Nested Generics
Let us take your game to the next level with generics.
Before Swift 3.1
To appreciate nested generics, let us look into your/my past.
Design Generic Enums
Create two generic enums: Gadget
and Company
.
enum Gadget<T> {
case smartphone
case laptop
case fridge
case others(T)
}
enum Company<T> {
case Samsung
case Apple
case Sony
case others(T)
}
Design Struct
Create a struct called, Product
. It contains three properties: company
, gadget
, and reviews
.
struct Product<T> {
let company: Company<T>
let gadget: Gadget<T>
let reviews: [T]
init(enterCompany: Company<T>, enterGadget: Gadget<T>, enterReview: [T]) {
company = enterCompany
gadget = enterGadget
reviews = enterReview
}
}
The init method seems unpleasant. Let us attempt to initialize.
let myProduct = Product(enterCompany: .Apple,
enterGadget: .fridge,
enterReview: ["Good, silver, but expensive"])
There must be an alternative.
Swift 3.1
Now, you may add a generic type within a generic type. Create a generic struct, Team
. Within the struct, it also contains another generic structure, TeamMember
.
struct Team<T> {
struct TeamMember {
let name: T?
let age: T?
}
}
Let us use nested generics using the Product
example.
struct NestedProduct<T> {
indirect enum Gadget {
case smartphone
case laptop
case fridge
case others(T)
}
indirect enum Company {
case Samsung
case Apple
case Sony
case others(T)
}
let company: Company
let gadget: Gadget
let reviews: [T]
init(enterCompany: Company, enterGadget: Gadget, enterReView: [T]) {
company = enterCompany
gadget = enterGadget
reviews = enterReView
}
}
Important: The
indirect
keyword is needed in front ofCompany
andGadget
due to a bug. You will learn more about the meaning ofindirect
soon.Cyclic Dependency: A relation between two or more modules which either directly or indirectly depend on each other to function properly. The tight coupling of the mutually dependent modules which reduces or makes impossible the separate re-use of a single module.
Let us initialize using the generic enums
let myFridge = NestedProduct(enterCompany: .Apple,
enterGadget: .laptop,
enterReView: ["Good"])
You are following the principle: write less, produce more.
Recursive Enum
Before I introduce you to the meaning of indirect
, let us take a look the characteristic of enum associated value.
First, let us design blueprints using class
, struct
, and enum
.
class BobClass {}
struct BobStruct {}
enum BobEnum {
case bobCase
}
Let us initialize
let bobClass = BobClass()
let bobStruct = BobStruct()
let bobEnum = BobEnum.bobCase
Create an enum, MyEnum
, with associated value.
enum MyEnum {
case myClass(BobClass)
case mySruct(BobStruct)
case myEnum(BobEnum)
}
Let us initialize.
MyEnum.myClass(bobClass)
MyEnum.mySruct(bobStruct)
MyEnum.myEnum(bobEnum)
Let us visualize.
Notes: The associated value of
myStruct
andmyEnum
are stored whilemyClass
is referenced somewhere in the cloud.
The Meaning of indirect enum with Binary Tree
Let us create an enum called, Tree
. There are two cases, either Empty
or Node
. The Node
case has associated value.
indirect enum Tree {
case Empty
case Node(value: Int ,left: Tree ,right: Tree)
}
Let us create a couple nodes.
let tree1 = Tree.Node(value: 1, left: Tree.Empty, right: Tree.Empty)
let tree2 = Tree.Node(value: 2, left: Tree.Empty, right: Tree.Empty)
let tree3 = Tree.Node(value: 3, left: tree1, right: tree2)
Let us visualize
Let us add two more nodes
let tree1 = Tree.Node(value: 1, left: Tree.Empty, right: Tree.Empty)
let tree2 = Tree.Node(value: 2, left: Tree.Empty, right: Tree.Empty)
let tree3 = Tree.Node(value: 3, left: tree1, right: tree2)
let tree4 = Tree.Node(value: 4, left: tree3, right: tree5)
let tree5 = Tree.Node(value: 5, left: Tree.Empty, right: Tree.Empty)
Let us visualize
Important:
indirect
enums no longer store associated value. It references. For instance, the associated value oftree4
has a reference totree5
andtree3
.tree3
has a reference totree1
andtree2
.
Recursive Function to Sum Values
Let us calculate the sum of every tree from the top using a recursive enum.
func evaluateTree(tree:Tree) -> Int {
switch tree {
case .Empty:
return 0
case .Node(let value, let left, let right):
return value + evaluateTree(tree: left) + evaluateTree(tree: right)
}
}
Let us test.
evaluateTree(tree: tree1) // 1
evaluateTree(tree: tree3) // 6
Important: You may have to dissect the function multiple times to understand. I recommend you to start using a smaller node.
Source Code
8001_nested_generics_recursive_enum
References
Generic struct crashes because of “cyclic metadata dependency
Conclusion
There were two objectives. First, you’ve learned how to design beautiful code with nested generics. Second, you’ve learned the difference between normal enums and indirect
enums when it comes to associated value. Remember, indirect
enums point rather than store. Some may be baffled by the recursive function to add the value of all nodes. Well, I can’t help you much. I recommend you to start with one node and move up.
In the following lesson, finally, you will learn how to create custom operators.
Note: Learn Swift with Bob is available on Udemy. If you wish to receive a discount link, you may sign up here.