像许多人一样,我试图使用整数和浮点以及这些类型的集合。
然而,Swift型系统并不总是可以原谅的。大多数人定义了一个数字协议来处理这个问题。我想知道是否有更好的方式以及Apple如何做到这一点。
所以:
Swift中的整数有几种类型
let i1 : Int16 = 6
let i2 : Int32 = 32
let i3 : Int = 64
我们可以确认他们的类型
print(i1.dynamicType) //Int16
print(i2.dynamicType) //Int32
print(i3.dynamicType) //Int
对于相同类型的int,使用算术可以正常工作。
例如减法(注意lhs和rhs的顺序在这里很重要)
let i11 = i1 - i1
//let i12 = i1 - i2 //operator cannot be applied to types Int16 and Int32
let i13 = i1 - i3
检查类型表明确实返回了最小的Int。
print(i11.dynamicType) //Int16
//print(i12.dynamicType) //n.a.
print(i13.dynamicType)//Int16
间奏曲:
Apple如何实现这一目标?他们将所有内容转换为Int,进行操作然后再进行转换吗?他们如何确定最小的类型? 参见Martin的评论
如果我们需要这些Int的集合,我们将遇到麻烦。
let int = [i1,i2,i3] // ambiguous without more context
let int : [Int] = [i1,i2,i3] // cannot convert Int16 to Int
此时我们搜索stackoverflow并决定使用DataType协议。
extension Int16 : DataType{}
extension Int32 : DataType{}
extension Int : DataType{}
这将允许我们拥有一个数组,
//let all = [i1,i2,i3] // type of expression is ambiguous without more context
如果我们明确指定元素类型。
let int : [DataType] = [i1,i2,i3]
print(int.dynamicType) //[DataType]
现在我们想要计算一些东西 var total:DataType = 0
print(total.dynamicType) //Int
//int.reduce(total) { $0 - $1 } //operator cannot be applied to types _ and DataType
不确定_是或应该或可能是什么。那么这个怎么样?
for element in int
{
total = total - element //operator cannot be applied to types DataType and DataType
}
好的,所以我们重载了两个DataTypes的函数
func - (lhs: DataType, rhs: DataType) -> DataType
{
print("substract: \(lhs) - \(rhs)")
// return lhs - rhs //RECURSION: this is going to take forever (don't remove the comments here)
//ok, so cast to Int. But what if our DataType is not an Int, but something crazy like a String or our custom DataType (e.g. complex number)?
//we can do double dispatch
rhs.substract(lhs)
}
在搜索了一些之后,我们发现了双重调度。通过双重调度,我们可以使类型更具体。
为此,我们首先扩展Int。
extension Int : DataType
{
func substract(lhs: DataType) -> DataType
{
//now we know the rhs (i.e. self) is an Int
return lhs.substract(self) //note that we "substract" lhs from rhs
}
func substract(rhs: Int) -> DataType
{
//now we know both lhs and rhs are Int's
return lhs - rhs //and this will use the builtin Swift function
}
}
现在我们可以安全地实现我们的全局功能
func - (lhs: DataType, rhs: DataType) -> DataType
{
rhs.substract(lhs) //note that we "substract" lhs from rhs
}
并定义我们的协议
Protocol DataType
{
func substact(lhs: DataType, rhs: DataType) -> DataType
func substact(lhs: DataType, rhs: Int16) -> DataType
func substact(lhs: DataType, rhs: Int32) -> DataType
func substact(lhs: DataType, rhs: Int) -> DataType
}
但是现在我们必须为符合DataType的每种可能类型添加一个函数。 我们必须为符合DataType的每种类型实现所有这些功能。
此外,我们不能使用泛型,因为这会再次导致递归。类型没有改变。
如果我们有两个常规的Int恰好有一个DataType签名:
let d1 : DataType = 6
let d2 : DataType = 8
let d12 = d1 - d2
然后即使内置函数更有效,也将使用双重调度。毕竟,这将跳过两个函数调用。
所以我的问题,除了来自intermezzo的那些,是这样的:有没有更好的方法来实现这一目标?一个不需要膨胀协议(每种支持类型的函数)并且不会不必要地回避内置的快速函数。
Apple是否使用双重调度来添加Int和Int16值(见上文)?
edit1 添加了直通(参见Martin的评论)
EDIT2 Swift有一个IntegerArithmatic协议,它继承自_IntegerArithmatic。
_IntegerArithmatic定义了这些不应该直接使用的函数,因为它们是一个实现细节(参见头文件)。
func isEqual(to other: Self) -> Bool
然而,FloatingPoint协议没有这样的区别,而且两者都是等等的。和' =='函数直接在FloatingPoint协议中声明。为什么不同?