斯威夫特的数字协议
我在新加坡iOS Conf的Jesse Squires演讲中,他讨论了Swift的数字类型和协议。 这让我想起了SE-0104(面向协议的整数),现在该实现是在Swift 4中进行的,认为现在是仔细研究的好时机。
让我们从每个人喜欢的整数类型Int
的协议层次结构开始:
与标准库中的大多数类型一样, Int
符合很多协议! 从头到尾, Numeric
是基本协议之一。 所有不同大小的整数类型( UInt
, Int8
等)以及浮点类型( Float
, Double
等)都符合Numeric
。
数值,从0x00开始
您可以在Integers.swift.gyb中遵循标准库源代码,尽管照常,所有相关代码都将在下面内联。
这是开始的协议声明:
public protocol Numeric : Equatable, ExpressibleByIntegerLiteral {
这里有另外两个符合性:
-
Equatable
增加了对==
运算符的支持。 -
ExpressibleByIntegerLiteral
允许使用let a: Int = 42
这样的代码let a: Int = 42
其中42
是上述的“整数文字”。
这意味着所有数字类型都可以使用整数文字进行初始化,但不能使用浮点文字进行初始化。 这段代码可以:
let someFloat: Float = 42 // ✅
但这不行:
let someInt: Int = 4.2 // 🙅
您将在特定的浮点类型中遇到ExpressibleByFloatLiteral
协议,但不会在一般的Numeric
类型级别上遇到。
初始化
该协议具有一个必需的初始化程序:
init?(exactly source: T)
这是一个失败的初始化程序,参数标签中的“完全”应该告诉您原因:如果尝试使用超出其可容纳范围的值初始化实例,则初始化程序将返回nil
:
let ok = Int8(exactly: 10) // 10
let tooBig = Int8(exactly: 300) // nil
还要注意对源类型的约束:它必须符合BinaryInteger
。 BinaryInteger
符合Numeric
因此对于像Numeric
这样的“父”协议来说,需要一种符合像BinaryInteger
这样的“子”协议的BinaryInteger
来构造自身似乎有点怪异。 这里发生了什么?
表示与使用
关于BinaryInteger
和Numeric
这两种协议的BinaryInteger
快速BinaryInteger
。
根据标题文档, BinaryInteger
就是它在BinaryInteger
上说的:“一个具有二进制表示形式的整数类型”。 对于许多程序员而言,这是非常熟悉的,因为我们一直使用以位为单位的整数。
另一方面, Numeric
与表示无关,与用法无关。 该协议“为标量值的算术提供了合适的基础”。
好,然后回到算术。 🤓
大小
Magnitude是数字的绝对值,因此42.magnitude
和-42.magnitude
都是42
。
associatedtype Magnitude : Comparable, Numeric
var magnitude: Magnitude { get }
magnitude
计算的属性必须是Numeric
并且也是Comparable
。 这意味着一个数字本身不必具有可比性,但其大小却可以。
当您查看诸如Int
之类的类型的具体实现时,您会看到它使用magnitude
进行一些算术,计算数字之间的距离等。
算术
我们已经达到了我们最喜欢的算术运算:加法!
static func + (_ lhs: Self, _ rhs: Self) -> Self
static func +=(_ lhs: inout Self, rhs: Self)
第一个加法函数采用两个值并产生和。 第二个是变异版本,其中“左侧”自变量( a += 10
中的a += 10
)被变异。
在您自己的数字类型中,至少应提供变异运算符的实现。
如果您已经编码了符合Equatable
类型,那么您会记得需要为==
提供实现,然后免费获得!=
。
同样,通常的模式是用+=
定义+
。 例如,具体标准库类型UInt16
中+
的实现使用+=
:
// Inside the UInt16 implementation
public static func +(_ lhs: UInt16, _ rhs: UInt16) -> UInt16 {
var lhs = lhs
lhs += rhs
return lhs
}
最后, Numeric
协议还要求运算符进行减法和乘法运算:
static func - (_ lhs: Self, _ rhs: Self) -> Self
static func -=(_ lhs: inout Self, rhs: Self)
static func * (_ lhs: Self, _ rhs: Self) -> Self
static func *=(_ lhs: inout Self, rhs: Self)
在应为自己的符合类型提供变异版本的地方,也适用相同的准则。
没说的事情
具体数字类型的作用远远超出了此协议中此处定义的范围。 正如您从本文顶部的图中所看到的,有很多协议组合在一起,使某些事情像Swift整数一样复杂。
就是说,我们只介绍了Numeric
,只看了我们习惯于使用数值类型的所有功能的一部分。 缺少什么大的东西?
- 除法 -我们已经看到了加法,减法和乘法运算,但是缺少的算术族成员是什么? 除法在
BinaryInteger
和FloatingPoint
协议中分别定义。
协议中的功能定义相同,但规格略有不同:整数除法会丢弃其余部分,而浮点除法则遵循IEEE-754规则。 - 浮点运算 -您看到了符合
Numeric
类型如何也是ExpressibleByIntegerLiteral
但不能用float文字表达。 整数可以直接转换为浮点数,但是从浮点数转换为整数时,您需要考虑舍入。 此舍入如何工作? 对于Numeric
这超出了范围。 - 可比 —同样,这是在
BinaryInteger
和FloatingPoint
协议级别上发现的,除了您之前看到的magnitude
属性。 这两个协议都符合Strideable
,这反过来又意味着Comparable
。
结束括号
Numeric
协议提供了数值类型的基础:
- 用整数值初始化
- 平等的
- 简单算术
- 确定其潜在价值的量级概念
除了上一节中列出的缺失内容外,该协议对存储也没有意见。 请记住, Numeric
是关于数字的用途 ,而其他协议(例如BinaryInteger
则是关于表示形式以及如何存储值的。
准备建立自己的自定义vigesimal数值类型了吗? 😉
}