2016-05-22 69 views
4

我試圖建立一個通用類型MyStruct<T>,可以使用FloatDouble作爲內部存儲。在初始化程序中,我傳遞了T型參數(我打算是FloatDouble)。該初始化程序調用一些trig函數,如sin()cos()。這兩種功能都在系統庫中過載以提供FloatDouble版本。使Swift仿製藥玩重載函數

var f:Float=1.2 
var d:Double=1.2 

sin(f) //0.9320391 
sin(d) //0.9320390859672263 

問題是,我不能在我的通用結構中使用它們。一個剝離下來的情況是這樣的:

struct MyStruct<T> { 
    var v:T 

    init(x:T){v=sin(x)} 
} 

因爲:

Playground execution failed: Untitled Page.xcplaygroundpage:9:17: error: cannot invoke 'sin' with an argument list of type '(T)' init(x:T){v=sin(x)}

Untitled Page.xcplaygroundpage:9:17: note: overloads for 'sin' exist with these partially matching parameter lists: (Float), (Double) init(x:T){v=sin(x)}

好像應該有一種方法,使這項工作,但感覺很像this situation。評論表明,沒有辦法要求全球職能的存在。

我可以用一個結構,如受力情況:

init(x:T){v=T(sin(Double(x)))} 

,並把約束上,它可以從Double構造T,但似乎打敗使得Float版本的目的的結構,這是爲了減少在關鍵代碼循環中使用時的計算量。

感覺就像這將是比較容易,如果sin()曾在圖書館被定義爲一個通用的函數,而不是一個重載函數:

func sin<T:FloatingPointType> (x:T) -> T 

但它是什麼。

有沒有一種方法可以讓我在標準庫之上建立一個Float/Double不可知的庫,而不會增加很多開銷?

+0

我不確定「this」應該是什麼,但是'sin'是幾個C函數,它們對Swift泛型沒有任何認識(所以你不能使用佔位符類型作爲它的參數)或Swift協議(所以你可以不使用FloatingPointType作爲參數)。 – matt

+0

@matt,「this」我的意思是創建一個浮點/雙精度不可知的庫,位於標準庫的頂部。這似乎是泛型的一種明顯的用法,但是我一直不能超過最基本的東西。 – Omegaman

回答

4

不幸的是,有沒有簡單的方法來做到這一點的方式sin()斯威夫特正在實施。必須將其視爲運營商(如Equatable==的工作原理),以便允許您將其作爲協議要求添加。

@matt's solution是一個不錯的快速修復程序,但是如果你想要一些更加永久的東西,你可能需要考慮創建一個協議,然後擴展浮點類型以允許你用泛型版本來重載sin()函數。

protocol FloatingPointMathType : FloatingPointType { 
    var _sinValue : Self { get } 
} 

extension Float : FloatingPointMathType { 
    var _sinValue : Float {return sin(self)} 
} 

extension Double : FloatingPointMathType { 
    var _sinValue : Double {return sin(self)} 
} 

extension CGFloat : FloatingPointMathType { 
    var _sinValue : CGFloat {return sin(self)} 
} 

func sin<T:FloatingPointMathType>(x:T) -> T {return x._sinValue} 

(隨意添加更多的數學函數)

我們不必使用「陰影」計算財產這裏來彌補這一事實,我們不能簡單地用sin()作爲協議要求。這並不理想 - 但可能和你一樣好。

現在,您可以繼續使用sin()作爲一個泛型函數:「有我這樣做不增加大量的開銷的方式」

struct MyStruct<T:FloatingPointMathType> { 
    var v : T 

    init(x:T) { 
     v = sin(x) 
    } 
} 

print(MyStruct(x: Float(3.0)).v) // 0.14112 
print(MyStruct(x: Double(3.0)).v) // 0.141120008059867 
print(MyStruct(x: CGFloat(3.0)).v) // 0.141120008059867 
+0

這似乎這樣做沒有運行時開銷。我認爲Swift可能會嵌入大部分額外的函數調用。我用'func sin() - > Float {return Foundation.sin(self)}'來代替var,但我認爲它是6合1,另一半是6。我不確定爲什麼你不需要添加基礎名稱空間來避免引用循環,也許是因爲你的泛型函數只是另一個重載?保持函數調用語法的通用函數是一個很好的接觸。 – Omegaman

+0

@Omegaman高興地幫助:)我可以忽略來自非泛型'sin'函數調用的'Foundation.'名稱空間的原因是Swift總是更喜歡用更具體的參數類型調用函數。因此,當你使用非泛型類型時,它會優先選擇泛型函數的非泛型函數sin函數,所以不存在任何問題 - 儘管很明顯,將它放入函數沒有什麼壞處。至於是否使用計算財產或功能,我懷疑它無論在什麼情況下都適用於優化,無論你喜歡使用什麼。 – Hamish

1

可以嘗試這樣的事:

struct MyStruct<T:FloatingPointType> { 
    var v:T 
    init(x:T){ 
     switch x { 
     case is Float: 
      v = sin(x as! Float) as! T 
     case is Double: 
      v = sin(x as! Double) as! T 
     case is CGFloat: 
      v = sin(x as! CGFloat) as! T 
     default:v = 0.0 as! T 
     } 
    } 
} 

似乎工作:

let s = MyStruct(x:Float(3)) 
s.v // 0.14112 
let s2 = MyStruct(x:Double(3)) 
s2.v // 0.1411200080598672 
let s3 = MyStruct(x:CGFloat(3)) 
s3.v // 0.1411200080598672 
+0

你可以用'case let x as Float'來使這個稍微好一些。這樣你就可以消除第一組強制downcasts。 – Hamish

+0

這在一個捏,我害怕它可能是唯一的選擇,直到@ originaluser2找到一種方法來做到這一點與一些聰明的協議專業化和通用功能來隱藏混亂。這裏的主要缺點是運行時類型檢查的開銷。 – Omegaman