6.5 枚举的原始值与相关值
枚举的原始值特性可以将枚举值与另一种数据类型进行绑定,相关值则可以为枚举值关联一些其他数据。通过相关值,开发者可以实现复杂的枚举类型。
6.5.1 枚举的原始值
6.4节中创建的枚举其实并没有声明一个原始值类型,Swift语言中的枚举支持开发者声明一个原始值类型,并将某个已经存在的类型的值与枚举值进行绑定,枚举指定原始值类型的语法与继承的语法有些类似,示例如下:
如果开发者要指定枚举的原始值类型为Int类型,那么可以只设置第一个枚举值的原始值,其后的枚举值的原始值会在第一个枚举值原始值的基础上依次递增,示例如下:
通过枚举类型中的rawValue属性来获取枚举的原始值,示例如下:
//创建枚举变量 var char = CharEnum.a //获取char枚举变量的原始值 "a" var rawValue = char.rawValue
在枚举变量初始化时,开发者可以使用枚举类型加点语法的方式,如果这个枚举有指定的原始值,也可以通过枚举值的原始值来完成枚举实例的构造,示例如下:
//通过原始值构造枚举变量一 var intEnum = IntEnum(rawValue: 1)
需要注意,通过原始值进行枚举实例的构造时,是有可能构造失败的,因为开发者传入的原始值不一定会对应某一个枚举值。因此,这个方法实际上返回的是一个Optional类型的可选值,如果构造失败,则会返回nil。
6.5.2 枚举的相关值
Swift语言在很多方面的设计都比其他编程语言更加灵活与现代,枚举的相关值语法最能够体现这一特点。
枚举类型的设计思路是帮助开发者将一些简单的同类数据进行整合。举一个例子,在游戏类软件的开发中经常会使用到各种物理模型,以形状为例,开发者通常会定义一系列的枚举值进行物理形状的枚举,如圆形、三角形、矩形等,示例如下:
上面的代码进行了形状的定义,但是有一个问题,这种枚举值的定义方式只适合简单数据类型的定义,而不同的形状可能需要不同的参数。例如,圆形需要圆心和半径来确定,矩形需要中心点与宽高来确定,三角形需要3个顶点来确定。如果对枚举类型进行实例化,可以根据不同的形状设置不同的参数,那么在使用时对开发者来说将十分方便,在Swift语言中,对枚举设置相关值就可以完成这样的需求。
在定义枚举值的时候,开发者可以为其设置一个参数列表,这个参数列表被称为枚举的相关值,示例如下:
在创建相关值枚举的时候,开发者需要提供参数列表中所需要的参数,示例如下:
//创建圆形枚举实例,此圆的圆心为(0,0),半径为3 var circle = Shape.circle(center: (0, 0), radius: 3) //创建矩形枚举实例,此矩形的中心点为(1,1),宽度为10,高度为15 var rect = Shape.rect(center: (1, 1), width: 10, height: 15) //创建三角形枚举实例,此三角形的3个顶点为(2,2)、(3,3)、(2,5) var triangle = Shape.triangle(point1: (2, 2), point2: (3, 3), point3: (2, 5))
在switch-case结构语句中,匹配到枚举后,可以通过参数捕获的方式来获取枚举实例的相关值,这里捕获到的相关值参数可以在开发者的代码中使用,示例如下:
6.5.3 递归枚举
递归枚举是Swift语言枚举相关语法中比较难于理解的一个语法,但是如果将其完全掌握,则可以编写出结构十分优美的代码。要完全明白递归枚举的意义与使用,首先需要明白两点——递归与枚举的实质。
递归是一种代码算法技巧,它并不区分语言,各种高级语言都可以实现自己的递归算法。简单来说,递归就是程序调用自身的编程技巧。针对函数来说,递归函数就是在函数内部进行了此函数本身的调用。读者需要注意一点,递归算法效率十分高,但是其性能资源的耗费也十分严重。在大多数情况下,开发者应该尽量避免使用递归。前面的章节中曾经使用过循环结构来计算正整数的阶乘,例如5!=5*4*3*2*1=120,这里我们使用递归算法来计算一个正整数的阶乘,代码如下:
函数的功能是进行数据的计算,递归函数只是使用递归的算法来进行数据的计算。而枚举则不同,枚举的功能是数据的描述。例如6.5.2小节中创建的形状枚举,其中只是对几种形状的数据结构进行描述和定义,并不具备数据计算的功能。递归枚举其实就是使用递归的方式来进行数据描述。
使用枚举描述加减乘除四则表达式的示例代码如下:
使用上面创建的枚举来描述四则运算表达式,示例如下:
//表示表达式5+5 var exp1 = Expression.add(param1: 5, param2: 5) //表示表达式10-5 var exp2 = Expression.sub(param1: 10, param2: 5) //表示表达式5*5 var exp3 = Expression.mul(param1: 5, param2: 5) //表示表达式10/2 var exp4 = Expression.div(param1: 10, param2: 2)
这里读者需要注意,变量exp1、exp2、exp3、exp4只是四则运算表达式的描述,并没有运算功能。可以简单地理解为:Expression枚举模拟的是一种四则运算表达式类型,如果要进行运算,开发者还需要实现具体的功能函数。
可以发现,Expression能够描述的表达式只是单运算表达式,它不能够进行复合表达式的描述,例如对于((5+5)*2-8)/2表达式的描述。分析这类复合表达式,其实质只是将单运算表达式作为计算的参数传入另一个单运算表达式。类比Swift语言中的枚举,一个枚举值的相关值类型可以设置为这个枚举本身的类型,通过这种递归的方式就可以实现复合表达式的描述,将前面创建的Expression枚举修改如下:
使用indirect关键字修饰的枚举值表示这个枚举值是可递归的,即此枚举值中的相关值可以使用其枚举类型本身。使用修改后的Expression枚举来描述复合表达式((5+5)*2-8)/2的代码如下:
最后得到的变量expFinal就是对((5+5)*2-8)/2的描述。另外,读者可以为这个四则表达式枚举类型Expression实现一个函数来进行运算,在开发中将描述与运算结合,能够编写出十分优美的代码。处理递归枚举通常会采用递归函数,函数方法实现示例如下:
关于递归枚举还有一点需要注意,如果一个枚举中所有的枚举值都是可递归的,开发者可以直接将整个枚举类型声明为可递归的,示例如下: