2016-02-26 90 views
0

美好的一天!快速奇怪的仿製藥行爲

我目前正在嘗試學習swift編程,嘗試一些泛型的例子。這裏是我的示例代碼

func findAll<T: Equatable>(arr: [T], _ elem: T) -> [Int] { 
    var indexesArr = [Int]() 
    var counter = 0 

    for i in arr { 
     if i == elem { 
      indexesArr.append(counter) 
     } 
     counter++ 
    } 

    return indexesArr 
} 

findAll([5, 3, 7, 3, 9], 3) 
findAll(["a", "b", "c", "b", "d"], 2.0) 

據推測,這兩個參數將是相同的類型。但是,不幸的是,正如你所看到的,我在我的第二個函數調用中傳遞了一個字符串數組和一個Double數組,它仍然有效!它返回一個空數組[]。沒有運行時或編譯錯誤。

請解釋我爲什麼它有效並且可能有一些解決方法。謝謝!

回答

1

想必您在代碼中有import Cocoaimport Foundation。如果你刪除它,你會發現你的代碼按照你的預期行事。

發生這種情況的原因是,在導入基金會導致Swift.Array神奇地橋接到NSArray(又名[AnyObject]),和某些原始值類型神奇地橋接到Foundation對象類型(如NSStringNSNumber)。一旦發生這種情況,使用[AnyObject]AnyObject類型的參數調用函數是完全合法的。


我不知道周圍的魔力轉化方式,但工作的方法之一是迫使你的泛型參數有原始值的類型遵循的協議,但基本值神奇包裹在可可物體中沒有。以確保這樣的一種方法是創建自己的協議:

public protocol NotAnyObject {} 
extension Int: NotAnyObject {} 
extension String: NotAnyObject {} 
extension Double: NotAnyObject {} 
func findAll<T: Equatable where T: NotAnyObject>(arr: [T], _ elem: T) -> [Int] { /*...*/ } 

我不知道這是最好的辦法,但...替代方案是受歡迎的。


順便提及,更慣用/ SWIFTY方式這樣做將是作爲一個類型擴展(或者一個協議擴展,如下,或上一個Array where Element: Equatable擴展)。你甚至可以去功能編程風格只是爲了好玩:

extension CollectionType where Generator.Element : Equatable { 
    public func allIndexesOf(element: Self.Generator.Element) -> [Self.Index] { 
     return zip(self.indices, self) // makes sequence of (index, element) 
      .filter { $0.1 == element } 
      .map { $0.0 } 
    } 
} 

這樣稱呼它:

let threes = [5, 3, 7, 3, 9].allIndexesOf(3) 
// returns [1, 3] 

這仍然是受同樣的魔力轉換的問題,雖然:

let notThrees = ["twenty", "forty", "eight"].allIndexesOf[2.0] 
// still compiles, returns [] 

所以你仍然需要使用這個擴展名,只能從沒有導入Foundation的Swift文件中使用這個擴展名,或者應用與上面相同的hack的變體,例如:

extension CollectionType where Generator.Element: Equatable, 
    Generator.Element: NotAnyObject { /*...*/ } 
+0

我同意這個解釋,我不同意這個建議的解決方案,刪除'Foundation'的輸入通常是不可能的。 – Sulthan

+0

非常感謝rickster和@Sulthan!這兩個答案真的幫助我!試圖upvote,但看起來像stackoverflow以另一種方式認爲:) – FlyOrDie

+0

這種橋接真的讓我難過:/開發IOS應用程序時,這樣的問題真的很麻煩嗎?哦,這只是一件要知道的事情,但不是真的有問題? – FlyOrDie

-1

對於這個問題的解釋看rickster's answer

需要注意的是要解決這樣的問題,你總是可以只檢查如何斯威夫特庫做到這一點。解決方案是將兩個參數都作爲泛型並使用協議約束。

E.g.使用SequenceType協議而不是Array

func findAll<S: SequenceType, T: Equatable where S.Generator.Element == T>(arr: S, _ elem: T) -> [Int] { 
    var indexesArr = [Int]() 
    var counter = 0 

    for i in arr { 
     if i == elem { 
      indexesArr.append(counter) 
     } 
     counter++ 
    } 

    return indexesArr 
} 
+0

'findAll([「a」,「b」,「c」,「b」,「d」],2.0)'仍然編譯並運行(並返回一個空數組)。 – rickster

+0

@rickster你是對的,但問題變化的原因。現在是因爲你只使用文字值,所以這些類型被推斷爲'[AnyObject]'。但是,使用'findAll([「a」,「b」,「c」,「b」,「d」],2.0作爲Double)''或'findAll([「a」,「b」 「,」c「,」b「,」d「]作爲[String],2.0)'。在大多數情況下,您可以事先在變量中聲明數組。 – Sulthan