swift中的双重调度,用于数字类型

时间:2016-06-16 14:47:01

标签: swift numeric double-dispatch swift3

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

然而,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的值以查看此信息。 注意:溢出时会发生运行时错误。
  3. 例如减法(注意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协议中声明。为什么不同?

0 个答案:

没有答案