美文网首页
Swift Range

Swift Range

作者: 忧郁的小码仔 | 来源:发表于2019-05-08 15:18 被阅读0次

说起区间,大家学过数学的应该都很清楚了,比如闭区间、开区间、半开半闭区间。今天为啥子说起Swift里的Range呢,其实也是闲来无事,想把截取String弄的简单一点,然后涉及到一些Range的使用,所以才有了后面的废话。

Swift不知不觉已经到了5.0了,过去如果想截取String的话,还有个subString(...)方法,但是现在这个方法已经标记为废弃状态了。但是从4.0开始,Swift提供了下面的方法来截取一个区间的字符串

@inlinable public subscript(r: Range<String.Index>) -> Substring { get }

这个方法唯一不方便的就是RangeBound竟然是String.Index,害得我们还要自己去获取一个String.Index的对象,用起来多少有些不方便。我想要的很简单,实现一些BoundInt类型的subscript方法就可以了,完成后可以像下面这样,直接截取String

let str = "swift is the best language"
print(str[0...2]) // swi
print(str[0..<2]) // sw
.....

还有几种就省略了。开始自定义 subscript方法之前,我们先来看下下面几种Range的源码解释,以及他们的一些方法

1. Range(半开区间)

public struct Range<Bound> where Bound : Comparable {
    public let lowerBound: Bound
    public let upperBound: Bound
    // ... ...
}

后面所有的Range都包含两个属性:表示最小边界值的lowerBound和表示最大边界值的upperBound,这里的Bound是遵守了Comparable的一个associatedtype,专门用来表示区间的边界值

associatedtype Bound : Comparable

关于Range的描述是这样的

A half-open interval from a lower bound up to, but not including, an upper bound.
You create a Range instance by using the half-open range operator (..<).

let underFive = 0.0..<5.0

可见Range表示的是半开区间,并且它可以用来表示一个空的间隔序列,比如这样

let emptyRange = 10..<10
emptyRange.contains(10) // false
emptyRange.isEmpty // true

这里还有另外两个方法:

@inlinable public func clamped(to limits: Range<Bound>) -> Range<Bound>
@inlinable public func overlaps(_ other: Range<Bound>) -> Bool
@inlinable public func contains(_ element: Bound) -> Bool

clamped(...)用来取两个区间的重叠区间,比如10..20.clamped(to: 15...100)结果是15...20,如果两个区间没有重叠部分,则返回一个空的区间
overlaps(...)则用来表示两个区间是否有重叠部分。
contains(...)用来判断区间是否包含某个值

2. ClosedRange(闭区间)

ClosedRange的属性和方法和Range是一样的,ClosedRangeRange的区别是包含最大边界值,并且它不能表达空区间这种概念

3. CountableRange 和 CountableClosedRange

这两个 Range是继承自RangeClosedRange的两个区间类,同样他们分别表示半开区间和闭区间,不过,他们都遵守了Strideable协议。

public typealias CountableRange<Bound> = Range<Bound> where Bound : Strideable, Bound.Stride : SignedInteger
public typealias CountableClosedRange<Bound> = ClosedRange<Bound> where Bound : Strideable, Bound.Stride : SignedInteger

A type representing continuous, one-dimensional values that can be offset and measured.
Strideable协议要表达的意思是一些连续的一维的数据,比如一串数字 12345...
,这串数字能够执行偏移和测量这样的动作。我们来看下它约定的方法就明白什么意思了

func distance(to other: Self) -> Self.Stride

这个方法用来计算连续数值中两个数值之间的间隔(measure)

func advanced(by n: Self.Stride) -> Self

这个方法则表示一个数值增加一定的偏移量后得到的另一个数值(offset)
这两个方法就保证了遵守Strideable协议就可以进行迭代操作,比如正常情况下,如果让你遍历0.1到2.3这个区间,你该怎么遍历呢?显然是没法遍历的,但是一旦Float遵守了Strideable协议,这件事情就好办了。因为一旦知道了步长以及根据步长得到下一个值的方法,你就可以把一个区间遍历完。

遵守了Strideable协议就可以使用stride(from:to:by:)stride(from:through:by:)来迭代一个序列了。比如,下面是源码中的一个小例子:

for radians in stride(from: 0.0, to: .pi * 2, by: .pi / 2) {
     let degrees = Int(radians * 180 / .pi)
     print("Degrees: \(degrees), radians: \(radians)")
 }
// Degrees: 0, radians: 0.0
// Degrees: 90, radians: 1.5707963267949
// Degrees: 180, radians: 3.14159265358979
// Degrees: 270, radians: 4.71238898038469

最后一个参数(Stride)表示步长,即两个相邻数值之间的距离

当一个Range的Bound是integers或者遵守了Strideable协议并且Stride是integer的时候,这个Range就可以用在for..in里,并且可以使用任何Sequence或者Collection的方法

4. PartialRangeUpTo、PartialRangeThrough和PartialRangeFrom

这三个Range都是表示部分区间的,
PartialRangeUpTo: 操作符 ..<
比如 ..<5.0表示数轴上5.0左边的区间,但不包括5.0这个值
PartialRangeThrough: 操作符 ...
比如 ...5.0表示数轴上5.0左边的区间,包括5.0这个值
PartialRangeFrom: 操作符 ...
比如 5.0...表示从数轴上从5.0开始的右边的区间,包含5.0这个值


下面,我们用上面这些 Range 来实现文章开始提到的截取String的快捷功能:

    // "swift"[0...2]  // swi
    subscript(value: CountableClosedRange<Int>) -> Substring {
        get {
            let start = index(self.startIndex, offsetBy: value.lowerBound)
            let end = index(self.startIndex, offsetBy: value.upperBound)
            return self[start ... end]
        }
    }
    
    // "swift"[0..<2]  // sw
    subscript(value: CountableRange<Int>) -> Substring {
        get {
            let start = index(self.startIndex, offsetBy: value.lowerBound)
            let end = index(self.startIndex, offsetBy: value.upperBound)
            return self[start ..< end]
        }
    }
    
    // "swift"[..<2]  // sw
    subscript(value: PartialRangeUpTo<Int>) -> Substring {
        get {
            return self[ ..<index(self.startIndex, offsetBy: value.upperBound)]
        }
    }
    
    // "swift"[...2]  // swi
    subscript(value: PartialRangeThrough<Int>) -> Substring {
        get {
            return self[ ...index(self.startIndex, offsetBy: value.upperBound)]
        }
    }
    
    // "swift"[2...]  // ift
    subscript(value: PartialRangeFrom<Int>) -> Substring {
        get {
            return self[index(self.startIndex, offsetBy: value.lowerBound)... ]
        }
    }

相关文章

网友评论

      本文标题:Swift Range

      本文链接:https://www.haomeiwen.com/subject/zjgmoqtx.html