虽然现在swift语言已经发展到了2.0版了,但是相信很多学习iOS开发的童鞋仍对swift语言存在各种各样的疑问,今天小编将为大家详细介绍swift中的范围和区间,下面我们一起来看看吧。

Ranges

在swift语言中,范围是用 Range 类型表达的,一个范围就是一个索引集合。

其中,值得注意的是Range在标准库中使用很频繁,特别是处在集合的上下文当中时。当我们查看 Range 定义时,范围和集合之间的紧密关系一目了然:

struct Range<Element : ForwardIndexType> : CollectionType, Indexable, ... {

...

}

在一个范围中的元素必需遵守 ForwardIndexType 协议,同时 CollecitonType 协议中的大量功能也是基于它实现的。有一个特殊的类型用来表示集合索引的范围,对于获取一个集合的子集是相当有意义的。例如,我们可以使用范围获取一个数组的部分:

let numbers = [1,2,3,4,5,6,7,8,9]

// 1..<5 等价于 Range(start: 1, end: 5)

numbers[1..<5] // [2,3,4,5]

正如类型定义中所看到的, Range 自身遵循 CollectionType 协议,所以几乎所有数组可以做的事情,范围也能够适用。比如用 for 循环遍历元素,或者使用 contains(_:) 检查一个值是否在这个范围内。

虽然范围主要适用于与其他集合配合使用,但谁也无法阻止你创建一个用于表示数字区间的 Range<Int> 。毕竟 Int 已经实现了 ForwardIndexType 协议。现在回到模式匹配问题。

我们可以用一个范围 (Int.min..<0).contains(x) 表示 x < 0 的情况,这是完全等价的,不过执行速度巨慢。毕竟默认需要遍历整个集合,最糟糕的情况下,将执行 9,223,372,036,854,775,808次 ,这相当耗费资源。我们可以为 Comparable (比如 Int )类型的索引提供一个更好实现:

extension Range where Element : Comparable {

func contains(element: Element) -> Bool {

return element >= startIndex && element < endIndex

}

}

(Int.min..<0).contains(-1) // true

(Int.min..<0).contains(0) // false

(Int.min..<0).contains(1) // false

这是一个非常好的练习,不过在我们案例中可有可无,因为 ~= 操作符为 Range 实现的匹配足够高效(就像我们的 contains(_:) , Comparable 只是在索引中工作)。所以我们可以这样的做:

Int.min..<0 ~= -1 // true

Int.min..<0 ~= 0 // false

Int.min..<0 ~= 1 // false

在这基础上,可以写一个 switch 语句,使用范围查询判断一个数字是否大于,小于还是等于 0,对吗?不幸地是,这并不适用。这段代码会崩溃:

let x = 10

switch x {

case 1...Int.max: // EXC_BAD_INSTRUCTION

print("positive")

case Int.min..<0:

print("negative")

case 0:

print("zero")

default:

fatalError("Should be unreachable")

}

我们会在 case 1...Int.max 这一行中得到一个 EXC_BAD_INSTRUCTION 错误信息表明“fatal error: Range end index has no valid successor”。导致错误的原因在于: range 中的 endIndex 总是指向范围中最后一个元素的后面。这对于半开区间(用 ..< 操作符创建)和闭合区间(用 … 操作符创建)都是一样的,因为二者的内部实现是一样的, a...b 事实上就是 a..<b.successor() 。

这里需要提醒大家的是,一个 Range<Int> 永远都不能有 Int.max,这也意味着 Int.max 永远都不会成为一个 Range<Int> 的成员,这同样适用于其他有最大值的类型。这个限制使范围不能满足我们所要的需求。所以接下来让我们来看看区间能不能满足我们的要求。

区间

其实,在swift中,范围和区间的是基本相同的概念构建的(一个连续元素的系列,有开始有结尾),但使用了不同的方法。范围基于索引,因此可以是个集合,他们的大多数功能都是基本这个特性的。区间不是集合,他们的实现是依赖 Comparable 协议的。我们只可以为服从 Comparable 协议的类型创建区间类型:

protocol IntervalType {

typealias Bound : Comparable

...

}

有别于范围的定义,区间使用 IntervalType 协议呈现,这个协议有两个具体的实现, HalfOpenInterval 和 ClosedInterval 。两个范围操作符也为区间提供了重载:..< 创建一个 HalfOpenInterval 和 … 创建一个 ClosedInterval 。由于默认是重载了 Range ,所以你必须明确变量为区间类型(IntervalType):

let int1: HalfOpenInterval = 1..<5

int1.contains(5) // false

let int2: ClosedInterval = 1...5

int2.contains(5) // true

而需要注意的是 ClosedInterval 不可以为空,x…x 总是会包含 x,而 x…(x-1) 会造成运行时错误。

然而闭合区间可以包含一个类型的最大值。这意味着我们现在可以写我们的 switch 语句了。重复一遍,一定要明确类型,告诉编译器我们想要的是区间而不是范围:

let x = 10

