2015-10-30 10 views
4

我有一個遞歸枚舉,其中大多數情況下具有相同的類型相關聯的值:Swift 2:在關聯值的enum switch語句中是否有任何方法使用'default'?

indirect enum Location { 
    case Title(String?) 
    case Region(Location) 
    case Area(Location, Location) 
    case City(Location, Location) 
    case Settlement(Location, Location) 
    case Street(Location, Location) 
    case House(Location, Location) 
} 

我想要做的是形成一個很好的字符串描述,這將包括所有非空頭銜。

func getStringFromLocation(location: Location) -> String? { 
    var parts: [String?] = [] 

    switch location { 
    case .Title(let title): return title 
    case .House(let title, let parent): 
     parts.append(getStringFromLocation(parent)) 
     parts.append(getStringFromLocation(title)) 
    case .Street(let title, let parent): 
     parts.append(getStringFromLocation(parent)) 
     parts.append(getStringFromLocation(title)) 
    case .Settlement(let title, let parent): 
     parts.append(getStringFromLocation(parent)) 
     parts.append(getStringFromLocation(title)) 
    case .City(let title, let parent): 
     parts.append(getStringFromLocation(parent)) 
     parts.append(getStringFromLocation(title)) 
    case .Area(let title, let parent): 
     parts.append(getStringFromLocation(parent)) 
     parts.append(getStringFromLocation(title)) 
    case .Region(let title): 
     parts.append(getStringFromLocation(title)) 
    } 

    return parts 
     .filter { $0 != nil } 
     .map { $0! } 
     .joinWithSeparator(", ") 
} 

的問題是,五出於七個可能的情況是完全一樣的,我有一堆複製粘貼代碼,因爲我想,是不是好。如果我列舉了100個案例呢?

有什麼辦法可以寫這樣的東西嗎?

switch location { 
case .Title(let title): 
    parts.append(title) 
case .Region(let title): 
    parts.append(getStringFromLocation(title)) 
default (let title, let parent): 
    parts.append(getStringFromLocation(parent)) 
    parts.append(getStringFromLocation(title)) 
} 

...使用一些默認情況下處理所有類似的情況?

回答

1

雖然我同意保羅關心的是,這樣巧妙地嵌套Location,但基本問題是可以解決的。就個人而言,我不會用default來解決它,我只是簡化代碼並使用Swift給我們的工具(如CustomStringConvertible;我也在您的數據上添加了標籤;它僅僅包含兩個Location元素不同的含義):

indirect enum Location: CustomStringConvertible { 
    case Title(String?) 
    case Region(Location) 
    case Area(title: Location, parent: Location) 
    case City(title: Location, parent: Location) 
    case Settlement(title: Location, parent: Location) 
    case Street(title: Location, parent: Location) 
    case House(title: Location, parent: Location) 

    var description: String { 

     func format(locs: (Location, Location)) -> String { 
      return [locs.0, locs.1].map{$0.description}.filter{$0 != ""}.joinWithSeparator(", ") 
     } 

     switch self { 
     case .Title(let title): return title ?? "" 

     case .Region(let title): return "\(title)" 

     case .House(let data):  return format(data) 
     case .Street(let data):  return format(data) 
     case .Settlement(let data): return format(data) 
     case .City(let data):  return format(data) 
     case .Area(let data):  return format(data) 
     } 
    } 
} 

通知我如何卸載整個解析成data。您不必在模式匹配中拆分元組。枚舉從來沒有多個關聯的數據。它們總是隻有一個:一個元組。 (功能也是如此,所有函數都取一個值並返回一個值,這個值可能恰好是一個元組)。

但是如果你真的想擺脫那個重複的return format(data),那麼你可以通過Mirror。 (你可以通過Mirror解決相當多的事情,你應該非常小心,這種情況只是重複輸入,而不是重複的邏輯,有點重複的輸入不是你應該創建很多複雜的刪除。 )

這裏是你會怎麼做:

var description: String { 
    switch self { 
    case .Title(let title): return title ?? "" 

    case .Region(let title): return "\(title)" 

    default: 
     let m = Mirror(reflecting: self) 
     guard let locs = (m.children.first?.value as? (Location, Location)) else { 
      preconditionFailure("Unexpected data in enum. Probably missing a case somewhere.") 
     } 
     return [locs.0, locs.1].map{$0.description}.filter{$0 != ""}.joinWithSeparator(", ") 
    } 
} 

這裏的教訓是,一個枚舉的第一個孩子是它的所有數據的元組。

但使用Mirror更脆弱(注意我打開崩潰的可能性)。雖然枚舉可能是一個很好的工具,但您仍然可能需要重新考慮這個數據結構。

+0

謝謝,羅布!我有你的觀點,看起來很清楚!我想嘗試使用這些遞歸枚舉,但是你和Paul說服我做我想要的並且完全同意 - 最好使用更傳統的數據結構)。非常感謝! –

0

不,沒有辦法讓Swift的模式匹配在不同的枚舉值之間匹配,而這些枚舉值碰巧有相同的關聯值。這是你的直接問題的答案。

正如羅伯特明智地建議的那樣,可能會將重複代碼重構爲病例語句 - 但不可能讓單個病例語句在枚舉值之間匹配以提取關聯的值。


事實上,你發現自己想要這樣做,這表明你可能想重新考慮你的枚舉設計。許多案例之間有共同的行爲和共享結構,但是枚舉案應該是相互排斥和獨立的。

也許區,城市,定居點,街道和房子都是真的一樣的東西?

indirect enum Location { 
    case Title(String?) 
    case Region(Location) 
    case BinaryLocation(BinaryKind, Location, Location) 

    enum BinaryKind { 
     case Area 
     case City 
     case Settlement 
     case Street 
     case House 
    } 
} 

(我不明白這兩個相關聯的位置的含義,但因爲你做的,我建議你想出比BinaryLocationBinaryKind更多解釋的名稱。)

這可能甚至不是適合枚舉的適當情況;例如,像這樣的事情可能會發揮更好:

protocol Location { 
    var description: String { get } 
} 

struct Title: Location { 
    var title: String? 

    var description: String { 
     return title 
    } 
} 

// ... and one for Region, and then ... 

protocol BinaryLocation { 
    var child0: Location { get } 
    var child1: Location { get } 
} 

extention BinaryLocation: Location { 
    var description: String { 
     return "\(child0), \(child1)" 
    } 
} 

// ...and then either individual structs for House, Street, etc., or 
// an enum like BinaryKind above. 

我不能說,因爲我不知道你的全部情況。

什麼我可以說的是,你對重複代碼的關注是有效的,具體的關注是退後一步,看看你的模型的大圖。以挑剔的類型嚴格的問題爲線索,以重新審視自己的造型的選擇是「雨燕道:」

  1. 「爲什麼我得到這個錯誤/打這堵牆?」成爲...
  2. 「爲什麼編譯(a)「我在我的模型中對我的問題域的結構做了什麼斷言?」(b)「這些斷言是否成立?「
+0

其實,我打算建議首先重新考慮一下枚舉的使用。初看起來似乎很酷,但很明顯,這只是阻礙。 – matt

+1

二叉樹中位置的任意嵌套對我來說很有趣。例如,House(地區(標題(「中西部」)),城市(地區(標題(「subshaharan非洲」)),房子(標題(「奶奶的房子」)))''是什麼意思?我會從那裏開始。 –

相關問題