-
作为一个类型,可以实现协议,定义扩展
-
实现递归枚举,可以很方便的表示类似列表或者树的结构,配合递归函数降低理解成本。
这些特性官方文档都有详细的说明,这里就不展开了。
Swift中的枚举和OC枚举的相同之处就是名称一样,内涵和外延已经完全不同,其核心价值在于提供了在多种类型(而非仅仅整型)间做选择的机制,在将这种机制的实现细节与对外抽象分离的同时,赋予其上述所有的能力,这提供了更高的抽象层次和更强的表现能力。
接下来我们来看一个简单的例子和一个综合应用的例子。
牛刀小试——简化错误处理
之前,函数的正常结果和异常结果是无法统一的,体现在代码上往往以类似if(someValue !=nil) {} else {}
的方式来处理,有了枚举之后,可以利用Optional将两种结果进行抽象统一,从而简化错误流程。下面这个例子的作用是从一个json字符串实例化为模型对象:
正常的代码:
func makeModel1(s : String?) -> Model?
{
let d1 = s?.data(using: String.Encoding.utf8)
// 判断是否为空
if let a = d1 {
// 可能throw的代码try一下
guard let dict = try? JSONSerialization.jsonObject(with: a) as? Dictionary<String, String> else {
fatalError(“Json data error”)
}
let model = Model.init(dict: dict)
return model
}
return nil
}
利用Optional简化后的代码
func makeModel2(s : String?) -> Model?
{
s?.data(using: String.Encoding.utf8).map{
try? JSONSerialization.jsonObject(with: $0)
}.map {
Model.init(dict: $0 as! Dictionary<String, String>)
}
}
这个逻辑并不复杂,但还是足以显示出合理抽象的威力,Optional提供了这样一种抽象,将错误处理的细节屏蔽掉了,我们只需要写数据转换的主逻辑,这种写与读的顺畅性在带给我们愉悦的同时,也提升了程序的正确性。在实际场景会遇到更复杂的多层嵌套各种if的代码,主流程非常不清晰,各种错误处理很容易出错,这个时候非常适合用Optional来简化。第二个版本是为了优化了可读性故意变成了多行,实际上只有一行代码
func makeModel2(s : String?) -> Model?
{
s?.data(using: String.Encoding.utf8).map{try? JSONSerialization.jsonObject(with: $0)}.map { Model.init(dict: $0 as! Dictionary<String, String>)}
}
综合应用——关联值、递归、模式匹配、方法定义、与高阶函数结合
在swift中,枚举有几个新的能力,比如可以关联值,支持递归,进行模式匹配,定义方法,正是这些能力使得枚举实现了能力的进击,我们通过一个例子来展示一下这些特性如何让我们的表达更方便。
最近笔者在设计一个表达式引擎的方案,在一开始研究了一个非常简单的表达式系统,即有理数域的加减乘除,这里笔者组合了模式匹配、关联值、方法定义、递归枚举的能力,用很少的代码进行了抽象,并将这个四则运算的表达式系统用于计算经典的游戏算法——24点,24点的游戏抽象成算法问题是这样的:
给定任意4个1-10内的整数,计算出它们都过加减乘除得出24的所有表达式
在继续往下之前,读者可以思考一下使用OC应该如何解决。
接下来我们开始,假如4个数字是1,2,3,4,我们知道(1+2+3)*4=24
,考察(1+2+3)*4
,这个可以看成一个如图所示的运算
这张图很直观的体现了3个信息:
-
表达式可以体现为树的形式,而树是很适合用递归来描述的;
-
表达式的节点分为两种,一种是数字,一种是运算,并且每个运算必定关联两个节点
-
表达式的求值过程式是递归地将运算应用到两个节点的过程
因此我们可以利用枚举关联值、递归的能力建立表达式的模型,并且利用模式匹配定义求值的方法:
enum Exp {
case value(Double) //值节点
indirect case plu(Exp, Exp) //加法
indirect case sub(Exp, Exp) //减法
indirect case mul(Exp, Exp) //乘法
indirect case div(Exp, Exp) //除法
// 求值,模式匹配
func eval() -> Double {
switch self {
case .value(let value):
return value;
case .plu(let exp1, let exp2):
return exp1.eval() + exp2.eval()
case .sub(let exp1, let exp2):
return exp1.eval() - exp2.eval()
case .mul(let exp1, let exp2):
return exp1.eval() * exp2.eval()
case .div(let exp1, let exp2):
return exp1.eval() / exp2.eval()
}
}
}
一个四则运算表达式的建模和求值就完成了。如果是利用class定义,需要定义一个类似Type的枚举来表示加减乘除,这就需要多写很多代码,更重要的是,加减乘除和具体的Double值之间的处理会比较繁琐,而对于枚举来说,就像呼吸一样自然。
那么怎么用这个Exp的枚举解决24点问题呢,这里一共有两大步:
- 将4个数字表示为可能的表达式数组,这一步又分成两小步:
获取4个数字的全排列
针对每一种排列方式,在每两个相邻的数字中间插入不同的运算符号
- 对数组中的表达式求值,取其中结果为24的表达式
限于篇幅,这里直接给出代码和注释
// 查找4个数字的24点答案
func search24(_ nums:(Double, Double, Double, Double)) -> [Exp] {
return makeExpFromNums([nums.0,nums.1,nums.2,nums.3]).filter{ $0.eval() == 24}
}
// 将一个数组递归构造成Exp的数组
func makeExpFromNums(_ nums:[Double]) -> [Exp] {
if (nums.count == 0) { //返回空数组
return []
} else if (nums.count == 1) { // 返回值
return [.value(nums.first!)]
} else { // 1、排列,2、分割数组,3、对分割后的两部分递归构造Exp,4、对递归的两个Exp数组组装成最终的Exp数组
return permutation(nums).flatMap{partition($0).flatMap{makeExpFromExpArray((makeExpFromNums($0.0), makeExpFromNums($0.1)))}}
}
}
// 构造4个数字的全排列,这是个递归函数,将第一个元素插入到后续元素的全排列中即可
func permutation(_ nums:[Double]) -> [[Double]] {
if (nums.count == 0) {
return [[]]
}
return permutation(Array(nums.suffix(from: 1))).flatMap{insert(num: nums.first!, nums: $0)}
}
// 给点一个数组nums,有nums.count+1的插入点
func insert(num:Double, nums:[Double]) -> [[Double]] {
(0…nums.count).map{nums.prefix(upTo:$0) + [num] + nums.suffix(from:$0)}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
![img](https://i-blog.csdnimg.cn/blog_migrate/1def444cf2202fb1003c6c5612d3efca.jpeg)
最后
2020年在匆匆忙忙慌慌乱乱中就这么度过了,我们迎来了新一年,互联网的发展如此之快,技术日新月异,更新迭代成为了这个时代的代名词,坚持下来的技术体系会越来越健壮,JVM作为如今是跳槽大厂必备的技能,如果你还没掌握,更别提之后更新的新技术了。
更多JVM面试整理:
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
后
2020年在匆匆忙忙慌慌乱乱中就这么度过了,我们迎来了新一年,互联网的发展如此之快,技术日新月异,更新迭代成为了这个时代的代名词,坚持下来的技术体系会越来越健壮,JVM作为如今是跳槽大厂必备的技能,如果你还没掌握,更别提之后更新的新技术了。
[外链图片转存中…(img-AQEg5kcN-1712693391560)]
更多JVM面试整理:
[外链图片转存中…(img-2OTw45RN-1712693391561)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!