switch x {

case 1...Int.max as ClosedInterval:

print("positive")

case Int.min..<0 as HalfOpenInterval:

print("negative")

case 0:

print("zero")

default:

fatalError("Should be unreachable")

}

为开区间定制操作符

如果想摆脱 Int.min 和 Int.max怎么办?这个时候,可以为开区间和闭区间自定义前缀操作符和后缀操作符,用于表示所有小于一个上边界的值,或者大于一个下边界的值。这样不仅在语法上要更友善;理想情况下,这些操作符不仅适用于 Int 类型,也可以适合于其它拥有最小和最大值的类型。实现看起来应该是这个样子:

switch x {

case 1...: // an interval from 1 to Int.max (inclusive)

print("positive")

case ..<0: // an interval from Int.min to 0 (exclusive)

print("negative")

...

}

我们需要为 ..< 和 ... 分别定义前缀和后缀的实现方式 。下面这段代码基本是基于 Nate Cook 写的 gist片段 。

首先,我们必须声明需要解释的操作符:

prefix operator ..< { }

prefix operator ... { }

postfix operator ..< { }

postfix operator ... { }

紧接着,为 Int 实现第一个运算符的方法:

/// Forms a half-open interval from `Int.min` to `upperBound`

prefix func ..< (upperBound: Int) -> HalfOpenInterval<Int> {

return Int.min..<upperBound

}

还可以让它更通用。区间要求它的底层类型都遵循 Comparable 协议,所以使用相同的条件约束是一个很自然的选择。但在这里我们会碰到一个问题:我们需要知道 T 类型的最小值来创建区间,但这并没有一个通用的方法:

prefix func ..< <T : Comparable>(upperBound: T) -> HalfOpenInterval<T> {

return T.min..<upperBound // error: type 'T' has no member 'min'

}

甚至是在标准库中的其他协议都没有为数字(就比如 IntegerType )提供这些–定义在数字类型中的 min 和 max 属性。

这个时候,我们可以试试这个解决方案:定义一个 MinMaxType 的自定义协议,这个协议定义了 min 和 max 两个属性。因为所有整数类型都有这两个属性,让他们遵守新的协议就不用额外写代码了:

/// Conforming types provide static `max` and `min` constants.

protocol MinMaxType {

static var min: Self { get }

static var max: Self { get }

}

// Extend relevant types

extension Int : MinMaxType {}

extension Int8 : MinMaxType {}

extension Int16 : MinMaxType {}

extension Int32 : MinMaxType {}

extension Int64 : MinMaxType {}

extension UInt : MinMaxType {}

extension UInt8 : MinMaxType {}

extension UInt16 : MinMaxType {}

extension UInt32 : MinMaxType {}

extension UInt64 : MinMaxType {}

这里有一个值得牢记的技巧。任何时候,当你有几个不相关的类型,但它们具有相同类型的一个或多个方法、属性,你都可以创建一个新的协议给他们提供一个通用接口。

告诉我们的通用类型 T 遵守 MinMaxType 协议以使这个实现可以正常运行:

/// Forms a half-open interval from `T.min` to `upperBound`

prefix func ..< <T : Comparable where T : MinMaxType>

(upperBound: T) -> HalfOpenInterval<T> {

return T.min..<upperBound

}

这里是其他三个操作符的实现:

/// Forms a closed interval from `T.min` to `upperBound`

prefix func ... <T : Comparable where T : MinMaxType>

(upperBound: T) -> ClosedInterval<T> {

return T.min...upperBound

}

/// Forms a half-open interval from `lowerBound` to `T.max`

postfix func ..< <T : Comparable where T : MinMaxType>

(lowerBound: T) -> HalfOpenInterval<T> {

return lowerBound..<T.max

}

/// Forms a closed interval from `lowerBound` to `T.max`

postfix func ... <T : Comparable where T : MinMaxType>

(lowerBound: T) -> ClosedInterval<T> {

return lowerBound...T.max

}

添加一些测试:

(..<0).contains(Int.min) // true

(..<0).contains(-1) // true

(..<0).contains(0) // false

(...0).contains(Int.min) // true

(...0).contains(0) // true

(...0).contains(1) // false

(0..<).contains(-1) // false

(0..<).contains(0) // true

(0..<).contains(Int.max) // false

(0..<).contains(Int.max - 1) // true

(0...).contains(-1) // false

(0...).contains(0) // true

(0...).contains(Int.max) // true

回到我们的 switch 语句,现在很好地工作了:

switch x {

case 1...:

print("positive")

case ..<0:

print("negative")

case 0:

print("zero")

default:

fatalError("Should be unreachable")

}

结束语

Swift 中范围和区间都有相似的目的,但有着不同的实现和泛型约束。范围基于索引并且经常用于集合上下文中。这意味着范围不能包含一个类型最大值,这就不适合用在数字的区间上。区间兼容所有的 Comparable 类型,并且没有最大值的限制。

如果要用swift语言开发iOS应用的话,区间和范围这两个概念还是需要理清楚,明白什么时候用范围、什么时候用区间,提高开发效率,提升代码质量,一步一步迈入iOS大神行列。

