2

像许多人一样,我正在尝试使用整数和浮点以及这些类型的集合。

然而 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 进行算术运算效果很好。

  1. 如果类型不同,它可能会或可能不会起作用。 2. 如果其中一种类型是 Int,则操作有效,并返回将保存结果的最小类型。
  2. 更改 i1、i2 和 i3 的值以查看此内容。注意:溢出时会发生运行时错误。

例如减法(注意这里 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,进行操作然后进行强制转换?他们如何确定最小的类型? 见马丁的评论

如果我们需要这些 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 的每种类型实现所有这些功能。

此外,我们不能使用泛型,因为这会再次导致递归。类型没有改变。

如果我们有两个恰好有 DataType 签名的常规 Int:

let d1 : DataType = 6
let d2 : DataType = 8

let d12 = d1 - d2

即使内置函数更有效,也将使用双重调度。毕竟这会跳过两个函数调用。

所以我的问题,除了那些来自间奏曲的问题,是这样的:有没有更好的方法来完成这个?一个不需要臃肿的协议(每个支持的类型的函数)并且不会不必要地回避内置的 swift 函数。

Apple 是否使用双重调度来添加 Int 和 Int16 值(见上文)?

edit1添加了 linethrough 的(见 Martin 的评论)

edit2 Swift 有一个继承自 _IntegerArithmatic 的 IntegerArithmatic 协议。

_IntegerArithmatic 定义了不应直接使用的此类函数,因为它们是实现细节(参见头文件)。

func isEqual(to  other: Self) -> Bool

然而 FloatingPoint 协议没有这样的区别,'isEqual' 和 '==' 函数都直接在 FloatingPoint 协议中声明。为什么有区别?

4

0 回答 0