Swift 与 OC 枚举的异同

  1. 作为一个类型,可以实现协议,定义扩展

  2. 实现递归枚举,可以很方便的表示类似列表或者树的结构,配合递归函数降低理解成本。

这些特性官方文档都有详细的说明,这里就不展开了。

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,这个可以看成一个如图所示的运算

257e6fec6f34ab1070875cbfc6f25d5c.png

这张图很直观的体现了3个信息:

  1. 表达式可以体现为树的形式,而树是很适合用递归来描述的;

  2. 表达式的节点分为两种,一种是数字,一种是运算,并且每个运算必定关联两个节点

  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点问题呢,这里一共有两大步:

  1. 将4个数字表示为可能的表达式数组,这一步又分成两小步:

获取4个数字的全排列

针对每一种排列方式,在每两个相邻的数字中间插入不同的运算符号

  1. 对数组中的表达式求值,取其中结果为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开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

2020年在匆匆忙忙慌慌乱乱中就这么度过了,我们迎来了新一年,互联网的发展如此之快,技术日新月异,更新迭代成为了这个时代的代名词,坚持下来的技术体系会越来越健壮,JVM作为如今是跳槽大厂必备的技能,如果你还没掌握,更别提之后更新的新技术了。

更多JVM面试整理:

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

2020年在匆匆忙忙慌慌乱乱中就这么度过了,我们迎来了新一年,互联网的发展如此之快,技术日新月异,更新迭代成为了这个时代的代名词,坚持下来的技术体系会越来越健壮,JVM作为如今是跳槽大厂必备的技能,如果你还没掌握,更别提之后更新的新技术了。

[外链图片转存中…(img-AQEg5kcN-1712693391560)]

更多JVM面试整理:

[外链图片转存中…(img-2OTw45RN-1712693391561)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 25
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值