相关文章:《Linux系统中CPU使用率查询常用的5个命令

Swift 中范围和区间如何使用?的更多相关文章

  1. Switch在swift中的使用

    switch的简单使用: 相比 C 和 objective - C 中的 switch 语句,Swift 中的 switch 语句不会默认的掉落到每个 case 的下面进入 另一个 case.相反,第 ...

  2. 总结 Swift 中随机数的使用

    在我们开发的过程中,时不时地需要产生一些随机数.这里我们总结一下Swift中常用的一些随机数生成函数.这里我们将在Playground中来做些示例演示. 整型随机数 如果我们想要一个整型的随机数,则可 ...

  3. swift项目第五天:swift中storyBoard Reference搭建主界面

    一:StoryBoard Reference的介绍 StoryBoard Reference是Xcode7,iOS9出现的新功能 目的是让我们可以更好的使用storyboard来开发项目 在之前的开发 ...

  4. swift 中关于open ,public ,fileprivate,private ,internal,修饰的说明

    关于 swift 中的open ,public ,fileprivate,private, internal的区别 以下按照修饰关键字的访问约束范围 从约束的限定范围大到小的排序进行说明 open,p ...

  5. 阿里巴巴最新开源项目 - [HandyJSON] 在Swift中优雅地处理JSON

    项目名称:HandyJSON 项目地址:https://github.com/alibaba/handyjson 背景 JSON是移动端开发常用的应用层数据交换协议.最常见的场景便是,客户端向服务端发 ...

  6. Swift中的可选链与内存管理(干货系列)

    干货之前:补充一下可选链(optional chain) class A { var p: B? } class B { var p: C? } class C { func cm() -> S ...

  7. 在Swift中实现单例方法

    在写Swift的单例方法之前可以温习一下Objective-C中单例的写法: + (instancetype)sharedSingleton{ static id instance; static d ...

  8. [翻译]理解Swift中的Optional

    原文出处:Understanding Optionals in Swift 苹果新的Swift编程语言带来了一些新的技巧,能使软件开发比以往更方便.更安全.然而,一个很有力的特性Optional,在你 ...

  9. 窥探Swift之使用Web浏览器编译Swift代码以及Swift中的泛型

    有的小伙伴会问:博主,没有Mac怎么学Swift语言呢,我想学Swift,但前提得买个Mac.非也,非也.如果你想了解或者初步学习Swift语言的话,你可以登录这个网站:http://swiftstu ...

随机推荐

  1. python--自动删除文件

    1.目的:定期自定删除7天前的数据 python脚本如下: #coding=utf-8 import os,time,datetime #需定时删除的目录的上一层路径 data_dir="/ ...

  2. 揭开HTTP网络协议神秘面纱系列(二)

    HTTP报文内的HTTP信息 HTTP协议交互的信息被称为HTTP报文,请求端的HTTP报文叫做请求报文,响应端的叫做响应报文. HTTP为了提升传输速率,其在传输数据时,按照数据原样进行压缩传输,相 ...

  3. 关于java中线程休眠的另一种写法

    编辑器加载中... 优先使用TimeUnit类中的sleep() TimeUnit是什么? TimeUnit是java.util.concurrent包下面的一个类,TimeUnit提供了可读性更好的 ...

  4. ARM Linux 3.x的设备树(Device Tree)

    http://blog.csdn.net/21cnbao/article/details/8457546 宋宝华 Barry Song <21cnbao@gmail.com> 1.     ...

  5. 初学layer-------web框架

    第一步,文件的下载   http://layer.layui.com/ 第二步,文件的部署即将包放到web端的相关目录下. 第三步,引用layer.js(此框架是基于jquery的)所以要先引用jqu ...

  6. CSS中父元素高度没有随子元素高度的改变而改变,应该如何解决?

    如果子元素没有设置浮动(float),父元素实际上会根据内容,自动宽高进行适应的. 当子元素增加了浮动后,最简单的处理方法是给父元素添加overflow:hidden属性,此时父元素的高度会随子元素的 ...

  7. 在centos上编译安装mariadb数据库

    一.安装前提(准备数据文件.安装其他依赖的软件) 1.准备数据存放的目录 [root@localhost ~]# fdisk /dev/sdb  (fdisk /dev/sdb 创建一个逻辑分区/de ...

  8. Splunk及splunkforward简单部署配置

    部署环境 操作系统 服务器操作系统版本:CentOS release 6.5 (Final) 2.6.32-431.el6.x86_64 软件 软件版本:splunk-6.4.0 tar: splun ...

  9. [SmartFoxServer概述]SFS2X栈平台

    SmartFoxServer 2X 栈平台 在这有一张SmartFoxServer 2X平台的鸟瞰图,接下来会简要介绍栈中的每个组件. 首先是服务器的核心——网络引擎(代号BitSwarm),它是用以 ...

  10. C++ set使用

    C++ set使用 实际上c++ STL中的set是的实现和C++ STL中的map的实现的底层数据结构是一样的,如果我们不在考虑红黑树中的卫星数据,而只是关键字,那么同样不允许key值得重复,那么就 ...