2017-03-03 57 views
1

我正在嘗試編寫一個簡單的手動滾動函數來壓扁嵌套數組。我的代碼作爲一個switch語句正常工作,但不是當我遞歸地調用它在「我在0..arr.count」循環中。我查看傳入的數組中的每個元素,並說「如果這是一個數組,調用該函數並將其傳入,否則追加到輸出數組」。採用通用數組的Swift函數

func flatten (input: [Any]) -> [Any] { 


    var outputArray = [Any]() 

    for i in 0..<input.count { 
     let data = input[i]; 
     (data is Array) ? outputArray += flatten(input: [data]) : outputArray.append(data) 

    } 

    return outputArray 

} 

因爲我的函數參數的類型必須爲[任何]的,我被迫通過「數據」變回成其爲[數據]。這只是強制函數不斷解壓數組,並且它只是卡在循環中。有關如何避免這種情況的任何建議?

+0

可能有所幫助:http://stackoverflow.com/questions/40669193/explain-swift-iterators和http://stackoverflow.com/questions/40689128/why-are-swift-iterators-slower-than -array建設。 –

+0

也可能有幫助:[擴展具有取決於元素類型的遞歸屬性/方法的集合](http://stackoverflow.com/q/41640321/2976878) – Hamish

+0

您是否嘗試過flatmap? –

回答

0

首先,您在相當複雜的情況下使用三元運算符(?:)。如果首先修改代碼以使用if語句,則出現在調用outputArray.append(data) - Generic parameter 'Element' could not be inferred時出現的編譯器錯誤 - 顯得更合理(即if語句的行)。

Generic parameter inference fails

該錯誤是比較容易解決 - 只需用Array<Any>取代Array,給我們這樣的:

func flatten(input: [Any]) -> [Any] { 
    var outputArray = [Any]() 

    for i in 0..<input.count { 
     let data = input[i] 
     if data is Array<Any> { 
      outputArray += flatten(input: [data]) 
     } else { 
      outputArray.append(data) 
     } 
    } 

    return outputArray 
} 

此時,原來的問題仍然存在,因爲這是在傳遞的值到flatten(input:)[data]。我們知道,由於if條件爲true,因此data確實屬於Array<Any>類型,因此值[data]必須是Array<Array<Any>>。相反,我們想通過data,這已經是一個數組。

你說你寫[data]的原因是因爲參數必須是一個數組,所以你被編譯器「被迫」。實際上,編譯器強迫你做的唯一事情就是傳入一個參數,其類型被聲明爲Array<Any>。我們確信,data是使用if聲明數組,但data仍然聲明Any(因爲它的input的元素,這是一個Array<Any>),所以編譯器不知道,它是一個真正的陣列。

的解決方案是 - 而不是使用if data is Array<Any>到遠暫時確定data是否是一個數組,而是立即扔的信息 - 來data轉換爲Array<Any>

新的if聲明變爲if let dataArray = data as? Array<Any>。聲明data as? Array<Any>試圖data轉換成數組,如果成功或nil否則返回Array<Any>類型的值。然後,if let dataArray = ...聲明將值存儲在dataArray中,如果給定非nil值,則返回true;如果給定nil值(這稱爲條件綁定),則返回false

通過這樣做,在true情況下if聲明,我們有機會獲得價值dataArrayArray<Any>類型 - 不像data,這是唯一的聲明爲Any。然後dataArray可以傳遞到flatten(input:),並且不會嵌套在另一個Array內。

func flatten(input: [Any]) -> [Any] { 
    var outputArray = [Any]() 

    for i in 0..<input.count { 
     let data = input[i] 
     if let dataArray = data as? Array<Any> { 
      outputArray += flatten(input: dataArray) 
     } else { 
      outputArray.append(data) 
     } 
    } 

    return outputArray 
} 

一對夫婦的其它註釋:

Array<Any>當然相當於[Any],所以if語句可以與(通常優選的)語法來寫,例如:

if let dataArray = data as? [Any] { 
    outputArray += flatten(input: dataArray) 
} 

另外,如果你只是迭代數組,就不需要經歷整個for i in 0..<input.count { let data = input[i] ...的考驗,就像這樣:

func flatten(input: [Any]) -> [Any] { 
    var outputArray = [Any]() 

    for data in input { 
     if let dataArray = data as? [Any] { 
      outputArray += flatten(input: dataArray) 
     } else { 
      outputArray.append(data) 
     } 
    } 

    return outputArray 
}